Skip to content

Commit 9ecd4e2

Browse files
Merge pull request #176 from Workiva/add-context-functions
add cross-cutting context api functions
2 parents fb092db + 9328236 commit 9ecd4e2

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+585
-432
lines changed

Diff for: .github/workflows/dart_ci.yaml

+5-18
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ permissions:
1515
contents: read
1616
issues: write
1717
pull-requests: write
18-
18+
1919
jobs:
2020
test-dartv2:
2121
runs-on: ubuntu-latest
@@ -26,14 +26,8 @@ jobs:
2626
- uses: dart-lang/setup-dart@v1
2727
with:
2828
sdk: 2.19.6
29-
- name: Install protobuf-compiler
30-
run: sudo apt install -y protobuf-compiler
31-
- name: Install Dart dependencies
32-
run: dart pub get
33-
- name: Initialize protobuf
34-
run: make init
35-
- name: Format, analyze, and run tests
36-
run: make format analyze test
29+
- run: sudo apt install -y protobuf-compiler
30+
- run: make init format analyze test
3731
- name: Generate Coverage
3832
run: dart test --coverage=./coverage
3933
- name: Activate Coverage Package
@@ -56,12 +50,5 @@ jobs:
5650
- uses: dart-lang/setup-dart@v1
5751
with:
5852
sdk: 3.2.0
59-
- name: Install protobuf-compiler
60-
run: sudo apt install -y protobuf-compiler
61-
- name: Install Dart dependencies
62-
run: dart pub get
63-
- name: Initialize protobuf
64-
run: make init
65-
- name: Format, analyze, and run tests
66-
run: |
67-
make format analyze test
53+
- run: sudo apt install -y protobuf-compiler
54+
- run: make init format analyze test

Diff for: Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ init:
1414
opentelemetry-proto/opentelemetry/proto/collector/trace/v1/trace_service.proto \
1515
opentelemetry-proto/opentelemetry/proto/trace/v1/trace.proto \
1616
opentelemetry-proto/opentelemetry/proto/resource/v1/resource.proto
17+
./scripts/attach_copyright.sh
1718

1819
analyze:
1920
@dart analyze

Diff for: README.md

+63-173
Original file line numberDiff line numberDiff line change
@@ -1,208 +1,98 @@
11
# OpenTelemetry for Dart
22

3-
This repo is intended to be the Dart implementation of the OpenTelemetry project, with a
4-
long-term goal of being open sourced.
3+
This repository is the Dart implementation of the [OpenTelemetry project](https://opentelemetry.io/). All contributions and designs should follow the [OpenTelemetry specification](https://github.com/open-telemetry/opentelemetry-specification).
54

6-
All contributions and designs should follow the
7-
[OpenTelemetry specification](https://github.com/open-telemetry/opentelemetry-specification)
8-
in an effort to be consistent with [all other languages](https://github.com/open-telemetry).
5+
## Project Status
96

10-
## Getting Started
11-
12-
First, you will need to configure at least one exporter. An exporter determines what happens to the spans you collect.
13-
The current options are:
14-
15-
| Exporter | Description |
16-
| -------- | ----------- |
17-
| [CollectorExporter](#collectorexporter) | Sends Spans to a configured opentelemetry-collector. |
18-
| [ConsoleExporter](#consoleexporter) | Prints Spans to the console. |
19-
20-
### Span Exporters
21-
22-
#### CollectorExporter
23-
24-
The CollectorExporter requires a Uri of the opentelemetry-collector instance's trace collector.
25-
26-
```dart
27-
import 'package:opentelemetry/sdk.dart' as otel_sdk;
28-
29-
final exporter = otel_sdk.CollectorExporter(Uri.parse('https://my-collector.com/v1/traces'));
30-
```
31-
32-
#### ConsoleExporter
33-
34-
The ConsoleExporter has no requirements, and has no configuration options.
35-
36-
```dart
37-
import 'package:opentelemetry/sdk.dart' as otel_sdk;
38-
39-
final exporter = otel_sdk.ConsoleExporter();
40-
```
41-
42-
### Span Processors
7+
| Signal | Status |
8+
| - | - |
9+
| Traces | Beta |
10+
| Metrics | Alpha |
11+
| Logs | Unimplemented |
4312

44-
Next, you will need at least one span processor. A span processor is responsible for collecting the spans you create and feeding them to the exporter.
45-
The current options are:
46-
47-
| SpanProcessor | Description |
48-
| -------- | ----------- |
49-
| [BatchSpanProcessor](#batchspanprocessor) | Batches spans to be exported on a configured time interval. |
50-
| [SimpleSpanProcessor](#simplespanprocessor) | Executes the provided exporter immediately upon closing the span. |
13+
## Getting Started
5114

52-
#### BatchSpanProcessor
15+
This section will show you how to initialize the OpenTelemetry SDK, capture a span, and propagate context.
5316

54-
BatchSpanProcessors collect up to 2048 spans per interval, and executes the provided exporter on a timer.
55-
| Option | Description | Default |
56-
| ------ | ----------- | ------- |
57-
| maxExportBatchSize | At most, how many spans are processed per batch. | 512 |
58-
| scheduledDelayMillis | How long to collect spans before processing them. | 5000 ms |
17+
### Initialize the OpenTelemetry SDK
5918

6019
```dart
61-
import 'package:opentelemetry/sdk.dart' as otel_sdk;
62-
63-
final exporter = otel_sdk.ConsoleExporter();
64-
final processor = otel_sdk.BatchSpanProcessor(exporter, scheduledDelayMillis: 10000);
20+
import 'package:opentelemetry/sdk.dart'
21+
show
22+
BatchSpanProcessor,
23+
CollectorExporter,
24+
ConsoleExporter,
25+
SimpleSpanProcessor,
26+
TracerProviderBase;
27+
import 'package:opentelemetry/api.dart'
28+
show registerGlobalTracerProvider, globalTracerProvider;
29+
30+
void main(List<String> args) {
31+
final tracerProvider = TracerProviderBase(processors: [
32+
BatchSpanProcessor(
33+
CollectorExporter(Uri.parse('https://my-collector.com/v1/traces'))),
34+
SimpleSpanProcessor(ConsoleExporter())
35+
]);
36+
37+
registerGlobalTracerProvider(tracerProvider);
38+
final tracer = globalTracerProvider.getTracer('instrumentation-name');
39+
}
6540
```
6641

67-
#### SimpleSpanProcessor
68-
69-
A SimpleSpanProcessor has no configuration options, and executes the exporter when each span is closed.
42+
### Capture a Span
7043

7144
```dart
72-
import 'package:opentelemetry/sdk.dart' as otel_sdk;
73-
74-
final exporter = otel_sdk.ConsoleExporter();
75-
final processor = otel_sdk.SimpleSpanProcessor(exporter);
45+
import 'package:opentelemetry/api.dart' show StatusCode, globalTracerProvider;
46+
47+
void main(List<String> args) {
48+
final tracer = globalTracerProvider.getTracer('instrumentation-name');
49+
50+
final span = tracer.startSpan('main');
51+
try {
52+
// do some work
53+
span.addEvent('some work');
54+
} catch (e, s) {
55+
span
56+
..setStatus(StatusCode.error, e.toString())
57+
..recordException(e, stackTrace: s);
58+
rethrow;
59+
} finally {
60+
span.end();
61+
}
62+
}
7663
```
7764

78-
### Tracer Provider
79-
80-
A trace provider registers your span processors, and is responsible for managing any tracers.
81-
| Option | Description | Default |
82-
| ------ | ----------- | ------- |
83-
| processors | A list of SpanProcessors to register. | A [SimpleSpanProcessor](#simplespanprocessor) configured with a [ConsoleExporter](#consoleexporter). |
65+
### Propagate Context
8466

85-
```dart
86-
import 'package:opentelemetry/sdk.dart' as otel_sdk;
87-
import 'package:opentelemetry/api.dart';
67+
### Intra-process
8868

89-
final exporter = otel_sdk.CollectorExporter(Uri.parse('https://my-collector.com/v1/traces'));
90-
final processor = otel_sdk.BatchSpanProcessor(exporter);
69+
In order to parent spans, context must be propagated. Propagation can be achieved by manually passing an instance of `Context` or by using Dart [`Zones`](https://dart.dev/libraries/async/zones).
9170

92-
// Send spans to a collector every 5 seconds
93-
final provider = otel_sdk.TracerProviderBase(processors: [processor]);
71+
See the [noop context manager example](./example/noop_context_manager.dart) and [zone context manager example](./example/zone_context_manager.dart) for more information.
9472

95-
// Optionally, multiple processors can be registered
96-
final provider = otel_sdk.TracerProviderBase(processors: [
97-
otel_sdk.BatchSpanProcessor(otel_sdk.CollectorExporter(Uri.parse('https://my-collector.com/v1/traces'))),
98-
otel_sdk.SimpleSpanProcessor(otel_sdk.ConsoleExporter())
99-
]);
73+
### Inter-process
10074

101-
registerGlobalTracerProvider(provider);
75+
In order to parent spans between processes, context can be serialized and deserialized using a `TextMapPropagator`, `TextMapSetter`, and `TextMapGetter`.
10276

103-
final tracer = provider.getTracer('instrumentation-name');
104-
// or
105-
final tracer = globalTracerProvider.getTracer('instrumentation-name');
106-
```
77+
See the [W3C context propagation example](./example/w3c_context_propagation.dart) for more information.
10778

108-
#### Tracer Provider with Browser Performance Features
79+
#### High Resolution Timestamps
10980

110-
A web-specific trace provider is also available. This trace provider makes available configurable options using the browser's performance API.
81+
A tracer provider can register a web-specific time provider that uses the browser's [performance API](https://developer.mozilla.org/en-US/docs/Web/API/Performance/now) instead of [DateTime](https://api.dart.dev/stable/dart-core/DateTime-class.html) when recording timestamps for a span's start timestamp, end timestamp, and span events.
11182

11283
```dart
113-
import 'package:opentelemetry/sdk.dart' as otel_sdk;
11484
import 'package:opentelemetry/web_sdk.dart' as web_sdk;
115-
import 'package:opentelemetry/api.dart';
116-
117-
final exporter = otel_sdk.CollectorExporter(Uri.parse('https://my-collector.com/v1/traces'));
118-
final processor = otel_sdk.BatchSpanProcessor(exporter);
119-
120-
// This provider is configured to create tracers which use the browser's
121-
// performance API instead of Dart's DateTime class when determining
122-
// timestamps for any spans they create.
123-
final provider = web_sdk.WebTracerProvider(
124-
processors: [processor],
125-
timeProvider: web_sdk.WebTimeProvider()
126-
);
127-
128-
// This tracer has been configured to use the browser's performance API when
129-
// determining timestamps for any spans it creates.
130-
final tracer = provider.getTracer('instrumentation-name');
131-
132-
// Or, these trace providers can also be registered globally.
133-
registerGlobalTracerProvider(provider);
134-
final tracer = globalTracerProvider.getTracer('instrumentation-name');
135-
```
136-
137-
Important Note: Span timestamps resulting from use of this trace provider may be inaccurate if the executing system is suspended for sleep.
138-
See [https://github.com/open-telemetry/opentelemetry-js/issues/852](https://github.com/open-telemetry/opentelemetry-js/issues/852) for more information.
139-
140-
## Collecting Spans
141-
142-
To start a span, execute `startSpan` on the tracer with the name of what you are tracing. When complete, call `end` on the span.
143-
144-
```dart
145-
final span = tracer.startSpan('doingWork');
146-
...
147-
span.end();
148-
```
149-
150-
To create children spans, use `Context.withSpan` and `Context.execute()` to execute work with a given span.
151-
152-
```dart
153-
final checkoutSpan = tracer.startSpan('checkout');
154-
Context.current.withSpan(checkoutSpan).execute(() {
155-
final ringUpSpan = tracer.startSpan('ringUp');
156-
...
157-
ringUpSpan.end();
158-
final receiveSpan = tracer.startSpan('receiveCash');
159-
...
160-
receiveSpan.end();
161-
final returnSpan = tracer.startSpan('returnChange');
162-
...
163-
returnSpan.end();
164-
});
165-
checkoutSpan.end();
166-
```
16785
168-
To avoid needing to pass spans around as arguments to other functions, you can get the current span with `Context.current.span`.
169-
170-
```dart
171-
doWork() {
172-
Span parentSpan = Context.current.span;
173-
174-
Context.current.withSpan(parentSpan).execute(() {
175-
Span span = tracer.startSpan('doWork');
176-
...
177-
span.end();
178-
});
179-
}
86+
final tracerProvider =
87+
web_sdk.WebTracerProvider(timeProvider: web_sdk.WebTimeProvider());
18088
```
18189

182-
### Span Events
183-
184-
A Span Event is a human-readable message on an Span that represents a discrete event with no duration that can be tracked by a single timestamp. You can think of it like a primitive log.
185-
186-
```dart
187-
span.addEvent('Doing something');
188-
189-
const result = doWork();
190-
```
191-
192-
You can also create Span Events with additional Attributes:
193-
```dart
194-
span.addEvent('some log', attributes: {
195-
'log.severity': 'error',
196-
'log.message': 'Data not found',
197-
'request.id': requestId,
198-
});
199-
```
90+
Important Note: Span timestamps may be inaccurate if the executing system is suspended for sleep. See [https://github.com/open-telemetry/opentelemetry-js/issues/852](https://github.com/open-telemetry/opentelemetry-js/issues/852) for more information.
20091

201-
## Development
92+
## Contributing
20293

20394
In order to generate protobuf definitions, you must have [protoc](https://github.com/protocolbuffers/protobuf/releases) installed and available in your path.
20495

20596
### Publishing New Versions
206-
See https://github.com/Workiva/Observability/blob/master/doc/publishing_opentelemetry_dart.md
20797

208-
Only Workiva maintainers can publish new versions of opentelemetry-dart.
98+
Only Workiva maintainers can publish new versions of opentelemetry-dart. See [Publishing opentelemetry-dart](https://github.com/Workiva/Observability/blob/master/doc/publishing_opentelemetry_dart.md)

Diff for: example/noop_context_manager.dart

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import 'package:opentelemetry/api.dart';
2+
import 'package:opentelemetry/sdk.dart'
3+
show ConsoleExporter, SimpleSpanProcessor, TracerProviderBase;
4+
import 'package:opentelemetry/src/experimental_api.dart'
5+
show NoopContextManager, registerGlobalContextManager;
6+
7+
void main(List<String> args) async {
8+
final tp =
9+
TracerProviderBase(processors: [SimpleSpanProcessor(ConsoleExporter())]);
10+
registerGlobalTracerProvider(tp);
11+
12+
final cm = NoopContextManager();
13+
registerGlobalContextManager(cm);
14+
15+
final span = tp.getTracer('instrumentation-name').startSpan('test-span-0');
16+
await test(contextWithSpan(cm.active, span));
17+
span.end();
18+
}
19+
20+
Future test(Context context) async {
21+
spanFromContext(context).setStatus(StatusCode.error, 'test error');
22+
globalTracerProvider
23+
.getTracer('instrumentation-name')
24+
.startSpan('test-span-1', context: context)
25+
.end();
26+
}

Diff for: example/w3c_context_propagation.dart

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import 'package:opentelemetry/api.dart';
2+
import 'package:opentelemetry/sdk.dart'
3+
show ConsoleExporter, SimpleSpanProcessor, TracerProviderBase;
4+
import 'package:opentelemetry/src/experimental_api.dart'
5+
show NoopContextManager, registerGlobalContextManager;
6+
7+
class MapSetter implements TextMapSetter<Map> {
8+
@override
9+
void set(Map carrier, String key, String value) {
10+
carrier[key] = value;
11+
}
12+
}
13+
14+
class MapGetter implements TextMapGetter<Map> {
15+
@override
16+
String? get(Map? carrier, String key) {
17+
return (carrier == null) ? null : carrier[key];
18+
}
19+
20+
@override
21+
Iterable<String> keys(Map carrier) {
22+
return carrier.keys.map((key) => key.toString());
23+
}
24+
}
25+
26+
void main(List<String> args) async {
27+
final tp =
28+
TracerProviderBase(processors: [SimpleSpanProcessor(ConsoleExporter())]);
29+
registerGlobalTracerProvider(tp);
30+
31+
final cm = NoopContextManager();
32+
registerGlobalContextManager(cm);
33+
34+
final tmp = W3CTraceContextPropagator();
35+
registerGlobalTextMapPropagator(tmp);
36+
37+
final span = tp.getTracer('instrumentation-name').startSpan('test-span-0');
38+
final carrier = <String, String>{};
39+
tmp.inject(contextWithSpan(cm.active, span), carrier, MapSetter());
40+
await test(carrier);
41+
span.end();
42+
}
43+
44+
Future test(Map<String, String> carrier) async {
45+
globalTracerProvider
46+
.getTracer('instrumentation-name')
47+
.startSpan('test-span-1',
48+
context: globalTextMapPropagator.extract(
49+
globalContextManager.active, carrier, MapGetter()))
50+
.end();
51+
}

0 commit comments

Comments
 (0)