diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000..2f60b0e3 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,5 @@ +# These owners will be the default owners for everything in +# the repo. Unless a later match takes precedence, +# @Workiva/product-new-relic will be requested for +# review when someone opens a pull request. +* @Workiva/product-new-relic diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..c35f1b5f --- /dev/null +++ b/Dockerfile @@ -0,0 +1 @@ +FROM scratch diff --git a/README.md b/README.md index 52c1d71c..0d7c6a89 100644 --- a/README.md +++ b/README.md @@ -1 +1,8 @@ -# opentelemetry-dart \ No newline at end of file +# OpenTelemetry for Dart + +This repo is intended to be the Dart implementation of the OpenTelemetry project, with a +long-term goal of being open sourced. + +All contributions and designs should follow the +[OpenTelemetry specification](https://github.com/open-telemetry/opentelemetry-specification) +in an effort to be consistent with [all other languages](https://github.com/open-telemetry). diff --git a/packages/opentelemetry_api/analysis_options.yaml b/packages/opentelemetry_api/analysis_options.yaml new file mode 100644 index 00000000..e69de29b diff --git a/packages/opentelemetry_api/lib/opentelemetry_api.dart b/packages/opentelemetry_api/lib/opentelemetry_api.dart new file mode 100644 index 00000000..be262805 --- /dev/null +++ b/packages/opentelemetry_api/lib/opentelemetry_api.dart @@ -0,0 +1,12 @@ +export 'src/context.dart' + show + isInstrumentationSuppressed, + suppressInstrumentationKey, + unsuppressInstrumentation; +export 'src/trace/span_context.dart'; +export 'src/trace/span_kind.dart'; +export 'src/trace/span.dart'; +export 'src/trace/status.dart'; +export 'src/trace/trace_state.dart'; +export 'src/trace/tracer_provider.dart'; +export 'src/trace/tracer.dart'; diff --git a/packages/opentelemetry_api/lib/src/context.dart b/packages/opentelemetry_api/lib/src/context.dart new file mode 100644 index 00000000..0491b484 --- /dev/null +++ b/packages/opentelemetry_api/lib/src/context.dart @@ -0,0 +1,12 @@ +import 'package:opentelemetry_context/opentelemetry_context.dart'; + +final suppressInstrumentationKey = Context.createKey('OpenTelemetry suppress instrumentation'); + +Context suppressInstrumentation(Context context) => + context.setValue(suppressInstrumentationKey, true); + +Context unsuppressInstrumentation(Context context) => + context.setValue(suppressInstrumentationKey, false); + +bool isInstrumentationSuppressed(Context context) => + context.getValue(suppressInstrumentationKey) ?? false; diff --git a/packages/opentelemetry_api/lib/src/trace/noop_span.dart b/packages/opentelemetry_api/lib/src/trace/noop_span.dart new file mode 100644 index 00000000..b0f08940 --- /dev/null +++ b/packages/opentelemetry_api/lib/src/trace/noop_span.dart @@ -0,0 +1,47 @@ + +import 'package:opentelemetry_api/src/trace/span_context.dart'; + +import 'package:opentelemetry_api/src/trace/status.dart'; + +import 'span.dart'; + +class NoopSpan implements Span { + @override + void addEvent(String name, {Map attributes, DateTime startTime}) { + // TODO: implement addEvent + } + + @override + // TODO: implement context + SpanContext get context => null; + + @override + void end({DateTime endTime}) { + // TODO: implement end + } + + @override + // TODO: implement isRecording + bool get isRecording => null; + + @override + void setAllAttributes(Map attributes) { + // TODO: implement setAllAttributes + } + + @override + void setAttribute(String key, Object value) { + // TODO: implement setAttribute + } + + @override + void setStatus(StatusCode code, {String description}) { + // TODO: implement setStatus + } + + @override + void updateName(String name) { + // TODO: implement updateName + } + +} diff --git a/packages/opentelemetry_api/lib/src/trace/span.dart b/packages/opentelemetry_api/lib/src/trace/span.dart new file mode 100644 index 00000000..1bf96e99 --- /dev/null +++ b/packages/opentelemetry_api/lib/src/trace/span.dart @@ -0,0 +1,69 @@ +import 'span_context.dart'; +import 'status.dart'; + +/// A representation of a single operation within a trace. +/// +/// Examples of a span might include remote procedure calls or in-process +/// function calls to sub-components. A trace has a single, top-level "root" +/// span that in turn may haze zero or more child Spans, which in turn may have +/// children. +/// +/// Spans are created by [Tracer.startSpan]. +abstract class Span { + /// The context associated with this span. + /// + /// This context is an immutable, serializable identifier for this span that + /// can be used to create new child spans and remains usable even after this + /// span ends. + SpanContext get context; + + /// Whether this span is active and recording information like events via + /// [addEvent], attributes via [setAttributes], and so on. + bool get isRecording; + + /// Adds an event to this span. + /// + /// If [attributes] and/or [startTime] are given, they will be set as + /// attributes to this span. + /// + /// Values in [attributes] must be of type [String], [num], [boolean], or a + /// [List] of these types. Null or improperly typed values will result in + /// undefined behavior. + void addEvent(String name, {Map attributes, DateTime startTime}); + + /// Marks the end of this span's execution. + /// + /// [endTime] will be set as this span's end time. If not provided, the + /// current time will be used. + void end({DateTime endTime}); + + // void recordException(Object error, {Map attributes, StackTrace stackTrace, DateTime startTime}); + + /// Sets an attribute to this span. + /// + /// [value] must non-null and of type [String], [num], [boolean], or a [List] + /// of one of these types. Null or improperly typed values will result in + /// undefined behavior. + void setAttribute(String key, Object value); + + /// Sets multiple attributes to this span. + /// + /// Values in [attributes] must be of type [String], [num], [boolean], or a + /// [List] of these types. Null or improperly typed values will result in + /// undefined behavior. + void setAllAttributes(Map attributes); + + /// Sets this span's status. + /// + /// If used, this will override the default status of [StatusCode.unset] and + /// will also override the value of previous calls on this Span. + void setStatus(StatusCode code, {String description}); + + /// Updates this span's name. + /// + /// Upon this update, any sampling behavior based on span name will depend on + /// the implementation. + void updateName(String name) { + + } +} diff --git a/packages/opentelemetry_api/lib/src/trace/span_context.dart b/packages/opentelemetry_api/lib/src/trace/span_context.dart new file mode 100644 index 00000000..0fba510c --- /dev/null +++ b/packages/opentelemetry_api/lib/src/trace/span_context.dart @@ -0,0 +1,23 @@ +import 'package:meta/meta.dart'; + +import 'trace_state.dart'; + +class SpanContext { + static SpanContext create(String traceId, String spanId, num traceFlags, TraceState traceState) + => SpanContext._(traceId, spanId, traceFlags, traceState, isRemote: false); + + static SpanContext createFromRemoteParent(String traceId, String spanId, num traceFlags, TraceState traceState) + => SpanContext._(traceId, spanId, traceFlags, traceState, isRemote: true); + + final bool isRemote; + + final String spanId; + + final num traceFlags; + + final String traceId; + + final TraceState traceState; + + SpanContext._(this.traceId, this.spanId, this.traceFlags, this.traceState, {@required this.isRemote}) +} diff --git a/packages/opentelemetry_api/lib/src/trace/span_kind.dart b/packages/opentelemetry_api/lib/src/trace/span_kind.dart new file mode 100644 index 00000000..48f35272 --- /dev/null +++ b/packages/opentelemetry_api/lib/src/trace/span_kind.dart @@ -0,0 +1,7 @@ +enum SpanKind { + client, + server, + producer, + consumer, + internal, +} diff --git a/packages/opentelemetry_api/lib/src/trace/status.dart b/packages/opentelemetry_api/lib/src/trace/status.dart new file mode 100644 index 00000000..b7bdb9e7 --- /dev/null +++ b/packages/opentelemetry_api/lib/src/trace/status.dart @@ -0,0 +1,11 @@ +abstract class Status { + StatusCode get code; + + String get message; +} + +enum StatusCode { + ok, + unset, + error, +} diff --git a/packages/opentelemetry_api/lib/src/trace/trace_state.dart b/packages/opentelemetry_api/lib/src/trace/trace_state.dart new file mode 100644 index 00000000..cd278983 --- /dev/null +++ b/packages/opentelemetry_api/lib/src/trace/trace_state.dart @@ -0,0 +1,9 @@ +abstract class TraceState { + TraceState get(String key); + + TraceState set(String key, String value); + + TraceState unset(String key); + + String serialize(); +} diff --git a/packages/opentelemetry_api/lib/src/trace/tracer.dart b/packages/opentelemetry_api/lib/src/trace/tracer.dart new file mode 100644 index 00000000..9541e65c --- /dev/null +++ b/packages/opentelemetry_api/lib/src/trace/tracer.dart @@ -0,0 +1,36 @@ +import 'package:opentelemetry_context/opentelemetry_context.dart'; + +import 'span.dart'; +import 'span_kind.dart'; + +/// An interface for creating [Span]s and propagating context in-process. +/// +/// Users may choose to use manual or automatic Context propagation. Because of +/// that, this class offers APIs to facilitate both usages. +abstract class Tracer { + /// Returns the current Span from the current context if available. + /// + /// If there is no Span associated with the current context, this returns + /// `null`. + /// + /// To install a [Span] to the current Context, use [Tracer.withSpan]. + // Span getCurrentSpan(); + + /// Starts a new [Span] without setting it as the current span in this + /// tracer's context. + /// + /// This method does NOT modify the current Context. To install a [Span] to + /// the current Context use [Tracer.withSpan]. + Span startSpan(String name, {Context context, SpanKind kind, DateTime startTime}); + + /// Calls [fn] within the Context provided by [span]. + /// + /// This is a convenience method for creating spans attached to the tracer's + /// context. Applications that need more control over the span lifetime should + /// use [Tracer.startSpan]. + // T withSpan(Span span, T Function() fn); + + /// Binds the given [context] span to [target], or propagates the current + /// context if one is not given. + // T bind(T target, {Span context}); +} diff --git a/packages/opentelemetry_api/lib/src/trace/tracer_provider.dart b/packages/opentelemetry_api/lib/src/trace/tracer_provider.dart new file mode 100644 index 00000000..6ab4304c --- /dev/null +++ b/packages/opentelemetry_api/lib/src/trace/tracer_provider.dart @@ -0,0 +1,11 @@ +import 'tracer.dart'; + +/// A registry for creating named [Tracer]s. +abstract class TracerProvider { + /// Returns a Tracer, creating one if one with the given [name] and [version] + /// is not already created. + /// + /// [name] should be the name of the tracer or instrumentation library. + /// [version] should be the version of the tracer or instrumentation library. + Tracer getTracer(String name, {String version}); +} diff --git a/packages/opentelemetry_api/lib/src/trace/utils.dart b/packages/opentelemetry_api/lib/src/trace/utils.dart new file mode 100644 index 00000000..1c1e0903 --- /dev/null +++ b/packages/opentelemetry_api/lib/src/trace/utils.dart @@ -0,0 +1,12 @@ +import 'span_context.dart'; + +const invalidSpanId = '0000000000000000'; +const invalidTraceId = '00000000000000000000000000000000'; +const invalidSpanContext = SpanContext.create( + invalidTraceId, + invalidSpanId, + traceFlags: TraceFlags.NONE); + +class TraceFlags { + static const num none = 0x0; +} diff --git a/packages/opentelemetry_api/pubspec.yaml b/packages/opentelemetry_api/pubspec.yaml new file mode 100644 index 00000000..aca23b08 --- /dev/null +++ b/packages/opentelemetry_api/pubspec.yaml @@ -0,0 +1,12 @@ +name: opentelemetry_api +version: 0.0.0 +publish_to: none + +dependencies: + logging: ^0.11.4 + meta: ^1.2.4 + opentelemetry_context: ^0.0.0 + +dependency_overrides: + opentelemetry_context: + path: ../opentelemetry_context diff --git a/packages/opentelemetry_context/analysis_options.yaml b/packages/opentelemetry_context/analysis_options.yaml new file mode 100644 index 00000000..e69de29b diff --git a/packages/opentelemetry_context/lib/opentelemetry_context.dart b/packages/opentelemetry_context/lib/opentelemetry_context.dart new file mode 100644 index 00000000..05c4687e --- /dev/null +++ b/packages/opentelemetry_context/lib/opentelemetry_context.dart @@ -0,0 +1,106 @@ +/// This library provides functions for interacting with the active "Context" +/// for the purposes of propagating execution-scoped values across API +/// boundaries and between logically associated execution units. +/// +/// The OpenTelemetry SDKs require a mechanism for propagating context and the +/// OpenTelemetry specification outlines the requirements for this context +/// implementation: https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/context/context.md +/// +/// The spec notes that "languages are expected to use the single, widely used +/// Context implementation if one exists for them." Fortunately, the Dart SDK +/// provides just that with [Zone] - a representation of "an environment that +/// remains stable across asynchronous calls." [Zone] also meets the core +/// requirements of immutability and being able to read and write values: +/// +/// - Immutable: a Zone's values are set when the Zone is created and cannot be +/// changed aftewards. +/// - Reading and writing values: a Zone implements the `[]` operator, allowing +/// values to be read directly from it like a [Map], and writing values is +/// possible only by forking another Zone and providing values to add/override +/// (the rest of the values will be inherited from the forked Zone). +/// +/// This library provides a simple abstraction over [Zone] for the purpose of +/// implementing the rest of the Context specification. OpenTelemetry SDKs and +/// instrumentation libraries should use this [Context] API instead of a [Zone] +/// directly. Other users should usually not interact with Context at all and +/// should instead manipulate it through cross-cutting concerns APIs provided by +/// OpenTelemetry SDKs. +library opentelemetry_context; + +import 'dart:async'; + +class Context { + /// The active context. + static Context get current => _current ?? Context._(Zone.current); + static Context _current; + + /// Returns a key to be used to read and/or write values to a context. + /// + /// [name] is for debug purposes only and does not uniquely identify the key. + /// Multiple calls to this function with the same [name] will not return + /// identical keys. + static ContextKey createKey(String name) => ContextKey(name); + + /// Makes [context] the active context (such that [current] returns this given + /// context) and returns a scope that should closed by passing it to [detach]. + /// + /// Every call to [attach] should result in a corresponding call to [detach]. + static Scope attach(Context context) { + if (context == null) { + // Null context not allowed, so ignore it. + return Scope._noop; + } + + if (context._zone == _current?._zone) { + return Scope._noop; + } + + var prev = _current; + _current = context; + return Scope._(() { + if (_current._zone != context._zone) { + // TODO: log + } + _current = prev; + }); + } + + /// Resets the active context to the value that was active before attaching + /// the context associated with [scope]. + /// + /// If [scope] is associated with a context that is not the active one, an + /// error will be logged. + void detach(Scope scope) { + scope._close(); + } + + /// The implicit "context" that is used to implement the APIs on this class. + final Zone _zone; + + Context._(this._zone); + + /// Returns the value from this context identified by [key], or null if no + /// such value is set. + T getValue(ContextKey key) => _zone[key]; + + /// Returns a new context created from this one with the given key/value pair + /// set. + /// + /// If [key] was already set in this context, it will be overridden. The rest + /// of the context values will be inherited. + Context setValue(ContextKey key, Object value) => Context._(_zone.fork(zoneValues: {key: value})); +} + +class ContextKey { + final String name; + ContextKey(this.name); + + @override + String toString() => 'ContextKey: $name'; +} + +class Scope { + static Scope _noop = Scope._(() {}); + void Function() _close; + Scope._(this._close); +} diff --git a/packages/opentelemetry_context/pubspec.yaml b/packages/opentelemetry_context/pubspec.yaml new file mode 100644 index 00000000..d4ab558b --- /dev/null +++ b/packages/opentelemetry_context/pubspec.yaml @@ -0,0 +1,3 @@ +name: opentelemetry_context +version: 0.0.0 +publish_to: none diff --git a/packages/opentelemetry_sdk/lib/core.dart b/packages/opentelemetry_sdk/lib/core.dart new file mode 100644 index 00000000..79242556 --- /dev/null +++ b/packages/opentelemetry_sdk/lib/core.dart @@ -0,0 +1 @@ +export 'src/core/instrumentation_library.dart'; diff --git a/packages/opentelemetry_sdk/lib/src/core/instrumentation_library.dart b/packages/opentelemetry_sdk/lib/src/core/instrumentation_library.dart new file mode 100644 index 00000000..4a24fdad --- /dev/null +++ b/packages/opentelemetry_sdk/lib/src/core/instrumentation_library.dart @@ -0,0 +1,6 @@ +class InstrumentationLibrary { + final String name; + final String version; + + InstrumentationLibrary(this.name, {String version}) : version = version ?? '*'; +} diff --git a/packages/opentelemetry_sdk/lib/src/trace/tracer.dart b/packages/opentelemetry_sdk/lib/src/trace/tracer.dart new file mode 100644 index 00000000..44308a60 --- /dev/null +++ b/packages/opentelemetry_sdk/lib/src/trace/tracer.dart @@ -0,0 +1,21 @@ +import 'package:logging/logging.dart'; +import 'package:opentelemetry_api/opentelemetry_api.dart' as api; +import 'package:opentelemetry_context/opentelemetry_context.dart'; +import 'package:opentelemetry_sdk/core.dart'; + +class Tracer implements api.Tracer { + final InstrumentationLibrary instrumentationLibrary; + final _log = Logger('opentelemetry_sdk.Tracer'); + + Tracer(this.instrumentationLibrary); + + @override + api.Span startSpan(String name, {Context context, api.SpanKind kind, DateTime startTime}) { + context ??= Context.current; + + if (api.isInstrumentationSuppressed(context)) { + _log.fine('Instrumentation suppressed, returning Noop Span'); + return api.noopSpan; + } + } +} diff --git a/packages/opentelemetry_sdk/lib/src/trace/tracer_provider.dart b/packages/opentelemetry_sdk/lib/src/trace/tracer_provider.dart new file mode 100644 index 00000000..a08dc6e2 --- /dev/null +++ b/packages/opentelemetry_sdk/lib/src/trace/tracer_provider.dart @@ -0,0 +1,9 @@ +import 'package:opentelemetry_api/opentelemetry_api.dart' as api; + +class TracerProvider implements api.TracerProvider { + @override + api.Tracer getTracer(String name, {String version}) { + // TODO: implement getTracer + return null; + } +} diff --git a/packages/opentelemetry_sdk/lib/trace.dart b/packages/opentelemetry_sdk/lib/trace.dart new file mode 100644 index 00000000..11d451f8 --- /dev/null +++ b/packages/opentelemetry_sdk/lib/trace.dart @@ -0,0 +1,2 @@ +export 'src/trace/tracer_provider.dart'; +export 'src/trace/tracer.dart'; diff --git a/packages/opentelemetry_sdk/pubspec.yaml b/packages/opentelemetry_sdk/pubspec.yaml new file mode 100644 index 00000000..ea5ce2c7 --- /dev/null +++ b/packages/opentelemetry_sdk/pubspec.yaml @@ -0,0 +1,14 @@ +name: opentelemetry_sdk +version: 0.0.0 +publish_to: none + +dependencies: + logging: ^0.11.4 + opentelemetry_api: ^0.0.0 + opentelemetry_context: ^0.0.0 + +dependency_overrides: + opentelemetry_api: + path: ../opentelemetry_api + opentelemetry_context: + path: ../opentelemetry_context diff --git a/packages/opentelemetry_tracing/analysis_options.yaml b/packages/opentelemetry_tracing/analysis_options.yaml new file mode 100644 index 00000000..e69de29b diff --git a/packages/opentelemetry_tracing/lib/opentelemetry_tracing.dart b/packages/opentelemetry_tracing/lib/opentelemetry_tracing.dart new file mode 100644 index 00000000..e69de29b diff --git a/packages/opentelemetry_tracing/lib/src/basic_tracer_provider.dart b/packages/opentelemetry_tracing/lib/src/basic_tracer_provider.dart new file mode 100644 index 00000000..40b7b078 --- /dev/null +++ b/packages/opentelemetry_tracing/lib/src/basic_tracer_provider.dart @@ -0,0 +1,5 @@ +import 'package:opentelemetry_api/opentelemetry_api.dart' as api; + +class BasicTracerProvider implements api.TracerProvider { + +} diff --git a/packages/opentelemetry_tracing/pubspec.yaml b/packages/opentelemetry_tracing/pubspec.yaml new file mode 100644 index 00000000..4f28821b --- /dev/null +++ b/packages/opentelemetry_tracing/pubspec.yaml @@ -0,0 +1,3 @@ +name: opentelemetry_tracing +version: 0.0.0 +publish_to: none diff --git a/packages/opentelemetry_vm/analysis_options.yaml b/packages/opentelemetry_vm/analysis_options.yaml new file mode 100644 index 00000000..e69de29b diff --git a/packages/opentelemetry_vm/lib/opentelemetry_vm.dart b/packages/opentelemetry_vm/lib/opentelemetry_vm.dart new file mode 100644 index 00000000..e69de29b diff --git a/packages/opentelemetry_vm/lib/src/vm_tracer_provider.dart b/packages/opentelemetry_vm/lib/src/vm_tracer_provider.dart new file mode 100644 index 00000000..16893d98 --- /dev/null +++ b/packages/opentelemetry_vm/lib/src/vm_tracer_provider.dart @@ -0,0 +1,5 @@ +import 'package:' + +class VmTracerProvider extends BasicTracerProvider { + +} diff --git a/packages/opentelemetry_vm/pubspec.yaml b/packages/opentelemetry_vm/pubspec.yaml new file mode 100644 index 00000000..ac7f4e88 --- /dev/null +++ b/packages/opentelemetry_vm/pubspec.yaml @@ -0,0 +1,3 @@ +name: opentelemetry_vm +version: 0.0.0 +publish_to: none diff --git a/packages/opentelemetry_web/analysis_options.yaml b/packages/opentelemetry_web/analysis_options.yaml new file mode 100644 index 00000000..e69de29b diff --git a/packages/opentelemetry_web/pubspec.yaml b/packages/opentelemetry_web/pubspec.yaml new file mode 100644 index 00000000..29eb5416 --- /dev/null +++ b/packages/opentelemetry_web/pubspec.yaml @@ -0,0 +1,3 @@ +name: opentelemetry_web +version: 0.0.0 +publish_to: none