Skip to content

Commit

Permalink
Merge pull request #39 from michaelyeager-wf/myeager-wf/O11Y-1508
Browse files Browse the repository at this point in the history
O11Y-1508: Change Span to use high resolution time instead of DateTime.now()
  • Loading branch information
rmconsole2-wf authored Apr 27, 2022
2 parents 2eb7a89 + e8468da commit f17329f
Show file tree
Hide file tree
Showing 40 changed files with 491 additions and 117 deletions.
9 changes: 8 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
FROM google/dart:2.13
WORKDIR /build

RUN apt update && apt install -y make protobuf-compiler
RUN apt update && apt install -y make protobuf-compiler wget

COPY pubspec.yaml .
RUN pub get

COPY . .

# Install Chrome for testing browser-specific features.
RUN wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - && \
echo 'deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main' | tee /etc/apt/sources.list.d/google-chrome.list && \
apt-get -qq update && apt-get install -y google-chrome-stable && \
mv /usr/bin/google-chrome-stable /usr/bin/google-chrome && \
sed -i --follow-symlinks -e 's/\"\$HERE\/chrome\"/\"\$HERE\/chrome\" --no-sandbox/g' /usr/bin/google-chrome

RUN make init analyze test

RUN ./package.sh
Expand Down
5 changes: 4 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ format:
@find ./test/ -name '*.dart' | xargs dart format --fix

test: format analyze
@dart test ./test --chain-stack-traces
@dart test ./test \
--chain-stack-traces \
--platform vm \
--platform chrome

.PHONY: init analyze test
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,37 @@ final tracer = provider.getTracer('instrumentation-name');
final tracer = otel_sdk.globalTracerProvider.getTracer('instrumentation-name');
```

#### Tracer Provider with Browser Performance Features

A web-specific trace provider is also available. This trace provider makes available configurable options using the browser's performance API.

```dart
import 'package:opentelemetry/sdk.dart' as otel_sdk;
import 'package:opentelemetry/web_sdk.dart' as web_sdk;
final exporter = otel_sdk.CollectorExporter(Uri.parse('https://my-collector.com/v1/traces'));
final processor = otel_sdk.BatchSpanProcessor(exporter);
// This provider is configured to create tracers which use the browser's
// performance API instead of Dart's DateTime class when determining
// timestamps for any spans they create.
final provider = web_sdk.WebTracerProvider(
processors: [processor],
timeProvider: web_sdk.WebTimeProvider()
);
// This tracer has been configured to use the browser's performance API when
// determining timestamps for any spans it creates.
final tracer = provider.getTracer('instrumentation-name');
// Or, these trace providers can also be registered globally.
otel_sdk.registerGlobalTracerProvider(provider);
final tracer = otel_sdk.globalTracerProvider.getTracer('instrumentation-name');
```

Important Note: Span timestamps resulting from use of this trace provider may be inaccurate if the executing system is suspended for sleep.
See https://github.com/open-telemetry/opentelemetry-js/issues/852 for more information.

## Collecting Spans

To start a span, execute `startSpan` on the tracer with the name of what you are tracing. When complete, call `end` on the span.
Expand Down
8 changes: 5 additions & 3 deletions lib/sdk.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ export 'src/sdk/open_telemetry.dart'
registerGlobalTracerProvider,
trace;
export 'src/sdk/resource/resource.dart' show Resource;
export 'src/sdk/time_providers/datetime_time_provider.dart'
show DateTimeTimeProvider;
export 'src/sdk/time_providers/time_provider.dart' show TimeProvider;
export 'src/sdk/trace/exporters/collector_exporter.dart' show CollectorExporter;
export 'src/sdk/trace/exporters/console_exporter.dart' show ConsoleExporter;
export 'src/sdk/trace/id_generator.dart' show IdGenerator;
Expand All @@ -21,11 +24,10 @@ export 'src/sdk/trace/sampling/sampling_result.dart'
show Decision, SamplingResult;
export 'src/sdk/trace/span.dart' show Span;
export 'src/sdk/trace/span_context.dart' show SpanContext;
export 'src/sdk/trace/span_limits.dart' show SpanLimits;
export 'src/sdk/trace/span_processors/batch_processor.dart'
show BatchSpanProcessor;
export 'src/sdk/trace/span_processors/simple_processor.dart'
show SimpleSpanProcessor;
export 'src/sdk/trace/tracer.dart' show Tracer;
export 'src/sdk/trace/tracer_provider.dart' show TracerProvider;
export 'src/sdk/trace/trace_state.dart' show TraceState;
export 'src/sdk/trace/span_limits.dart' show SpanLimits;
export 'src/sdk/trace/tracer_provider.dart' show TracerProviderBase;
2 changes: 1 addition & 1 deletion lib/src/sdk/open_telemetry.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import 'dart:async';
import '../../api.dart' as api;
import '../../sdk.dart' as sdk;

final api.TracerProvider _noopTracerProvider = sdk.TracerProvider();
final api.TracerProvider _noopTracerProvider = sdk.TracerProviderBase();
api.TracerProvider _tracerProvider = _noopTracerProvider;
api.TextMapPropagator _textMapPropagator;

Expand Down
36 changes: 36 additions & 0 deletions lib/src/sdk/platforms/web/time_providers/web_time_provider.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import 'dart:html';
import 'dart:js';

import 'package:fixnum/fixnum.dart';

import '../../../../../sdk.dart' as sdk;

/// BrowserTimeProvider retrieves high-resolution timestamps utilizing the
/// `window.performance` API.
///
/// See [DOMHighResTimeStamp](https://www.w3.org/TR/hr-time/#sec-DOMHighResTimeStamp)
/// for more information.
///
/// Note that this time may be inaccurate if the executing system is suspended
/// for sleep. See https://github.com/open-telemetry/opentelemetry-js/issues/852
/// for more information.
class WebTimeProvider implements sdk.TimeProvider {
static final Int64 _timeOrigin = _fromDouble(
JsObject.fromBrowserObject(window)['performance']['timeOrigin'] ??
// fallback for browsers that don't support timeOrigin, like Dartium
window.performance.timing.navigationStart.toDouble());

/// Derive a time, in nanoseconds, from a floating-point time, in milliseconds.
static Int64 _fromDouble(double time) =>
Int64((time * sdk.TimeProvider.nanosecondsPerMillisecond).round());

/// The current time, in nanoseconds since Unix Epoch.
///
/// Note that this time may be inaccurate if the executing system is suspended
/// for sleep. See https://github.com/open-telemetry/opentelemetry-js/issues/852
/// for more information.
@override
Int64 get now =>
// .now() returns an int in Dartium, requiring .toDouble()
_timeOrigin + _fromDouble(window.performance.now().toDouble());
}
54 changes: 54 additions & 0 deletions lib/src/sdk/platforms/web/trace/web_tracer_provider.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import '../../../../../api.dart' as api;
import '../../../../../sdk.dart' as sdk;
import '../../../trace/tracer.dart';

/// A [api.TracerProvider] which implements features specific to `dart:html`.
///
/// Use of [WebTracerProvider] with this provider results in a [api.Tracer]
/// which uses the `window.performance` API for high-precision timestamps
/// on the [api.Span]s it creates.
///
/// Note that these timestamps may be inaccurate if the executing system is
/// suspended for sleep.
/// See https://github.com/open-telemetry/opentelemetry-js/issues/852
/// for more information.
class WebTracerProvider extends sdk.TracerProviderBase {
final Map<String, api.Tracer> _tracers = {};
final List<api.SpanProcessor> _processors;
final sdk.Resource _resource;
final sdk.Sampler _sampler;
final sdk.TimeProvider _timeProvider;
final api.IdGenerator _idGenerator;
final sdk.SpanLimits _spanLimits;

WebTracerProvider(
{List<api.SpanProcessor> processors,
sdk.Resource resource,
sdk.Sampler sampler,
sdk.TimeProvider timeProvider,
api.IdGenerator idGenerator,
sdk.SpanLimits spanLimits})
:
// Default to a no-op TracerProvider.
_processors = processors ?? [],
_resource = resource ?? sdk.Resource([]),
_sampler = sampler ?? sdk.ParentBasedSampler(sdk.AlwaysOnSampler()),
_timeProvider = timeProvider ?? sdk.DateTimeTimeProvider(),
_idGenerator = idGenerator ?? sdk.IdGenerator(),
_spanLimits = spanLimits ?? sdk.SpanLimits(),
super(
processors: processors,
resource: resource,
sampler: sampler,
idGenerator: idGenerator,
spanLimits: spanLimits);

@override
api.Tracer getTracer(String name, {String version = ''}) {
return _tracers.putIfAbsent(
'$name@$version',
() => Tracer(_processors, _resource, _sampler, _timeProvider,
_idGenerator, sdk.InstrumentationLibrary(name, version),
spanLimits: _spanLimits));
}
}
10 changes: 10 additions & 0 deletions lib/src/sdk/time_providers/datetime_time_provider.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import 'package:fixnum/fixnum.dart';
import 'time_provider.dart';

/// DateTimeTimeProvider retrieves timestamps using DateTime.
class DateTimeTimeProvider implements TimeProvider {
@override
Int64 get now =>
Int64(DateTime.now().microsecondsSinceEpoch) *
TimeProvider.nanosecondsPerMicrosecond;
}
14 changes: 14 additions & 0 deletions lib/src/sdk/time_providers/time_provider.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import 'package:fixnum/fixnum.dart';

abstract class TimeProvider {
// The smallest increment that DateTime can report is in microseconds, while
// OpenTelemetry expects time in nanoseconds.
static const int nanosecondsPerMicrosecond = 1000;

// window.performance API reports time in fractional milliseconds, while
// OpenTelemetry expects time in nanoseconds.
static const int nanosecondsPerMillisecond = 1000000;

/// The current time, in nanoseconds since Unix Epoch.
Int64 get now;
}
4 changes: 2 additions & 2 deletions lib/src/sdk/trace/exporters/collector_exporter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,8 @@ class CollectorExporter implements api.SpanExporter {
spanId: span.spanContext.spanId.get(),
parentSpanId: span.parentSpanId?.get(),
name: span.name,
startTimeUnixNano: span.startTime * 1000,
endTimeUnixNano: span.endTime * 1000,
startTimeUnixNano: span.startTime,
endTimeUnixNano: span.endTime,
attributes: span.attributes.keys.map((key) => pb_common.KeyValue(
key: key,
value: _attributeValueToProtobuf(span.attributes.get(key)))),
Expand Down
11 changes: 6 additions & 5 deletions lib/src/sdk/trace/span.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import 'package:fixnum/fixnum.dart';
import '../common/attributes.dart';

import '../../../api.dart' as api;
import '../../../sdk.dart' as sdk;
import '../common/attributes.dart';

/// A representation of a single operation within a trace.
class Span implements api.Span {
Expand All @@ -12,6 +12,7 @@ class Span implements api.Span {
final api.SpanStatus _status = api.SpanStatus();
final List<api.SpanProcessor> _processors;
final List<api.SpanLink> _links; // ignore: unused_field
final sdk.TimeProvider _timeProvider;
final sdk.Resource _resource;
final sdk.SpanLimits _spanLimits;
final api.InstrumentationLibrary _instrumentationLibrary;
Expand All @@ -28,16 +29,15 @@ class Span implements api.Span {

/// Construct a [Span].
Span(this.name, this._spanContext, this._parentSpanId, this._processors,
this._resource, this._instrumentationLibrary,
this._timeProvider, this._resource, this._instrumentationLibrary,
{api.SpanKind kind,
List<api.Attribute> attributes,
List<api.SpanLink> links,
sdk.SpanLimits spanlimits,
Int64 startTime})
: _links = links ?? [],
_kind = kind ?? api.SpanKind.internal,
_startTime =
startTime ?? Int64(DateTime.now().toUtc().microsecondsSinceEpoch),
_startTime = startTime ?? _timeProvider.now,
_spanLimits = spanlimits ?? sdk.SpanLimits() {
if (attributes != null) {
setAttributes(attributes);
Expand All @@ -62,7 +62,8 @@ class Span implements api.Span {

@override
void end() {
_endTime ??= Int64(DateTime.now().toUtc().microsecondsSinceEpoch);
_endTime ??= _timeProvider.now;

for (var i = 0; i < _processors.length; i++) {
_processors[i].onEnd(this);
}
Expand Down
10 changes: 6 additions & 4 deletions lib/src/sdk/trace/tracer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,20 @@ import 'package:fixnum/fixnum.dart';

import '../../../api.dart' as api;
import '../../../sdk.dart' as sdk;
import 'span.dart';

/// An interface for creating [api.Span]s and propagating context in-process.
class Tracer implements api.Tracer {
final List<api.SpanProcessor> _processors;
final sdk.Resource _resource;
final sdk.Sampler _sampler;
final sdk.TimeProvider _timeProvider;
final api.IdGenerator _idGenerator;
final api.InstrumentationLibrary _instrumentationLibrary;
final sdk.SpanLimits _spanLimits;

Tracer(this._processors, this._resource, this._sampler, this._idGenerator,
this._instrumentationLibrary,
Tracer(this._processors, this._resource, this._sampler, this._timeProvider,
this._idGenerator, this._instrumentationLibrary,
{sdk.SpanLimits spanLimits})
: _spanLimits = spanLimits ?? sdk.SpanLimits();

Expand Down Expand Up @@ -54,8 +56,8 @@ class Tracer implements api.Tracer {
final spanContext =
sdk.SpanContext(traceId, spanId, traceFlags, traceState);

return sdk.Span(name, spanContext, parentSpanId, _processors, _resource,
_instrumentationLibrary,
return Span(name, spanContext, parentSpanId, _processors, _timeProvider,
_resource, _instrumentationLibrary,
kind: kind,
attributes: attributes,
links: links,
Expand Down
12 changes: 9 additions & 3 deletions lib/src/sdk/trace/tracer_provider.dart
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import './tracer.dart';
import '../../../api.dart' as api;
import '../../../sdk.dart' as sdk;

/// A registry for creating named [api.Tracer]s.
class TracerProvider implements api.TracerProvider {
class TracerProviderBase implements api.TracerProvider {
final Map<String, api.Tracer> _tracers = {};
final List<api.SpanProcessor> _processors;
final sdk.Resource _resource;
final sdk.Sampler _sampler;
final api.IdGenerator _idGenerator;
final sdk.SpanLimits _spanLimits;

TracerProvider(
TracerProviderBase(
{List<api.SpanProcessor> processors,
sdk.Resource resource,
sdk.Sampler sampler,
Expand All @@ -29,7 +30,12 @@ class TracerProvider implements api.TracerProvider {
final key = '$name@$version';
return _tracers.putIfAbsent(
key,
() => sdk.Tracer(_processors, _resource, _sampler, _idGenerator,
() => Tracer(
_processors,
_resource,
_sampler,
sdk.DateTimeTimeProvider(),
_idGenerator,
sdk.InstrumentationLibrary(name, version),
spanLimits: _spanLimits));
}
Expand Down
4 changes: 4 additions & 0 deletions lib/web_sdk.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export 'src/sdk/platforms/web/time_providers/web_time_provider.dart'
show WebTimeProvider;
export 'src/sdk/platforms/web/trace/web_tracer_provider.dart'
show WebTracerProvider;
1 change: 1 addition & 0 deletions test/integration/api/context_test.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
@TestOn('vm')
import 'package:opentelemetry/api.dart';
import 'package:test/test.dart';

Expand Down
Loading

0 comments on commit f17329f

Please sign in to comment.