From aa9ccbba4f432cd44540b33d067a4a470dbad105 Mon Sep 17 00:00:00 2001 From: legendecas Date: Fri, 21 Oct 2022 16:05:17 +0800 Subject: [PATCH] docs: document how to implement a propagator --- doc/propagation.md | 89 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 87 insertions(+), 2 deletions(-) diff --git a/doc/propagation.md b/doc/propagation.md index 1cb3928bde4..fffc3348bf4 100644 --- a/doc/propagation.md +++ b/doc/propagation.md @@ -1,5 +1,90 @@ # Propagation -TODO +Span context fields like trace id, span id, trace flags, and baggages need to be send to the downstream services +in order to properly associate downstream created spans with the current span. -_Propagation API reference: _ +This is commonly achieved with HTTP headers, RPC metadata, with well-known formats like: +- [W3C Trace Context][]: Supported with [W3CTraceContextPropagator][]. +- [B3][]: Supported with [B3Propagator][]. +- Jaeger: Supported with [JaegerPropagator][]. + +If none of above format meet your needs, you can implement your own propagator. + +## Implement your own propagator + +To implement a propagator, you need to define a class implementing the `TextMapPropagator` interface. + +```ts +import { + Context, + isSpanContextValid, + isValidSpanId, + isValidTraceId, + TextMapGetter, + TextMapPropagator, + TextMapSetter, + TraceFlags, + trace, +} from '@opentelemetry/api'; +import { isTracingSuppressed } from '@opentelemetry/core'; + +// Example header, the content format can be `:` +const MyHeader = 'my-header'; + +export class MyPropagator implements TextMapPropagator { + // Inject the header to the outgoing request. + inject(context: Context, carrier: unknown, setter: TextMapSetter): void { + const spanContext = trace.getSpanContext(context); + // Skip if the current span context is not valid or suppressed. + if ( + !spanContext || + !isSpanContextValid(spanContext) || + isTracingSuppressed(context) + ) { + return; + } + + const value = `${spanContext.traceId}-${spanContext.spanId}`; + setter.set(carrier, MyHeader, value); + } + + // Extract the header from the incoming request. + extract(context: Context, carrier: unknown, getter: TextMapGetter): Context { + const headers = getter.get(carrier, MyHeader); + const header = Array.isArray(headers) ? headers[0] : headers; + if (typeof header !== 'string') return context; + + const [traceId, spanId] = header.split(':'); + + // Skip if the traceId or spanId is invalid. + if (!isValidTraceId(traceId) || !isValidSpanId(spanId)) return context; + + return trace.setSpanContext(context, { + traceId, + spanId, + isRemote: true, + traceFlags: TraceFlags.SAMPLED, + }); + } + + fields(): string[] { + return [MyHeader]; + } +} +``` + +With the propagator defined, you can set it as the global propagator, so that all instrumentations +can make use of it. + +```ts +const api = require('@opentelemetry/api'); +const { MyPropagator } = require('./my-propagator'); + +api.propagation.setGlobalPropagator(new MyPropagator()); +``` + +[B3]: https://github.com/openzipkin/b3-propagation +[B3Propagator]: https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-propagator-b3 +[JaegerPropagator]: https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-propagator-jaeger +[W3C Trace Context]: https://www.w3.org/TR/trace-context/ +[W3CTraceContextPropagator]: https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-core#w3ctracecontextpropagator-propagator