diff --git a/packages/opentelemetry-api/src/trace/span_context.ts b/packages/opentelemetry-api/src/trace/span_context.ts index 183fe7cc0df..82eafe43320 100644 --- a/packages/opentelemetry-api/src/trace/span_context.ts +++ b/packages/opentelemetry-api/src/trace/span_context.ts @@ -50,25 +50,6 @@ export interface SpanContext { * SAMPLED = 0x1 and NONE = 0x0; */ traceFlags: TraceFlags; - /** - * Debug flag to propagate. - * - * This flag is relevant to b3. According to the b3 spec, - * Debug is encoded as X-B3-Flags: 1. Absent or any other value can be ignored. - * Debug is a production troubleshooting aid used in tools like curl or chrome debug. - * Debug is an emphasized accept decision that implies accept (or setting traceFlags to accept), - * additionally reporting Span.debug = true for each span in the trace. Adding this - * to the span context because the b3 spec says when this flag is set properly, - * do not send the sampled header as well. - */ - debug?: boolean; - /** - * - * The ID of the parent span propagated by b3. Encoded as a 16 - * lowercase hex characters corresponding to 64 bits. - * May be present on a child span and must be absent on the root span - */ - parentSpanId?: string; /** * Tracing-system-specific info to propagate. * diff --git a/packages/opentelemetry-core/src/context/propagation/B3Propagator.ts b/packages/opentelemetry-core/src/context/propagation/B3Propagator.ts index 8b7cfd208a3..3754617298c 100644 --- a/packages/opentelemetry-core/src/context/propagation/B3Propagator.ts +++ b/packages/opentelemetry-core/src/context/propagation/B3Propagator.ts @@ -28,6 +28,8 @@ export const X_B3_SPAN_ID = 'x-b3-spanid'; export const X_B3_SAMPLED = 'x-b3-sampled'; export const X_B3_PARENT_SPAN_ID = 'x-b3-parentspanid'; export const X_B3_FLAGS = 'x-b3-flags'; +export const PARENT_SPAN_ID_KEY = Context.createKey(X_B3_PARENT_SPAN_ID); +export const DEBUG_FLAG_KEY = Context.createKey(X_B3_FLAGS); const VALID_TRACEID_REGEX = /^([0-9a-f]{16}){1,2}$/i; const VALID_SPANID_REGEX = /^[0-9a-f]{16}$/i; const INVALID_ID_REGEX = /^0+$/i; @@ -48,22 +50,23 @@ export class B3Propagator implements HttpTextPropagator { inject(context: Context, carrier: unknown, setter: SetterFunction) { const spanContext = getParentSpanContext(context); if (!spanContext) return; + const parentSpanId = context.getValue(PARENT_SPAN_ID_KEY); if ( isValidTraceId(spanContext.traceId) && isValidSpanId(spanContext.spanId) ) { - const parentSpanExists = !!spanContext.parentSpanId; - if (parentSpanExists) { - if (isValidTraceId(spanContext.parentSpanId || '')) - setter(carrier, X_B3_PARENT_SPAN_ID, spanContext.parentSpanId); + if (parentSpanId) { + if (isValidTraceId(parentSpanId as string)) + setter(carrier, X_B3_PARENT_SPAN_ID, parentSpanId); else return; } + const debug = context.getValue(DEBUG_FLAG_KEY); setter(carrier, X_B3_TRACE_ID, spanContext.traceId); setter(carrier, X_B3_SPAN_ID, spanContext.spanId); // According to the B3 spec, if the debug flag is set, // the sampled flag shouldn't be propagated as well. - if (spanContext.debug) { - setter(carrier, X_B3_FLAGS, '1'); + if (debug === '1') { + setter(carrier, X_B3_FLAGS, debug); } // We set the header only if there is an existing sampling decision. // Otherwise we will omit it => Absent. @@ -101,9 +104,7 @@ export class B3Propagator implements HttpTextPropagator { const debugHeaderValue = Array.isArray(flagsHeader) ? flagsHeader[0] : flagsHeader; - const debug = isNaN(Number(debugHeaderValue)) - ? false - : debugHeaderValue === '1'; + const debug = debugHeaderValue === '1'; const traceFlagsOrDebug = Number(debug) || Number(options); if ( @@ -114,6 +115,9 @@ export class B3Propagator implements HttpTextPropagator { return context; } + context = context.setValue(PARENT_SPAN_ID_KEY, parentSpanId); + context = context.setValue(DEBUG_FLAG_KEY, debug ? '1' : undefined); + const traceId = traceIdHeaderValue.padStart(32, '0'); if (isValidTraceId(traceId) && isValidSpanId(spanId)) { @@ -123,8 +127,6 @@ export class B3Propagator implements HttpTextPropagator { isRemote: true, // Set traceFlags as 1 if debug is 1 traceFlags: traceFlagsOrDebug || TraceFlags.NONE, - debug, - parentSpanId, }); } return context; diff --git a/packages/opentelemetry-core/test/context/B3Propagator.test.ts b/packages/opentelemetry-core/test/context/B3Propagator.test.ts index 4c72866c51c..7d9c59be66f 100644 --- a/packages/opentelemetry-core/test/context/B3Propagator.test.ts +++ b/packages/opentelemetry-core/test/context/B3Propagator.test.ts @@ -33,6 +33,8 @@ import { X_B3_SAMPLED, X_B3_SPAN_ID, X_B3_TRACE_ID, + DEBUG_FLAG_KEY, + PARENT_SPAN_ID_KEY, } from '../../src/context/propagation/B3Propagator'; import { TraceState } from '../../src/trace/TraceState'; @@ -64,6 +66,7 @@ describe('B3Propagator', () => { assert.deepStrictEqual(carrier[X_B3_SPAN_ID], '6e0c63257de34c92'); assert.deepStrictEqual(carrier[X_B3_SAMPLED], '1'); assert.deepStrictEqual(carrier[X_B3_FLAGS], undefined); + assert.deepStrictEqual(carrier[X_B3_PARENT_SPAN_ID], undefined); }); it('should set b3 traceId and spanId headers - ignore tracestate', () => { @@ -87,6 +90,7 @@ describe('B3Propagator', () => { assert.deepStrictEqual(carrier[X_B3_SPAN_ID], '6e0c63257de34c92'); assert.deepStrictEqual(carrier[X_B3_SAMPLED], '0'); assert.deepStrictEqual(carrier[X_B3_FLAGS], undefined); + assert.deepStrictEqual(carrier[X_B3_PARENT_SPAN_ID], undefined); }); it('should set set b3 traceId, spanId, and flags headers', () => { @@ -94,11 +98,14 @@ describe('B3Propagator', () => { traceId: 'd4cda95b652f4a1592b449d5929fda1b', spanId: '6e0c63257de34c92', traceFlags: TraceFlags.NONE, - debug: true, }; + const contextWithDebug = Context.ROOT_CONTEXT.setValue( + DEBUG_FLAG_KEY, + '1' + ); b3Propagator.inject( - setExtractedSpanContext(Context.ROOT_CONTEXT, spanContext), + setExtractedSpanContext(contextWithDebug, spanContext), carrier, defaultSetter ); @@ -109,19 +116,22 @@ describe('B3Propagator', () => { assert.deepStrictEqual(carrier[X_B3_SPAN_ID], '6e0c63257de34c92'); assert.deepStrictEqual(carrier[X_B3_FLAGS], '1'); assert.deepStrictEqual(carrier[X_B3_SAMPLED], undefined); + assert.deepStrictEqual(carrier[X_B3_PARENT_SPAN_ID], undefined); }); it('should set b3 traceId, spanId, and parentSpanId headers', () => { const spanContext: SpanContext = { traceId: 'd4cda95b652f4a1592b449d5929fda1b', - parentSpanId: 'f4592dc481026a8c', spanId: '6e0c63257de34c92', traceFlags: TraceFlags.NONE, - debug: true, }; + const contextWithParentSpanId = Context.ROOT_CONTEXT.setValue( + PARENT_SPAN_ID_KEY, + 'f4592dc481026a8c' + ); b3Propagator.inject( - setExtractedSpanContext(Context.ROOT_CONTEXT, spanContext), + setExtractedSpanContext(contextWithParentSpanId, spanContext), carrier, defaultSetter ); @@ -131,8 +141,7 @@ describe('B3Propagator', () => { ); assert.deepStrictEqual(carrier[X_B3_PARENT_SPAN_ID], 'f4592dc481026a8c'); assert.deepStrictEqual(carrier[X_B3_SPAN_ID], '6e0c63257de34c92'); - assert.deepStrictEqual(carrier[X_B3_FLAGS], '1'); - assert.deepStrictEqual(carrier[X_B3_SAMPLED], undefined); + assert.deepStrictEqual(carrier[X_B3_FLAGS], undefined); }); it('should not inject empty spancontext', () => { @@ -148,6 +157,8 @@ describe('B3Propagator', () => { ); assert.deepStrictEqual(carrier[X_B3_TRACE_ID], undefined); assert.deepStrictEqual(carrier[X_B3_SPAN_ID], undefined); + assert.deepStrictEqual(carrier[X_B3_FLAGS], undefined); + assert.deepStrictEqual(carrier[X_B3_PARENT_SPAN_ID], undefined); }); }); @@ -155,90 +166,104 @@ describe('B3Propagator', () => { it('should extract context of a unsampled span from carrier', () => { carrier[X_B3_TRACE_ID] = '0af7651916cd43dd8448eb211c80319c'; carrier[X_B3_SPAN_ID] = 'b7ad6b7169203331'; - const extractedSpanContext = getExtractedSpanContext( - b3Propagator.extract(Context.ROOT_CONTEXT, carrier, defaultGetter) + const context = b3Propagator.extract( + Context.ROOT_CONTEXT, + carrier, + defaultGetter ); - + const extractedSpanContext = getExtractedSpanContext(context); assert.deepStrictEqual(extractedSpanContext, { - debug: false, spanId: 'b7ad6b7169203331', - parentSpanId: undefined, traceId: '0af7651916cd43dd8448eb211c80319c', isRemote: true, traceFlags: TraceFlags.NONE, }); + assert.equal(context.getValue(DEBUG_FLAG_KEY), undefined); + assert.equal(context.getValue(PARENT_SPAN_ID_KEY), undefined); }); it('should extract context of a sampled span from carrier', () => { carrier[X_B3_TRACE_ID] = '0af7651916cd43dd8448eb211c80319c'; carrier[X_B3_SPAN_ID] = 'b7ad6b7169203331'; carrier[X_B3_SAMPLED] = '1'; - const extractedSpanContext = getExtractedSpanContext( - b3Propagator.extract(Context.ROOT_CONTEXT, carrier, defaultGetter) + const context = b3Propagator.extract( + Context.ROOT_CONTEXT, + carrier, + defaultGetter ); + const extractedSpanContext = getExtractedSpanContext(context); assert.deepStrictEqual(extractedSpanContext, { - debug: false, spanId: 'b7ad6b7169203331', - parentSpanId: undefined, traceId: '0af7651916cd43dd8448eb211c80319c', isRemote: true, traceFlags: TraceFlags.SAMPLED, }); + assert.equal(context.getValue(DEBUG_FLAG_KEY), undefined); + assert.equal(context.getValue(PARENT_SPAN_ID_KEY), undefined); }); it('should extract context of a sampled span from carrier when sampled is mentioned as boolean true flag', () => { carrier[X_B3_TRACE_ID] = '0af7651916cd43dd8448eb211c80319c'; carrier[X_B3_SPAN_ID] = 'b7ad6b7169203331'; carrier[X_B3_SAMPLED] = true; - const extractedSpanContext = getExtractedSpanContext( - b3Propagator.extract(Context.ROOT_CONTEXT, carrier, defaultGetter) + const context = b3Propagator.extract( + Context.ROOT_CONTEXT, + carrier, + defaultGetter ); + const extractedSpanContext = getExtractedSpanContext(context); assert.deepStrictEqual(extractedSpanContext, { - debug: false, spanId: 'b7ad6b7169203331', - parentSpanId: undefined, traceId: '0af7651916cd43dd8448eb211c80319c', isRemote: true, traceFlags: TraceFlags.SAMPLED, }); + assert.equal(context.getValue(DEBUG_FLAG_KEY), undefined); + assert.equal(context.getValue(PARENT_SPAN_ID_KEY), undefined); }); it('should extract context of a sampled span from carrier when sampled is mentioned as boolean false flag', () => { carrier[X_B3_TRACE_ID] = '0af7651916cd43dd8448eb211c80319c'; carrier[X_B3_SPAN_ID] = 'b7ad6b7169203331'; carrier[X_B3_SAMPLED] = false; - const extractedSpanContext = getExtractedSpanContext( - b3Propagator.extract(Context.ROOT_CONTEXT, carrier, defaultGetter) + const context = b3Propagator.extract( + Context.ROOT_CONTEXT, + carrier, + defaultGetter ); + const extractedSpanContext = getExtractedSpanContext(context); assert.deepStrictEqual(extractedSpanContext, { - debug: false, spanId: 'b7ad6b7169203331', - parentSpanId: undefined, traceId: '0af7651916cd43dd8448eb211c80319c', isRemote: true, traceFlags: TraceFlags.NONE, }); + assert.equal(context.getValue(DEBUG_FLAG_KEY), undefined); + assert.equal(context.getValue(PARENT_SPAN_ID_KEY), undefined); }); it('should extract context of a debug span from carrier when debug flag is valid', () => { carrier[X_B3_TRACE_ID] = '0af7651916cd43dd8448eb211c80319c'; carrier[X_B3_SPAN_ID] = 'b7ad6b7169203331'; carrier[X_B3_FLAGS] = '1'; - const extractedSpanContext = getExtractedSpanContext( - b3Propagator.extract(Context.ROOT_CONTEXT, carrier, defaultGetter) + const context = b3Propagator.extract( + Context.ROOT_CONTEXT, + carrier, + defaultGetter ); + const extractedSpanContext = getExtractedSpanContext(context); assert.deepStrictEqual(extractedSpanContext, { - debug: true, spanId: 'b7ad6b7169203331', - parentSpanId: undefined, traceId: '0af7651916cd43dd8448eb211c80319c', isRemote: true, traceFlags: TraceFlags.SAMPLED, }); + assert.strictEqual(context.getValue(DEBUG_FLAG_KEY), '1'); + assert.equal(context.getValue(PARENT_SPAN_ID_KEY), undefined); }); it('should extract context of a span from carrier when debug flag is invalidly set as 0', () => { @@ -246,18 +271,21 @@ describe('B3Propagator', () => { carrier[X_B3_SPAN_ID] = 'b7ad6b7169203331'; carrier[X_B3_FLAGS] = '0'; carrier[X_B3_SAMPLED] = '1'; - const extractedSpanContext = getExtractedSpanContext( - b3Propagator.extract(Context.ROOT_CONTEXT, carrier, defaultGetter) + const context = b3Propagator.extract( + Context.ROOT_CONTEXT, + carrier, + defaultGetter ); + const extractedSpanContext = getExtractedSpanContext(context); assert.deepStrictEqual(extractedSpanContext, { - debug: false, spanId: 'b7ad6b7169203331', - parentSpanId: undefined, traceId: '0af7651916cd43dd8448eb211c80319c', isRemote: true, traceFlags: TraceFlags.SAMPLED, }); + assert.equal(context.getValue(DEBUG_FLAG_KEY), undefined); + assert.equal(context.getValue(PARENT_SPAN_ID_KEY), undefined); }); it('should extract context of a span from carrier with a parent span id', () => { @@ -266,88 +294,84 @@ describe('B3Propagator', () => { carrier[X_B3_PARENT_SPAN_ID] = 'f4592dc481026a8c'; carrier[X_B3_FLAGS] = '0'; carrier[X_B3_SAMPLED] = '1'; - const extractedSpanContext = getExtractedSpanContext( - b3Propagator.extract(Context.ROOT_CONTEXT, carrier, defaultGetter) + const context = b3Propagator.extract( + Context.ROOT_CONTEXT, + carrier, + defaultGetter ); + const extractedSpanContext = getExtractedSpanContext(context); assert.deepStrictEqual(extractedSpanContext, { - debug: false, spanId: 'b7ad6b7169203331', - parentSpanId: 'f4592dc481026a8c', traceId: '0af7651916cd43dd8448eb211c80319c', isRemote: true, traceFlags: TraceFlags.SAMPLED, }); + assert.equal(context.getValue(DEBUG_FLAG_KEY), undefined); + assert.equal(context.getValue(PARENT_SPAN_ID_KEY), 'f4592dc481026a8c'); }); it('should return undefined when traceId is undefined', () => { carrier[X_B3_TRACE_ID] = undefined; carrier[X_B3_SPAN_ID] = undefined; - assert.deepStrictEqual( - getExtractedSpanContext( - b3Propagator.extract(Context.ROOT_CONTEXT, carrier, defaultGetter) - ), - undefined + const context = getExtractedSpanContext( + b3Propagator.extract(Context.ROOT_CONTEXT, carrier, defaultGetter) ); + assert.deepStrictEqual(context, undefined); }); it('should return undefined when spanId is undefined', () => { carrier[X_B3_TRACE_ID] = '0af7651916cd43dd8448eb211c80319c'; carrier[X_B3_SPAN_ID] = undefined; - assert.deepStrictEqual( - getExtractedSpanContext( - b3Propagator.extract(Context.ROOT_CONTEXT, carrier, defaultGetter) - ), - undefined + const context = getExtractedSpanContext( + b3Propagator.extract(Context.ROOT_CONTEXT, carrier, defaultGetter) ); + assert.deepStrictEqual(context, undefined); }); it('should return undefined when parentSpanId is invalid', () => { carrier[X_B3_TRACE_ID] = '0af7651916cd43dd8448eb211c80319c'; carrier[X_B3_SPAN_ID] = 'b7ad6b7169203331'; carrier[X_B3_PARENT_SPAN_ID] = 'invalid'; - assert.deepStrictEqual( - getExtractedSpanContext( - b3Propagator.extract(Context.ROOT_CONTEXT, carrier, defaultGetter) - ), - undefined + const context = getExtractedSpanContext( + b3Propagator.extract(Context.ROOT_CONTEXT, carrier, defaultGetter) ); + assert.deepStrictEqual(context, undefined); }); it('returns undefined if b3 header is missing', () => { - assert.deepStrictEqual( - getExtractedSpanContext( - b3Propagator.extract(Context.ROOT_CONTEXT, carrier, defaultGetter) - ), - undefined + const context = getExtractedSpanContext( + b3Propagator.extract(Context.ROOT_CONTEXT, carrier, defaultGetter) ); + assert.deepStrictEqual(context, undefined); }); it('returns undefined if b3 header is invalid', () => { carrier[X_B3_TRACE_ID] = 'invalid!'; - assert.deepStrictEqual( - getExtractedSpanContext( - b3Propagator.extract(Context.ROOT_CONTEXT, carrier, defaultGetter) - ), - undefined + const context = getExtractedSpanContext( + b3Propagator.extract(Context.ROOT_CONTEXT, carrier, defaultGetter) ); + assert.deepStrictEqual(context, undefined); }); it('extracts b3 from list of header', () => { carrier[X_B3_TRACE_ID] = ['0af7651916cd43dd8448eb211c80319c']; carrier[X_B3_SPAN_ID] = 'b7ad6b7169203331'; carrier[X_B3_SAMPLED] = '01'; - const extractedSpanContext = getExtractedSpanContext( - b3Propagator.extract(Context.ROOT_CONTEXT, carrier, defaultGetter) + const context = b3Propagator.extract( + Context.ROOT_CONTEXT, + carrier, + defaultGetter ); + const extractedSpanContext = getExtractedSpanContext(context); assert.deepStrictEqual(extractedSpanContext, { - debug: false, spanId: 'b7ad6b7169203331', - parentSpanId: undefined, traceId: '0af7651916cd43dd8448eb211c80319c', isRemote: true, traceFlags: TraceFlags.SAMPLED, }); + assert.equal(context.getValue(DEBUG_FLAG_KEY), undefined); + assert.equal(context.getValue(PARENT_SPAN_ID_KEY), undefined); }); it('should gracefully handle an invalid b3 header', () => { @@ -422,18 +446,21 @@ describe('B3Propagator', () => { carrier[X_B3_TRACE_ID] = '8448eb211c80319c'; carrier[X_B3_SPAN_ID] = 'b7ad6b7169203331'; carrier[X_B3_SAMPLED] = '1'; - const extractedSpanContext = getExtractedSpanContext( - b3Propagator.extract(Context.ROOT_CONTEXT, carrier, defaultGetter) + const context = b3Propagator.extract( + Context.ROOT_CONTEXT, + carrier, + defaultGetter ); + const extractedSpanContext = getExtractedSpanContext(context); assert.deepStrictEqual(extractedSpanContext, { - debug: false, spanId: 'b7ad6b7169203331', - parentSpanId: undefined, traceId: '00000000000000008448eb211c80319c', isRemote: true, traceFlags: TraceFlags.SAMPLED, }); + assert.equal(context.getValue(DEBUG_FLAG_KEY), undefined); + assert.equal(context.getValue(PARENT_SPAN_ID_KEY), undefined); }); }); });