@@ -19,168 +19,54 @@ import {
1919 GetterFunction ,
2020 TextMapPropagator ,
2121 SetterFunction ,
22- TraceFlags ,
23- getParentSpanContext ,
24- setExtractedSpanContext ,
2522} from '@opentelemetry/api' ;
23+ import { B3SinglePropagator , B3_CONTEXT_HEADER } from './B3SinglePropagator' ;
24+ import { B3MultiPropagator } from './B3MultiPropagator' ;
2625
27- import { createContextKey } from '@opentelemetry/context-base' ;
28-
29- export const X_B3_TRACE_ID = 'x-b3-traceid' ;
30- export const X_B3_SPAN_ID = 'x-b3-spanid' ;
31- export const X_B3_SAMPLED = 'x-b3-sampled' ;
32- export const X_B3_PARENT_SPAN_ID = 'x-b3-parentspanid' ;
33- export const X_B3_FLAGS = 'x-b3-flags' ;
34- export const PARENT_SPAN_ID_KEY = createContextKey (
35- 'OpenTelemetry Context Key B3 Parent Span Id'
36- ) ;
37- export const DEBUG_FLAG_KEY = createContextKey (
38- 'OpenTelemetry Context Key B3 Debug Flag'
39- ) ;
40- const VALID_TRACEID_REGEX = / ^ ( [ 0 - 9 a - f ] { 16 } ) { 1 , 2 } $ / i;
41- const VALID_SPANID_REGEX = / ^ [ 0 - 9 a - f ] { 16 } $ / i;
42- const INVALID_ID_REGEX = / ^ 0 + $ / i;
43- const VALID_SAMPLED_VALUES = new Set ( [ true , 'true' , 'True' , '1' , 1 ] ) ;
44- const VALID_UNSAMPLED_VALUES = new Set ( [ false , 'false' , 'False' , '0' , 0 ] ) ;
45-
46- function isValidTraceId ( traceId : string ) : boolean {
47- return VALID_TRACEID_REGEX . test ( traceId ) && ! INVALID_ID_REGEX . test ( traceId ) ;
48- }
49-
50- function isValidSpanId ( spanId : string ) : boolean {
51- return VALID_SPANID_REGEX . test ( spanId ) && ! INVALID_ID_REGEX . test ( spanId ) ;
52- }
53-
54- function isValidParentSpanID ( spanId : string | undefined ) : boolean {
55- return spanId === undefined || isValidSpanId ( spanId ) ;
56- }
57-
58- function isValidSampledValue ( sampled : TraceFlags | undefined ) : boolean {
59- return sampled === TraceFlags . SAMPLED || sampled === TraceFlags . NONE ;
60- }
61-
62- function parseHeader ( header : unknown ) {
63- return Array . isArray ( header ) ? header [ 0 ] : header ;
64- }
65-
66- function getHeaderValue ( carrier : unknown , getter : GetterFunction , key : string ) {
67- const header = getter ( carrier , key ) ;
68- return parseHeader ( header ) ;
69- }
70-
71- function getTraceId ( carrier : unknown , getter : GetterFunction ) : string {
72- const traceId = getHeaderValue ( carrier , getter , X_B3_TRACE_ID ) ;
73- if ( typeof traceId === 'string' ) {
74- return traceId . padStart ( 32 , '0' ) ;
75- }
76- return '' ;
26+ /** Enumeraion of B3 inject encodings */
27+ export enum B3InjectEncoding {
28+ SINGLE_HEADER ,
29+ MULTI_HEADER ,
7730}
7831
79- function getSpanId ( carrier : unknown , getter : GetterFunction ) : string {
80- const spanId = getHeaderValue ( carrier , getter , X_B3_SPAN_ID ) ;
81- if ( typeof spanId === 'string' ) {
82- return spanId ;
83- }
84- return '' ;
85- }
86-
87- function getParentSpanId (
88- carrier : unknown ,
89- getter : GetterFunction
90- ) : string | undefined {
91- const spanId = getHeaderValue ( carrier , getter , X_B3_PARENT_SPAN_ID ) ;
92- if ( typeof spanId === 'string' ) {
93- return spanId ;
94- }
95- return ;
96- }
97-
98- function getDebug (
99- carrier : unknown ,
100- getter : GetterFunction
101- ) : string | undefined {
102- const debug = getHeaderValue ( carrier , getter , X_B3_FLAGS ) ;
103- return debug === '1' ? '1' : undefined ;
104- }
105-
106- function getTraceFlags (
107- carrier : unknown ,
108- getter : GetterFunction
109- ) : TraceFlags | undefined {
110- const traceFlags = getHeaderValue ( carrier , getter , X_B3_SAMPLED ) ;
111- const debug = getDebug ( carrier , getter ) ;
112- if ( debug === '1' || VALID_SAMPLED_VALUES . has ( traceFlags ) ) {
113- return TraceFlags . SAMPLED ;
114- }
115- if ( traceFlags === undefined || VALID_UNSAMPLED_VALUES . has ( traceFlags ) ) {
116- return TraceFlags . NONE ;
117- }
118- // This indicates to isValidSampledValue that this is not valid
119- return ;
32+ /** Configuration for the B3Propagator */
33+ export interface B3PropagatorConfig {
34+ injectEncoding ?: B3InjectEncoding ;
12035}
12136
12237/**
123- * Propagator for the B3 HTTP header format.
38+ * Propagator that extracts B3 context in both single and multi-header variants,
39+ * with configurable injection format defaulting to B3 single-header. Due to
40+ * the asymmetry in injection and extraction formats this is not suitable to
41+ * be implemented as a composite propagator.
12442 * Based on: https://github.com/openzipkin/b3-propagation
12543 */
12644export class B3Propagator implements TextMapPropagator {
127- inject ( context : Context , carrier : unknown , setter : SetterFunction ) {
128- const spanContext = getParentSpanContext ( context ) ;
129- if ( ! spanContext ) return ;
130- const parentSpanId = context . getValue ( PARENT_SPAN_ID_KEY ) as
131- | undefined
132- | string ;
133- if (
134- isValidTraceId ( spanContext . traceId ) &&
135- isValidSpanId ( spanContext . spanId ) &&
136- isValidParentSpanID ( parentSpanId )
137- ) {
138- const debug = context . getValue ( DEBUG_FLAG_KEY ) ;
139- setter ( carrier , X_B3_TRACE_ID , spanContext . traceId ) ;
140- setter ( carrier , X_B3_SPAN_ID , spanContext . spanId ) ;
141- if ( parentSpanId ) {
142- setter ( carrier , X_B3_PARENT_SPAN_ID , parentSpanId ) ;
143- }
144- // According to the B3 spec, if the debug flag is set,
145- // the sampled flag shouldn't be propagated as well.
146- if ( debug === '1' ) {
147- setter ( carrier , X_B3_FLAGS , debug ) ;
148- } else if ( spanContext . traceFlags !== undefined ) {
149- // We set the header only if there is an existing sampling decision.
150- // Otherwise we will omit it => Absent.
151- setter (
152- carrier ,
153- X_B3_SAMPLED ,
154- ( TraceFlags . SAMPLED & spanContext . traceFlags ) === TraceFlags . SAMPLED
155- ? '1'
156- : '0'
157- ) ;
158- }
45+ private readonly _b3MultiPropagator : B3MultiPropagator = new B3MultiPropagator ( ) ;
46+ private readonly _b3SinglePropagator : B3SinglePropagator = new B3SinglePropagator ( ) ;
47+ private readonly _inject : (
48+ context : Context ,
49+ carrier : unknown ,
50+ setter : SetterFunction
51+ ) => void ;
52+
53+ constructor ( config : B3PropagatorConfig = { } ) {
54+ if ( config . injectEncoding === B3InjectEncoding . MULTI_HEADER ) {
55+ this . _inject = this . _b3MultiPropagator . inject ;
56+ } else {
57+ this . _inject = this . _b3SinglePropagator . inject ;
15958 }
16059 }
16160
162- extract ( context : Context , carrier : unknown , getter : GetterFunction ) : Context {
163- const traceId = getTraceId ( carrier , getter ) ;
164- const spanId = getSpanId ( carrier , getter ) ;
165- const parentSpanId = getParentSpanId ( carrier , getter ) ;
166- const traceFlags = getTraceFlags ( carrier , getter ) as TraceFlags ;
167- const debug = getDebug ( carrier , getter ) ;
61+ inject ( context : Context , carrier : unknown , setter : SetterFunction ) {
62+ this . _inject ( context , carrier , setter ) ;
63+ }
16864
169- if (
170- isValidTraceId ( traceId ) &&
171- isValidSpanId ( spanId ) &&
172- isValidParentSpanID ( parentSpanId ) &&
173- isValidSampledValue ( traceFlags )
174- ) {
175- context = context . setValue ( PARENT_SPAN_ID_KEY , parentSpanId ) ;
176- context = context . setValue ( DEBUG_FLAG_KEY , debug ) ;
177- return setExtractedSpanContext ( context , {
178- traceId,
179- spanId,
180- isRemote : true ,
181- traceFlags,
182- } ) ;
65+ extract ( context : Context , carrier : unknown , getter : GetterFunction ) : Context {
66+ if ( getter ( carrier , B3_CONTEXT_HEADER ) ) {
67+ return this . _b3SinglePropagator . extract ( context , carrier , getter ) ;
68+ } else {
69+ return this . _b3MultiPropagator . extract ( context , carrier , getter ) ;
18370 }
184- return context ;
18571 }
18672}
0 commit comments