opentelemetry_propagator_gcp

OpenTelemetry propagator for Google Cloud's X-Cloud-Trace-Context header, for Dart.

Google Cloud services — Cloud Run, App Engine, Cloud Functions, and the HTTP(S) load balancer among them — inject X-Cloud-Trace-Context on inbound requests. Honouring it links your service's spans to the trace the platform already started. Without it, your waterfall begins after the interesting part: you lose the gap that shows queueing and cold start.

Google publishes this propagator for Python, Go and JavaScript. This package is the Dart equivalent.

Community-maintained. This is not an official Google or OpenTelemetry package. It mirrors the official implementations listed below, and we will gladly transfer it to GoogleCloudPlatform or to the OpenTelemetry project if either would like to own it — open an issue and it is yours.

Install

dependencies:
  opentelemetry_propagator_gcp: ^0.0.1

This package depends on the OpenTelemetry API only, never an SDK, so adding it does not force an SDK choice on your consumers.

Usage

Most services want to accept the Google header and emit W3C traceparent. That is CloudTraceOneWayPropagator, composed with a W3C propagator:

import 'package:dartastic_opentelemetry_api/dartastic_opentelemetry_api.dart';
import 'package:opentelemetry_propagator_gcp/opentelemetry_propagator_gcp.dart';

// Reads X-Cloud-Trace-Context, writes nothing. Pair it with a W3C propagator so
// outbound calls carry traceparent rather than a vendor-specific header.
const propagator = CloudTraceOneWayPropagator<Map<String, String>>();

final context = propagator.extract(
  OTelAPI.context(),
  headers,
  MyHeaderGetter(headers),
);

Use CloudTraceFormatPropagator instead when a downstream service reads the Google header and does not understand traceparent:

const propagator = CloudTraceFormatPropagator<Map<String, String>>();
propagator.inject(context, headers, MyHeaderSetter(headers));
// headers['x-cloud-trace-context'] == '<32 hex>/<decimal span id>;o=1'

Which one you want is the same split the official Python package makes with CloudTraceFormatPropagator and CloudTraceOneWayPropagator.

The header format, and the mistake everyone makes

X-Cloud-Trace-Context: TRACE_ID/SPAN_ID;o=OPTIONS
Field Encoding
TRACE_ID 32 hex characters, case-insensitive
SPAN_ID decimal representation of an unsigned 64-bit integer
o= 1 if the parent was sampled, 0 if not; absent means not sampled

SPAN_ID is decimal, not hex. Every other id in tracing is hex, so this is the field that gets misread. 1234567890 is span id 00000000499602d2, not 0000001234567890 — and a wrong parent produces a broken trace that still looks plausible. In Dart the value also does not fit a signed 64-bit int, so parsing goes through BigInt.

Behaviour

  • A malformed header never throws. extract returns the context it was given, because a broken header from an upstream caller must not fail a request.
  • Extracted span contexts are marked remote, so samplers and backends treat the parent as foreign rather than locally created.
  • Trace ids are normalised to lowercase. Google documents the field as case-insensitive; W3C requires lowercase.
  • All-zero trace and span ids are rejected, per the W3C definition of an invalid id.
  • Other o=-adjacent options are tolerated and ignored, so a header that grows a field degrades to "not sampled" rather than to "unparseable".
  • CloudTraceOneWayPropagator.fields() returns an empty list, because the OpenTelemetry contract defines fields as the keys inject writes — and it writes none.

The official implementations this mirrors

License

Apache-2.0, matching OpenTelemetry and the Google Cloud OpenTelemetry packages, so that donating this package to either would need no relicensing.

Libraries

opentelemetry_propagator_gcp
OpenTelemetry propagator for Google Cloud's X-Cloud-Trace-Context header.