middleware_dart_opentelemetry 1.0.5 copy "middleware_dart_opentelemetry: ^1.0.5" to clipboard
middleware_dart_opentelemetry: ^1.0.5 copied to clipboard

Middleware.io's OpenTelemetry SDK for Dart

OpenTelemetry SDK for Dart #

License OpenTelemetry Specification Coverage Report

Middleware is an OpenTelemetry SDK to add standard observability to Dart applications. Middleware can be used with any OTel backend, it's standards-compliant.

Flutter developers should use the Middleware_Flutter OpenTelemetry SDK which builds on top of Middleware Dart OTel.

Middleware.io provides an OpenTelemetry support, training, consulting and an Observability backend customized for Flutter apps, Dart backends, and any other service or process that produces OpenTelemetry data.

Features #

  • ๐Ÿš€ Friendly API: An easy to use, discoverable, immutable, typesafe API that feels familiar to Dart developers.
  • ๐Ÿ“ Standards Compliant: Complies with the OpenTelemetry specification so it's portable and future-proof.
  • ๐ŸŒŽ Ecosystem:
    • Middleware.io is an OTel backend for Dart with a generous free tier, professional support and enterprise features.
    • Middleware_Flutter OTel adds Middleware OTel to Flutter apps with ease. Observe app routes, errors, web vitals and more with as few as two lines of code.
  • ๐Ÿ’ช๐Ÿป Powerful:
    • Propagate OpenTelemetry Context across async gaps and Isolates.
    • Pick from a rich set of Samplers including On/Off, probability and rate-limiting.
    • Automatically capture platform resources on initialization.
    • No skimping - If it's optional.
    • A pluggable and extensible API and SDK enables implementation freedom.
  • ๐Ÿงท Typesafe Semantics: Ensure you're speaking the right language with a massive set of enums matching the OpenTelemetry Semantics Conventions.
  • ๐Ÿ“Š Excellent Performance:
    • Low overhead
    • Batch processing
    • Performance test suite for proven benchmarks
  • ๐Ÿž Well Tested: Good test coverage (>85%).
  • ๐Ÿ“ƒ Quality Documentation: If it's not clearly documented, it's a bug. Extensive examples and best practices are provided. See the examples directory.
  • โœ… Supported Telemetry Signals and Features:
    • Tracing with span processors and samplers
    • Metrics collection and aggregation
    • Context propagation
    • Baggage management
    • Http Instrumentation
    • Logging is not available yet

Middleware_Dart OTel is suitable for Dart backends, CLIs or any Dart application.

Dartastic OTel API is the API for the Dartastic OTel SDK. The dartastic_opentelemetry_api exists as a standalone library to strictly adhere to the OpenTelemetry specification which separates API and the SDK. All OpenTelemetry API classes on in dartastic_opentelemetry_api.

Middleware_Flutter OTel adds Middleware Dart OTel to Flutter apps with ease.

Middleware dart and flutter-sdk OTel are made with ๐Ÿ’™

Getting started #

Include this in your pubspec.yaml:

dependencies:
  middleware_dart_opentelemetry: ^1.0.4

The entrypoint to the SDK is the OTel class. OTel has static "factory" methods for all OTel API and SDK objects. OTel needs to be initialized first to point to an OpenTelemetry backend. Initialization does a lot of work under the hood including gathering a rich set of standard resources for any OS that Dart runs in. It prepares for the creation of the global default TracerProvider with the serviceName and a default Tracer, both created on first use. All configuration, include Trace and Metric exporters, can be made in code via OTel.initialize().
Codeless configuration can be done with standard OpenTelemetry environmental variables either through POSIX variable or -D or --define for Dart or with --dart-define for Flutter apps.

Environment Variables #

Middleware Dart OpenTelemetry supports is working on support for all standard OpenTelemetry environment variables as defined in the OpenTelemetry Specification.

Environment variables provide a convenient way to configure OpenTelemetry without hardcoding values. All environment variable names are available as strongly-typed constants in the SDK for compile-time safety and IDE autocomplete. See lib/src/environment/env_constants.dart for a complete list.

How It Works #

Dart environment variables can be set in two ways:

  1. System Environment Variables (Non-web only): Traditional POSIX environment variables

    export OTEL_SERVICE_NAME=my-service
    dart run
    
  2. Compile-time Constants (All platforms including Flutter web): Passed during compilation/execution

    For Dart commands (dart run, dart compile, dart test):

    # Using --define (or -D shorthand)
    dart run --define=OTEL_SERVICE_NAME=my-service
    dart compile exe -D=OTEL_SERVICE_NAME=my-service -o myapp
    dart test -DOTEL_SERVICE_NAME=my-service
    

    For Flutter commands:

    # Flutter uses --dart-define (note the different flag name)
    flutter run --dart-define=OTEL_SERVICE_NAME=my-service
    flutter build apk --dart-define=OTEL_SERVICE_NAME=my-service
    

Priority: Compile-time constants (--define or --dart-define) take precedence over system environment variables. Explicit parameters to OTel.initialize() override both. Thus, POSIX env vars cannot override --dart-defines and neither POSIX env vars nor --dart-defines can override code. This is sensible and reduces security vectors.

Web Support: Flutter web and Dart web only support compile-time constants (--define or --dart-define), as browser environments don't have access to system environment variables.

Using Environment Variable Constants #

All OpenTelemetry environment variable names are available as typed constants:

import 'package:middleware_dart_opentelemetry/middleware_dart_opentelemetry.dart';

void main() {
  // Reference constants instead of strings
  final serviceName = EnvironmentService.instance.getValue(otelServiceName);
  final endpoint = EnvironmentService.instance.getValue(otelExporterOtlpEndpoint);
  
  print('Service: $serviceName');
  print('Endpoint: $endpoint');
}

Constants are defined for all 74 OpenTelemetry environment variables. See lib/src/environment/env_constants.dart for the complete list.

Supported Environmental Variables #

Service Configuration

Constant Environment Variable Description Example
otelServiceName OTEL_SERVICE_NAME Sets the service name my-dart-app
otelResourceAttributes OTEL_RESOURCE_ATTRIBUTES Additional resource attributes environment=prod,region=us-west
otelLogLevel OTEL_LOG_LEVEL SDK internal log level INFO, DEBUG, WARN, ERROR

OTLP Exporter Configuration

Constant Environment Variable Description Default Example
otelExporterOtlpEndpoint OTEL_EXPORTER_OTLP_ENDPOINT OTLP endpoint URL http://localhost:4318 https://otel-collector:4317
otelExporterOtlpProtocol OTEL_EXPORTER_OTLP_PROTOCOL Transport protocol http/protobuf grpc, http/protobuf, http/json
otelExporterOtlpHeaders OTEL_EXPORTER_OTLP_HEADERS Headers (key=value,...) None api-key=secret,tenant=acme
otelExporterOtlpTimeout OTEL_EXPORTER_OTLP_TIMEOUT Timeout in milliseconds 10000 5000
otelExporterOtlpCompression OTEL_EXPORTER_OTLP_COMPRESSION Compression algorithm None gzip

Signal-Specific Configuration

Traces
Constant Environment Variable Description
otelTracesExporter OTEL_TRACES_EXPORTER Trace exporter type
otelExporterOtlpTracesEndpoint OTEL_EXPORTER_OTLP_TRACES_ENDPOINT Traces-specific endpoint
otelExporterOtlpTracesProtocol OTEL_EXPORTER_OTLP_TRACES_PROTOCOL Traces-specific protocol
otelExporterOtlpTracesHeaders OTEL_EXPORTER_OTLP_TRACES_HEADERS Traces-specific headers
Metrics
Constant Environment Variable Description
otelMetricsExporter OTEL_METRICS_EXPORTER Metrics exporter type
otelExporterOtlpMetricsEndpoint OTEL_EXPORTER_OTLP_METRICS_ENDPOINT Metrics-specific endpoint
otelExporterOtlpMetricsProtocol OTEL_EXPORTER_OTLP_METRICS_PROTOCOL Metrics-specific protocol
otelExporterOtlpMetricsHeaders OTEL_EXPORTER_OTLP_METRICS_HEADERS Metrics-specific headers

For the complete list of all 74 supported environment variables with full documentation, see lib/src/environment/env_constants.dart.

Usage Examples #

Dart Application with Environment Variables

Note the ',' in OTEL_RESOURCE_ATTRIBUTES for POSIX env vars but a ';' for --dart-define. This is due to a Dart quirk.

# Set environment variables
export OTEL_SERVICE_NAME=my-backend-service
export OTEL_RESOURCE_ATTRIBUTES="service.version=1.2.3,deployment.environment=prod"
export OTEL_EXPORTER_OTLP_ENDPOINT=https://otel-collector:4318
export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
export OTEL_EXPORTER_OTLP_HEADERS=api-key=your-key
export OTEL_LOG_LEVEL=DEBUG

# Run your application
dart run bin/my_app.dart

Flutter Application with --dart-define

flutter run \
  --dart-define=OTEL_SERVICE_NAME=my-flutter-app \
  --dart-define=OTEL_RESOURCE_ATTRIBUTES="service.version=1.2.3;deployment.environment=prod"
  --dart-define=OTEL_EXPORTER_OTLP_ENDPOINT=https://otel-collector:4317 \
  --dart-define=OTEL_EXPORTER_OTLP_PROTOCOL=grpc \
  --dart-define=OTEL_EXPORTER_OTLP_HEADERS=api-key=your-key

Flutter Web (requires --dart-define)

# Web MUST use --dart-define (environment variables don't work in browsers)
flutter run -d chrome \
  --dart-define=OTEL_SERVICE_NAME=my-web-app \
  --dart-define=OTEL_EXPORTER_OTLP_ENDPOINT=https://collector:4318

Combining Both (--dart-define wins)

# Environment variable
export OTEL_SERVICE_NAME=from-environment

# --dart-define takes precedence
dart run --dart-define=OTEL_SERVICE_NAME=from-dart-define

# Result: Uses "from-dart-define"

In Code

import 'package:middleware_dart_opentelemetry/middleware_dart_opentelemetry.dart';

void main() async {
  // OTel.initialize() automatically reads environment variables
  // when parameters are not explicitly provided
  await OTel.initialize();
  
  // Environment variables are read automatically:
  // - OTEL_SERVICE_NAME
  // - OTEL_EXPORTER_OTLP_ENDPOINT
  // - OTEL_EXPORTER_OTLP_PROTOCOL
  // - And 90+ others...
  
  // Explicit parameters override environment variables
  await OTel.initialize(
    serviceName: 'explicit-service',  // Overrides OTEL_SERVICE_NAME
    endpoint: 'https://override:4318', // Overrides OTEL_EXPORTER_OTLP_ENDPOINT
  );
  
  // You can also read environment variables directly
  final endpoint = EnvironmentService.instance.getValue(otelExporterOtlpEndpoint);
  print('Using endpoint: $endpoint');
}

Testing with Environment Variables #

Integration tests can use real environment variables:

# Run tests with environment variables
OTEL_SERVICE_NAME=test-service dart test

# Run tests with --dart-define
dart test --dart-define=OTEL_SERVICE_NAME=test-service

# Run the provided integration test script
./tool/test_env_vars.sh

The SDK includes an integration test suite (test/integration/environment_variables_test.dart) and a test script (tool/test_env_vars.sh) that demonstrates proper environment variable usage.

Minimal Code Example #

import 'package:middleware_dart_opentelemetry/middleware_dart_opentelemetry.dart';

void main() async {
  // Initialize - automatically reads environment variables
  await OTel.initialize();

  // Get the default tracer
  final tracer = OTel.tracer();

  // Create a span
  final span = tracer.startSpan('my-operation');
  
  try {
    // Your code here
    await doWork();
  } catch (e, stackTrace) {
    span.recordException(e, stackTrace: stackTrace);
    span.setStatus(SpanStatusCode.error, 'Operation failed');
  } finally {
    span.end();
  }
}

Since middleware_opentelemetry exports all the classes of opentelemetry_api, refer to opentelemetry_api for documentation of API classes.

See the /example folder for more examples.

๐ŸŒ HTTP Client Instrumentation #

Middleware Dart OpenTelemetry includes automatic HTTP client instrumentation for out-of-the-box tracing of outbound HTTP requests. This allows you to:

Create spans for each HTTP request

Propagate W3C Trace Context headers (traceparent, tracestate)

Capture HTTP metadata (method, URL, status, timings, errors)

Automatically connect client spans to downstream services

This works for any Dart backend, CLI, or Flutter-based network implementation using dart:io.

โœจ Features of OTelHttpClient #

  • Wraps any existing HttpClient
  • Automatically injects OTel propagation headers using your TextMapSetter
  • Creates spans around each request
  • Records exceptions, errors, and status codes
  • Provides full W3C spec-compliant context propagation

Usage #

1. Import the package

import 'package:middleware_dart_opentelemetry/middleware_dart_opentelemetry.dart';
import 'dart:io';

2. Initialize OpenTelemetry

 await OTel.initialize(
   serviceName: 'my-dart-service',
   endpoint: '',
 );

3. Wrap the Dart HttpClient with OTelHttpClient

final client = OTelHttpClient(HttpClient());

4. Make instrumented HTTP requests

final request = await client.getUrl(Uri.parse('https://api.example.com/data'));
final response = await request.close();

print('Status: ${response.statusCode}');

That's all you need โ€” spans are now generated automatically and exported via your configured exporter.

๐Ÿš€ Full Example #

  void main() async {
    await OTel.initialize(serviceName: 'http-client-demo');
    
    final tracer = OTel.tracer();
    final client = OTelHttpClient(HttpClient());
    
    final span = tracer.startSpan('demo-operation');
    
    await Context.withSpan(span, () async {
    final request = await client.getUrl(Uri.parse('https://middleware.io'));
    final response = await request.close();
    print('Status: ${response.statusCode}');
    });
    span.end();
  }

This automatically creates:

  • A parent span (demo-operation)
  • A child HTTP client span for the outbound request
  • Correct W3C propagation

OpenTelemetry Metrics API #

The Metrics API in OpenTelemetry provides a way to record measurements about your application. These measurements can be exported later as metrics, allowing you to monitor and analyze the performance and behavior of your application.

Concepts #

  • MeterProvider: Entry point to the metrics API, responsible for creating Meters
  • Meter: Used to create instruments for recording measurements
  • Instrument: Used to record measurements
    • Synchronous instruments: record measurements at the moment of calling their APIs
    • Asynchronous instruments: collect measurements on demand via callbacks

Instrument Types #

  • Counter: Synchronous, monotonic increasing counter (can only go up)
  • UpDownCounter: Synchronous, non-monotonic counter (can go up or down)
  • Histogram: Synchronous, aggregable measurements with statistical distributions
  • Gauge: Synchronous, non-additive value that represents current state
  • ObservableCounter: Asynchronous version of Counter
  • ObservableUpDownCounter: Asynchronous version of UpDownCounter
  • ObservableGauge: Asynchronous version of Gauge

Usage Pattern #

Similar to the Tracing API, the metrics API follows a multi-layered factory pattern:

  1. API Layer: Defines interfaces and provides no-op implementations
  2. SDK Layer: Provides concrete implementations
  3. Flutter Layer: Adds UI-specific functionality

The API follows the pattern of using factory methods for creation rather than constructors:

// Get a meter from the meter provider
final meter = OTel.meterProvider().getMeter('component_name');

// Create a counter instrument
final counter = meter.createCounter('my_counter');

// Record measurements
counter.add(1, {'attribute_key': 'attribute_value'});

For asynchronous instruments:

// Create an observable counter
final observableCounter = meter.createObservableCounter(
  'my_observable_counter',
  () => [Measurement(10, {'attribute_key': 'attribute_value'})],
);

Understanding Metric Types and When to Use Them #

Instrument Type Use Case Example
Counter Count things that only increase Request count, completed tasks
UpDownCounter Count things that can increase or decrease Active requests, queue size
Histogram Measure distributions Request durations, payload sizes
Gauge Record current value CPU usage, memory usage
ObservableCounter Count things that only increase, collected on demand Total CPU time
ObservableUpDownCounter Count things that can increase or decrease, collected on demand Memory usage
ObservableGauge Record current value, collected on demand Current temperature

Integration with Dart/Flutter #

This API implementation follows the same pattern as the tracing API, where the creation of objects is managed through factory methods. This allows for a clear separation between API and SDK, and ensures that the metrics functionality can be used in a no-op mode when the SDK is not initialized.

Commercial Support #

Middleware.io provides an OpenTelemetry Observability backend specifically built for Dart and Flutter applications. Features include:

  • Enhanced tracing with source code integration
  • Session Replay
  • Real-time user monitoring for Flutter apps
  • Advanced dashboard and visualization
  • Integration with native platforms
  • Generous free tier and enterprise support options

License #

Apache 2.0 - See the LICENSE file for details.

Commercial Support #

Middleware.io provides an OpenTelemetry support, training, consulting, enhanced private packages and an Observability backend customized for Flutter apps, Dart backends, and any other service or process that produces OpenTelemetry data. Middleware.io is built on open standards, specifically catering to Flutter and Dart applications with the ability to show Dart source code lines and function calls from production errors and logs.

Middleware.io offers:

  • Free, paid, and enterprise support
  • Packages with advanced features not available in the open source offering
  • Native code integration and Real-Time User Monitoring for Flutter apps
  • Multiple backends (Elastic, Grafana) customized for Flutter apps.

Additional information #