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.
Dartastic.io: AI Code Healing and Real User Monitoring for Flutter
See what your users experience. Find problems with Flutter apps in production. Fix them with AI.
Dartastic.io brings AI code healing, real user monitoring, and OpenTelemetry observability together in one platform built for Dart and Flutter. (Patents pending)

Dartastic AI — From Production Problems to Code Fixes
Dartastic AI analyzes production telemetry alongside your application source code to diagnose problems and propose fixes.
- Investigate production issues using crashes, errors, traces, metrics, and real user telemetry.
- Find the offending code by connecting observed problems with your application's source in Dartastic Pub or your source code repos.
- Generate code patches that address the underlying problems.
- Create actionable issues in your issue tracker, complete with findings and the proposed code patch.
Dartastic RUM — See What Your Users See
Understand how people actually experience your Flutter application.
- Replay user sessions in the Dartastic Observatory session player.
- Choose your replay fidelity, from privacy-preserving widget outlines to pixel-level reproductions of the user experience.
- Investigate real user problems using session replay and production observability data.

Dartastic Observatory — Observability Built for Flutter
A complete observability backend where Flutter and Dart are first-class citizens.
- Flutter-specific dashboards for application performance, health, and reliability.
- Alerts and notifications through email, Slack, and PagerDuty.
- Dartastic Cloud Observatory — Start with a 30-day free trial on isolated, shared infrastructure, as low as $39/mo.
- Dartastic Hosted Observatory — Run the observability stack in a private managed environment, sized from Mini to Enterprise XL.
- Dartastic Self-Hosted Observatory — The whole platform inside your own network. Your keys, your cluster, with nothing leaving your perimeter.
Dartastic Pro OTel Runtime — Native OpenTelemetry Performance
Collect production telemetry without burdening Flutter's main isolate.
- Native OpenTelemetry runtime that moves telemetry processing off the main isolate.
- Native crash reporting for problems that Dart can't catch.
- Widget performance diagnostics to identify janky widgets.
- On-device PII filtering before sensitive data leaves your application.
- Source-level error diagnostics powered by the Dartastic Symbolizer API.
- Platform-native metrics for iOS, Android, and Linux, including iOS MetricKit and Android Vitals.
- OpenTelemetry compatibility — Send telemetry to Dartastic Observatory or any OpenTelemetry platform.
Dartastic Labs — OpenTelemetry Integrations
Instrument more of your application without writing everything yourself.
- 50+ open-source integrations for Dart and Flutter libraries including Dio, Shelf, and Logger. Look for otel_* in pub.flutter-io.cn.
- 600+ Pro integrations for services and libraries including Anthropic, AWS, Azure, and Stripe.
More Dartastic Tools
A private Dart package registry for sharing packages and plugins with your team, partners, and customers.
Turn production errors into lines of source code through a web API, while keeping your source code artifacts private.
Professional Support and Training
Dartastic.io offers commercial support for the open-source dartastic_opentelemetry and dartastic_opentelemetry_api packages and the complete Dartastic product suite.
- Professional support with available around-the-clock coverage and response times under four hours on qualifying plans.
- OpenTelemetry training for teams instrumenting Dart, Flutter, mobile, and web applications.
Open Standards. No Mandatory Backend.
The Dartastic OpenTelemetry API and SDK are open source and standards-based. You can use them independently of Dartastic's commercial services and send telemetry to any compatible OpenTelemetry backend.
Dartastic's commercial products add native performance, advanced diagnostics, session replay, AI-assisted remediation, and managed observability services.
API 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 and Flutter apps, include the latest dartastic_opentelemetry and use its OTel class.
About the API - use the SDK
This dartastic_opentelemetry_api OTel API for Dart and Flutter 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,
instrumenters will include dartastic_opentelemetry in their pubspec.yaml
and this dartastic_opentelemetry_api will be a transitive dependency.
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
- ✅ Typed OTel Semantic Convention enums for the full OTel registry — attribute keys, attribute values, metrics, events, and entities for all 90 registry namespaces.
- ✅ 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
Demos 🎬
- Dart OTel Reference Demo The Dart OpenTelemetry Reference Demo A reference implementation for use of this SDK demonstrating well-instrumented:
- Dart server applications,
- Dart CLIs
- Flutter apps
- Dart CloudRun functions
- Dart Cloud Functions (Firebase Functions in Dart)
- Serverpod backends
Getting Started
Typically, you wouldn't use this library and will use Dartastic OTel dartastic_opentelemetry
instead to get a working OTel implementation in your Dart or Flutter application.
Installation
Add the package to your pubspec.yaml:
dependencies:
dartastic_opentelemetry_api: ^0.12.0
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
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: ^0.12.0
Usage
The entrypoint for almost all object creation is the OTelAPI class. Again this would rarely be used, instead
use the OTel class from dartastic_opentelemetry which has the same methods with additional methods
for SDK objects like Resource and SpanProcessor.
All constructors are package-internal except OTelAPIFactory's — 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.
Prefer typed enum keys over raw strings for attributes. The API ships enums for every
namespace in the OTel semantic conventions
(Http, Url, Server, Db, User, etc.)
For app-specific attributes that aren't in a convention, define your own enum implementing
OTelSemantic.
import 'package:dartastic_opentelemetry_api/dartastic_opentelemetry_api.dart';
/// Example-only attribute keys for things not in the OTel semantic
/// conventions. Rename this in your own code (e.g. `CheckoutAttribute`)
/// so the names reflect your domain.
enum ExampleAttribute implements OTelSemantic {
operationSuccess('operation.success'),
operationValue('operation.value'),
retryCount('retry.count'),
requestDuration('request.duration'),
requestSuccess('request.success'),
tags('tags');
@override
final String key;
@override
String toString() => key;
const ExampleAttribute(this.key);
}
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');
try {
tracer.withSpan(rootSpan, () {
rootSpan.setBoolAttribute(ExampleAttribute.operationSuccess.key, true);
// Child span — parented to rootSpan via the active context. Wrap
// each span in its own try/catch/finally so exceptions are recorded
// on the right span and the span is always ended.
final childSpan = tracer.startSpan('sub-operation');
try {
tracer.withSpan(childSpan, () {
childSpan.setIntAttribute(ExampleAttribute.operationValue.key, 42);
});
// No setStatus(SpanStatusCode.Ok) here: Ok is the default, so
// only Error needs to be set explicitly.
} catch (e, stackTrace) {
// Per the OTel spec: recordException first, then setStatus(Error).
childSpan.recordException(e, stackTrace: stackTrace);
childSpan.setStatus(SpanStatusCode.Error, e.toString());
rethrow;
} finally {
childSpan.end();
}
});
} catch (e, stackTrace) {
rootSpan.recordException(e, stackTrace: stackTrace);
rootSpan.setStatus(SpanStatusCode.Error, e.toString());
rethrow;
} finally {
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;
});
}
Running traced work in another isolate
Isolates share no mutable state, so context and configuration do not
cross the boundary on their own. Context.runIsolate carries them for
you — the OTel configuration, the active context (the propagated
SpanContext arrives isRemote: true, like a context extracted from
W3C headers), and your installed error handler:
final result = await Context.current.runIsolate(() async {
// OTel is configured, Context.current is this context, and the
// error handler behaves as in the parent.
return heavyComputation();
});
Isolates spawned directly (Isolate.spawn, compute) start
unconfigured. See doc/isolates.md for the full
story, including the SendPort pattern for aggregating error reports
across isolates.
Error handling
Per the OpenTelemetry error-handling principles, the API never throws into application code when misused — invalid input degrades safely and is reported. Where those reports go is yours to configure:
// Default: reports are logged via OTelLog and never throw.
// Route them to your own sink instead:
OTelAPI.setErrorHandler((error, stackTrace) {
myTelemetryHealthMonitor.record(error, stackTrace);
});
// Strict mode for development — crash on any OTel misuse:
OTelAPI.setErrorHandler((error, stackTrace) =>
Error.throwWithStackTrace(error, stackTrace ?? StackTrace.current));
// Passing null restores the default handler.
OTelAPI.setErrorHandler(null);
The handler receives the library's internal error reports (invalid
attribute keys, malformed input, dropped data). Exceptions thrown by
your own code inside withSpan blocks are never routed here — they
always rethrow; how they are recorded on the span is controlled by the
SDK's SpanExceptionOptions.
Like every other OpenTelemetry global, the handler is held by the
installed OTelFactory: setErrorHandler stores it on the factory —
buffering it until one is installed, so the call is safe in any order
relative to initialize() — and passing null returns to the
factory's overridable defaultErrorHandler (logging via OTelLog,
unless an SDK factory substitutes its own). Factories are per-isolate;
Context.runIsolate re-installs your handler in child isolates
(doc/isolates.md).
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. Mix API convention enums (e.g.
// ServiceResource) with your own enum (ExampleAttribute) for
// non-convention keys — never use raw strings.
Attributes attributes = OTelAPI.attributes([
OTelAPI.attributeString(Service.serviceName.key, 'payment-processor'),
OTelAPI.attributeInt(ExampleAttribute.retryCount.key, 3),
OTelAPI.attributeDouble(ExampleAttribute.requestDuration.key, 0.125),
OTelAPI.attributeBool(ExampleAttribute.requestSuccess.key, true),
OTelAPI.attributeStringList(
ExampleAttribute.tags.key, ['payment', 'critical']),
]);
// Using attributesFromSemanticMap — same typed-enum principle, no
// `.key` accessor on each entry. Mixes any combination of
// OTel-spec semconv enums with your own ExampleAttribute-style enums.
Attributes fromMap = OTelAPI.attributesFromSemanticMap({
Http.httpRequestMethod: 'GET',
Url.urlFull: 'https://api.example.com/users',
Http.httpResponseStatusCode: 200,
Deployment.deploymentEnvironmentName: 'production',
User.userRoles: ['admin', 'operator'],
});
}
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 = OTelAPI.attributesFromSemanticMap({
Db.dbOperationName: 'update',
Db.dbCollectionName: 'orders',
Db.dbResponseReturnedRows: 3,
});
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
APITracerProvider provider = OTelAPI.tracerProvider();
// Create baggage
Baggage baggage = OTelAPI.baggageForMap({'userId': 'user-123'});
// Create a context carrying the baggage
Context context = OTelAPI.context(baggage: baggage);
// Create attributes
Attribute attr1 = OTelAPI.attributeString('key', 'value');
Attribute attr2 = OTelAPI.attributeInt('count', 42);
Attributes attributes = OTelAPI.attributes([attr1, attr2]);
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 and checked for compliance with the OTel spec.
Additional Resources
- OpenTelemetry Specification
- Dartastic OTel SDK - For Dart backend applications
- Dartastic.io - An OpenTelemetry backend for Dart and Flutter
License
Apache 2.0 - See the LICENSE file for details.
Acknowledgements
This Dart API and the Dartastic SDK are made with 💙 by Michael Bushe at Dartastic.io.
Libraries
- dartastic_opentelemetry_api
- OpenTelemetry API for Dart