Dartastic OpenTelemetry API for Dart
A Dart implementation of the OpenTelemetry API that strictly adheres to the OpenTelemetry (OTel) specification. This package provides a vendor-neutral, implementation-agnostic API for observability instrumentation in Dart and Flutter applications.
Overview
Developers generally do not code with the API, they code with the SDK via the OTel class. This OpenTelemetry API for Dart exists as a standalone library to strictly adhere to the OpenTelemetry specification which separates API and SDK concerns. The specification requires that the API can be dropped into an app without an SDK and it will work in a no-op fashion.
This API is rarely used without an SDK. The SDK for this API is implemented by
dartastic_opentelemetry, the Dartastic OpenTelemetry SDK.
To instrument Dart apps, include the latest dartastic_opentelemetry and use its OTel class.
To instrument Flutter applications use the Flutterrific OpenTelemetry SDK,
flutterrific_opentelemetry to gain almost automatic instrumentation for app routes, error catching
and web vitals metrics and much more.
Commercial Support
Dartastic.io provides:
- Dartastic Cloud - an OpenTelemetry Observability platform integrated with Dart backends and Flutter apps.
- Facilities to get a source stack trace from production errors including Flutter mobile and web.
- Custom dashboards for Flutter OTel
- Dartastic Pub Dev - a private pub dev server
- access Dartastic releases available to subscribers
- access to packages with advanced features not available in the open source offering
- access instrumented versions of Dart and Flutter libraries, such as Dio
- integrated with Dart build systems and Dartastic Cloud to show Dart source code lines and function calls from production error logs.
- allows you to share your packages and plugins privately, within your team or with your partners or customers.
- access Dartastic releases available to subscribers
- Various levels of free, paid, and enterprise support.
- Training on OTel for Dart and Flutter apps.
- Professional consulting in Dart, Flutter and Observability.
About the API - use the SDK
This dartastic_opentelemetry_api OTel API for Dart exists as a standalone library to strictly adhere to the
OpenTelemetry specification which separates API and the SDK. The specification
requires that the API can be dropped into an app without an SDK and it will work in a no-op fashion.
You could include just dartastic_opentelemetry_api in your pubspec.yaml to get a no-op implementation
as required by the OTel specification, though this would be a rare use case. Typically,
backend instrumenters will include dartastic_opentelemetry in their pubspec.yaml
and this dartastic_opentelemetry_api will be a transitive dependency. Flutter instrumentation developers
will include flutterrific_opentelemetry.
Another direct use for this library is for developers who write instrumentation libraries.
This OpenTelemetry API is pluggable. You can create your own OTelFactory to
implement your own SDK implementation. See the Dartastic OTel SDK's OTelSDKFactory
for an example.
Features
- ✅ Complete OpenTelemetry API implementation for Dart
- ✅ Strict adherence to the OpenTelemetry specification
- All MUST and SHOULD requirements are implemented
- Most, if not all, MAY requirements are implemented
- ✅ Supported signal types:
- Traces
- Metrics
- Logs
- ✅ Fully typed API with strong Dart type safety
- ✅ Cross-platform compatibility - works across all Dart environments (Servers, Mobile, Web, Desktop)
- ✅ No-op implementation for safely including in any application
- ✅ Pluggable API design - create your own SDK implementation using
OTelFactory
Getting Started
Typically, you wouldn't use this library and will use Dartastic OTel dartastic_opentelemetry
or flutterrific_opentelemetry instead to get a working OTel implementation in your
Dart or Flutter application, respectively.
Installation
Add the package to your pubspec.yaml:
dependencies:
dartastic_opentelemetry_api: ^1.0.0-beta
Then run:
dart pub get
Using with an SDK
This API is rarely used without an SDK. For a fully functional OpenTelemetry implementation, use one of the following:
-
Dart Backend Applications: Use the Dartastic OTel SDK.
dependencies: dartastic_opentelemetry: ^1.0.0 -
Flutter Applications: Use the Flutterific OTel SDK.
dependencies: flutterrific_opentelemetry: ^1.0.0
Each layer exports all the relevant classes to the next layer so you only have to include one library in your pubspec.yaml.
Direct API Usage (No-op Mode)
If you need a no-op OpenTelemetry implementation (unusual but compliant with the OTel spec):
dependencies:
dartastic_opentelemetry_api: ^1.0.0-beta
Usage
The entrypoint for almost all object creation is the OTelAPI class. Again this would be rarely used, instead
use the OTel class from dartastic_opentelemetry which has the same methods with addition methods
for SDK objects like Resource and SpanProcessor.
All public constructors are private except the OTelAPIFactory. Use OTelAPI to create API objects.
Convenience static factories such as Attributes.of, plus copyWith, copyWithout, and toJson
methods, are provided on objects such as Attributes, Baggage, and Context.
In order to strictly comply with the limited types the OpenTelemetry specification allows
for Attributes there's no generic OTelAPI.attribute<T> creation method and instead, to provide a
typesafe API, there are 8 creation methods for String, bool, int, double and Lists of those types,
i.e. OTelAPI.attributeString('foo', 'bar'), OTelAPI.attributeIntList('baz', [1, 2, 3]).
Usage Examples
Basic Tracing Example
This is a no-op when using OTelAPI. Use OTel from the SDK to record real traces.
import 'package:dartastic_opentelemetry_api/dartastic_opentelemetry_api.dart';
void main() {
// Get a tracer.
final tracer = OTelAPI.tracerProvider().getTracer('example-service');
// tracer.startSpan() does NOT activate the span (per the OpenTelemetry
// specification). Use tracer.withSpan to make a span active for a scope
// so that any spans started inside are parented to it via the active
// context. Use withSpanAsync for asynchronous scopes.
final rootSpan = tracer.startSpan('main-operation');
tracer.withSpan(rootSpan, () {
try {
rootSpan.setBoolAttribute('operation.success', true);
// Child span — parented to rootSpan via the active context.
// Wrap each span in try/finally so it's always ended, even on error.
final childSpan = tracer.startSpan('sub-operation');
try {
tracer.withSpan(childSpan, () {
childSpan.setIntAttribute('operation.value', 42);
});
} finally {
childSpan.end();
}
} catch (e, stackTrace) {
rootSpan
..setStatus(SpanStatusCode.Error, e.toString())
..recordException(e, stackTrace: stackTrace);
rethrow;
} finally {
// end() defaults the status to Ok if it was never set.
rootSpan.end();
}
});
}
Using Context and Baggage
import 'package:dartastic_opentelemetry_api/dartastic_opentelemetry_api.dart';
void main() {
// Create baggage with user info
Baggage baggage = OTelAPI.baggageForMap({
'userId': 'user-123',
'tenant': 'example-tenant'
});
// Create a context with this baggage
Context context = OTelAPI.context(baggage: baggage);
// Activate the context for a scope. runSync uses Zones, so the baggage
// propagates to nested code, including any async callbacks started inside.
context.runSync(() {
// Read baggage off the active context.
final currentBaggage = Context.current.baggage;
final userId = currentBaggage?.getEntry('userId')?.value;
});
}
Working with Attributes
import 'package:dartastic_opentelemetry_api/dartastic_opentelemetry_api.dart';
void main() {
// Using of
Attributes equalToTheAbove = Attributes.of({
'example_string_key': 'foo',
'example_double_key': 42.1,
'example_bool_list_key': [true, false, true],
'example_int_list_key': [42, 43, 44],
});
// Using the typesafe API methods
Attributes attributes = OTelAPI.attributes([
OTelAPI.attributeString('service.name', 'payment-processor'),
OTelAPI.attributeInt('retry.count', 3),
OTelAPI.attributeDouble('request.duration', 0.125),
OTelAPI.attributeBool('request.success', true),
OTelAPI.attributeStringList('tags', ['payment', 'critical']),
]);
// Using Map extension
Attributes fromMap = <String, Object>{
'http.method': 'GET',
'http.url': 'https://api.example.com/users',
'http.status_code': 200,
'environment': 'production',
'user.roles': ['admin', 'operator'],
}.toAttributes();
}
Working with logging
This is a no-op when using OTelAPI. Use the OTel SDK to emit real logs.
import 'package:dartastic_opentelemetry_api/dartastic_opentelemetry_api.dart';
final otelLoggerProvider = OTelAPI.loggerProvider();
final otelLogger = otelLoggerProvider.getLogger('dart-otel-api-faux-db-service');
final attrs = {
'db.operation': 'update',
'db.table': 'orders',
'db.rows_affected': 3,
}.toAttributes();
otelLogger.emit(
eventName: 'order_update',
severityNumber: Severity.INFO,
body: 'Order update completed.',
attributes: attrs,
);
See the /example folder for more complete examples.
API Overview
Main API Components
- OTelAPI - The main entry point for creating API objects
- Tracer - Creates spans for tracing operations
- Span - Represents a unit of work or operation
- Context - Carries execution metadata across API boundaries
- Baggage - Provides a mechanism to propagate key-value pairs alongside a context
- Attributes - Represent key-value pairs with a known set of value types
Important OTelAPI Methods
The entrypoint for almost all object creation is the OTelAPI class.
In real applications, you would typically use the OTel class from an SDK implementation.
// Get the tracer provider
TracerProvider provider = OTelAPI.tracerProvider();
// Create context
Context context = OTelAPI.context(baggage: baggage, spanContext: spanContext);
// Create attributes
Attribute attr1 = OTelAPI.attributeString('key', 'value');
Attribute attr2 = OTelAPI.attributeInt('count', 42);
Attributes attributes = OTelAPI.attributes([attr1, attr2]);
// Create baggage
Baggage baggage = OTelAPI.baggageForMap({'userId': 'user-123'});
CNCF Contribution and Alignment
This project aims to align with Cloud Native Computing Foundation (CNCF) best practices:
- Interoperability - Works with the broader OpenTelemetry ecosystem
- Specification compliance - Strictly follows the OpenTelemetry specification
- Vendor neutrality - Provides a foundation for any OpenTelemetry SDK implementation
For Instrumentation Library Developers
This API can be used directly by those writing instrumentation libraries. By coding against this API rather than a specific SDK implementation, your instrumentation will work with any compliant OpenTelemetry SDK.
To create your own SDK implementation, implement the OTelFactory interface. See the Dartastic OTel SDK's
OTelSDKFactory for an example.
AI Usage
Practically all code in Dartastic was originally generated by Claude.
EVERY character is reviewed by a human for compliance with the OTel spec.
A vast amount of code was edited by hand.
Tests may need improved quality.
Additional Resources
- OpenTelemetry Specification
- Dartastic OTel SDK - For Dart backend applications
- Flutterific OTel SDK - For Flutter applications
- Dartastic.io - An OpenTelemetry backend for Dart and Flutter
License
Apache 2.0 - See the LICENSE file for details.
Acknowledgements
This Dart API, the Dartastic SDK, and Flutterific OTel are made with 💙 by Michael Bushe at Mindful Software.
Libraries
- dartastic_opentelemetry_api
- OpenTelemetry API for Dart