diff --git a/packages/opentelemetry-api/src/index.ts b/packages/opentelemetry-api/src/index.ts index e1af8b5199..02a257f0b5 100644 --- a/packages/opentelemetry-api/src/index.ts +++ b/packages/opentelemetry-api/src/index.ts @@ -61,6 +61,9 @@ export { INVALID_SPANID, INVALID_TRACEID, INVALID_SPAN_CONTEXT, + isSpanContextValid, + isValidTraceId, + isValidSpanId, } from './trace/spancontext-utils'; export { diff --git a/packages/opentelemetry-core/src/context/propagation/B3MultiPropagator.ts b/packages/opentelemetry-core/src/context/propagation/B3MultiPropagator.ts new file mode 100644 index 0000000000..159fae4c46 --- /dev/null +++ b/packages/opentelemetry-core/src/context/propagation/B3MultiPropagator.ts @@ -0,0 +1,144 @@ +/* + * Copyright The OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + Context, + GetterFunction, + TextMapPropagator, + SetterFunction, + TraceFlags, + isValidSpanId, + isValidTraceId, + isSpanContextValid, + getParentSpanContext, + setExtractedSpanContext, +} from '@opentelemetry/api'; +import { B3_DEBUG_FLAG_KEY } from './b3-common'; + +/* b3 multi-header keys */ +export const X_B3_TRACE_ID = 'x-b3-traceid'; +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'; + +const VALID_SAMPLED_VALUES = new Set([true, 'true', 'True', '1', 1]); +const VALID_UNSAMPLED_VALUES = new Set([false, 'false', 'False', '0', 0]); + +function isValidSampledValue(sampled: TraceFlags | undefined): boolean { + return sampled === TraceFlags.SAMPLED || sampled === TraceFlags.NONE; +} + +export function parseHeader(header: unknown) { + return Array.isArray(header) ? header[0] : header; +} + +function getHeaderValue(carrier: unknown, getter: GetterFunction, key: string) { + const header = getter(carrier, key); + return parseHeader(header); +} + +function getTraceId(carrier: unknown, getter: GetterFunction): string { + const traceId = getHeaderValue(carrier, getter, X_B3_TRACE_ID); + if (typeof traceId === 'string') { + return traceId.padStart(32, '0'); + } + return ''; +} + +function getSpanId(carrier: unknown, getter: GetterFunction): string { + const spanId = getHeaderValue(carrier, getter, X_B3_SPAN_ID); + if (typeof spanId === 'string') { + return spanId; + } + return ''; +} + +function getDebug( + carrier: unknown, + getter: GetterFunction +): string | undefined { + const debug = getHeaderValue(carrier, getter, X_B3_FLAGS); + return debug === '1' ? '1' : undefined; +} + +function getTraceFlags( + carrier: unknown, + getter: GetterFunction +): TraceFlags | undefined { + const traceFlags = getHeaderValue(carrier, getter, X_B3_SAMPLED); + const debug = getDebug(carrier, getter); + if (debug === '1' || VALID_SAMPLED_VALUES.has(traceFlags)) { + return TraceFlags.SAMPLED; + } + if (traceFlags === undefined || VALID_UNSAMPLED_VALUES.has(traceFlags)) { + return TraceFlags.NONE; + } + // This indicates to isValidSampledValue that this is not valid + return; +} + +/** + * Propagator for the B3 multiple-header HTTP format. + * Based on: https://github.com/openzipkin/b3-propagation + */ +export class B3MultiPropagator implements TextMapPropagator { + inject(context: Context, carrier: unknown, setter: SetterFunction) { + const spanContext = getParentSpanContext(context); + if (!spanContext || !isSpanContextValid(spanContext)) return; + + const debug = context.getValue(B3_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 (debug === '1') { + setter(carrier, X_B3_FLAGS, debug); + } else if (spanContext.traceFlags !== undefined) { + // We set the header only if there is an existing sampling decision. + // Otherwise we will omit it => Absent. + setter( + carrier, + X_B3_SAMPLED, + (TraceFlags.SAMPLED & spanContext.traceFlags) === TraceFlags.SAMPLED + ? '1' + : '0' + ); + } + } + + extract(context: Context, carrier: unknown, getter: GetterFunction): Context { + const traceId = getTraceId(carrier, getter); + const spanId = getSpanId(carrier, getter); + const traceFlags = getTraceFlags(carrier, getter) as TraceFlags; + const debug = getDebug(carrier, getter); + + if ( + isValidTraceId(traceId) && + isValidSpanId(spanId) && + isValidSampledValue(traceFlags) + ) { + context = context.setValue(B3_DEBUG_FLAG_KEY, debug); + return setExtractedSpanContext(context, { + traceId, + spanId, + isRemote: true, + traceFlags, + }); + } + return context; + } +} diff --git a/packages/opentelemetry-core/src/context/propagation/B3Propagator.ts b/packages/opentelemetry-core/src/context/propagation/B3Propagator.ts index af51087ca7..94974061ef 100644 --- a/packages/opentelemetry-core/src/context/propagation/B3Propagator.ts +++ b/packages/opentelemetry-core/src/context/propagation/B3Propagator.ts @@ -19,168 +19,54 @@ import { GetterFunction, TextMapPropagator, SetterFunction, - TraceFlags, - getParentSpanContext, - setExtractedSpanContext, } from '@opentelemetry/api'; +import { B3SinglePropagator, B3_CONTEXT_HEADER } from './B3SinglePropagator'; +import { B3MultiPropagator } from './B3MultiPropagator'; -import { createContextKey } from '@opentelemetry/context-base'; - -export const X_B3_TRACE_ID = 'x-b3-traceid'; -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 = createContextKey( - 'OpenTelemetry Context Key B3 Parent Span Id' -); -export const DEBUG_FLAG_KEY = createContextKey( - 'OpenTelemetry Context Key B3 Debug Flag' -); -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; -const VALID_SAMPLED_VALUES = new Set([true, 'true', 'True', '1', 1]); -const VALID_UNSAMPLED_VALUES = new Set([false, 'false', 'False', '0', 0]); - -function isValidTraceId(traceId: string): boolean { - return VALID_TRACEID_REGEX.test(traceId) && !INVALID_ID_REGEX.test(traceId); -} - -function isValidSpanId(spanId: string): boolean { - return VALID_SPANID_REGEX.test(spanId) && !INVALID_ID_REGEX.test(spanId); -} - -function isValidParentSpanID(spanId: string | undefined): boolean { - return spanId === undefined || isValidSpanId(spanId); -} - -function isValidSampledValue(sampled: TraceFlags | undefined): boolean { - return sampled === TraceFlags.SAMPLED || sampled === TraceFlags.NONE; -} - -function parseHeader(header: unknown) { - return Array.isArray(header) ? header[0] : header; -} - -function getHeaderValue(carrier: unknown, getter: GetterFunction, key: string) { - const header = getter(carrier, key); - return parseHeader(header); -} - -function getTraceId(carrier: unknown, getter: GetterFunction): string { - const traceId = getHeaderValue(carrier, getter, X_B3_TRACE_ID); - if (typeof traceId === 'string') { - return traceId.padStart(32, '0'); - } - return ''; +/** Enumeraion of B3 inject encodings */ +export enum B3InjectEncoding { + SINGLE_HEADER, + MULTI_HEADER, } -function getSpanId(carrier: unknown, getter: GetterFunction): string { - const spanId = getHeaderValue(carrier, getter, X_B3_SPAN_ID); - if (typeof spanId === 'string') { - return spanId; - } - return ''; -} - -function getParentSpanId( - carrier: unknown, - getter: GetterFunction -): string | undefined { - const spanId = getHeaderValue(carrier, getter, X_B3_PARENT_SPAN_ID); - if (typeof spanId === 'string') { - return spanId; - } - return; -} - -function getDebug( - carrier: unknown, - getter: GetterFunction -): string | undefined { - const debug = getHeaderValue(carrier, getter, X_B3_FLAGS); - return debug === '1' ? '1' : undefined; -} - -function getTraceFlags( - carrier: unknown, - getter: GetterFunction -): TraceFlags | undefined { - const traceFlags = getHeaderValue(carrier, getter, X_B3_SAMPLED); - const debug = getDebug(carrier, getter); - if (debug === '1' || VALID_SAMPLED_VALUES.has(traceFlags)) { - return TraceFlags.SAMPLED; - } - if (traceFlags === undefined || VALID_UNSAMPLED_VALUES.has(traceFlags)) { - return TraceFlags.NONE; - } - // This indicates to isValidSampledValue that this is not valid - return; +/** Configuration for the B3Propagator */ +export interface B3PropagatorConfig { + injectEncoding?: B3InjectEncoding; } /** - * Propagator for the B3 HTTP header format. + * Propagator that extracts B3 context in both single and multi-header variants, + * with configurable injection format defaulting to B3 single-header. Due to + * the asymmetry in injection and extraction formats this is not suitable to + * be implemented as a composite propagator. * Based on: https://github.com/openzipkin/b3-propagation */ export class B3Propagator implements TextMapPropagator { - inject(context: Context, carrier: unknown, setter: SetterFunction) { - const spanContext = getParentSpanContext(context); - if (!spanContext) return; - const parentSpanId = context.getValue(PARENT_SPAN_ID_KEY) as - | undefined - | string; - if ( - isValidTraceId(spanContext.traceId) && - isValidSpanId(spanContext.spanId) && - isValidParentSpanID(parentSpanId) - ) { - const debug = context.getValue(DEBUG_FLAG_KEY); - setter(carrier, X_B3_TRACE_ID, spanContext.traceId); - setter(carrier, X_B3_SPAN_ID, spanContext.spanId); - if (parentSpanId) { - setter(carrier, X_B3_PARENT_SPAN_ID, parentSpanId); - } - // According to the B3 spec, if the debug flag is set, - // the sampled flag shouldn't be propagated as well. - if (debug === '1') { - setter(carrier, X_B3_FLAGS, debug); - } else if (spanContext.traceFlags !== undefined) { - // We set the header only if there is an existing sampling decision. - // Otherwise we will omit it => Absent. - setter( - carrier, - X_B3_SAMPLED, - (TraceFlags.SAMPLED & spanContext.traceFlags) === TraceFlags.SAMPLED - ? '1' - : '0' - ); - } + private readonly _b3MultiPropagator: B3MultiPropagator = new B3MultiPropagator(); + private readonly _b3SinglePropagator: B3SinglePropagator = new B3SinglePropagator(); + private readonly _inject: ( + context: Context, + carrier: unknown, + setter: SetterFunction + ) => void; + + constructor(config: B3PropagatorConfig = {}) { + if (config.injectEncoding === B3InjectEncoding.MULTI_HEADER) { + this._inject = this._b3MultiPropagator.inject; + } else { + this._inject = this._b3SinglePropagator.inject; } } - extract(context: Context, carrier: unknown, getter: GetterFunction): Context { - const traceId = getTraceId(carrier, getter); - const spanId = getSpanId(carrier, getter); - const parentSpanId = getParentSpanId(carrier, getter); - const traceFlags = getTraceFlags(carrier, getter) as TraceFlags; - const debug = getDebug(carrier, getter); + inject(context: Context, carrier: unknown, setter: SetterFunction) { + this._inject(context, carrier, setter); + } - if ( - isValidTraceId(traceId) && - isValidSpanId(spanId) && - isValidParentSpanID(parentSpanId) && - isValidSampledValue(traceFlags) - ) { - context = context.setValue(PARENT_SPAN_ID_KEY, parentSpanId); - context = context.setValue(DEBUG_FLAG_KEY, debug); - return setExtractedSpanContext(context, { - traceId, - spanId, - isRemote: true, - traceFlags, - }); + extract(context: Context, carrier: unknown, getter: GetterFunction): Context { + if (getter(carrier, B3_CONTEXT_HEADER)) { + return this._b3SinglePropagator.extract(context, carrier, getter); + } else { + return this._b3MultiPropagator.extract(context, carrier, getter); } - return context; } } diff --git a/packages/opentelemetry-core/src/context/propagation/B3SinglePropagator.ts b/packages/opentelemetry-core/src/context/propagation/B3SinglePropagator.ts new file mode 100644 index 0000000000..72f84d9ac4 --- /dev/null +++ b/packages/opentelemetry-core/src/context/propagation/B3SinglePropagator.ts @@ -0,0 +1,91 @@ +/* + * Copyright The OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + Context, + GetterFunction, + TextMapPropagator, + SetterFunction, + TraceFlags, + isValidSpanId, + isValidTraceId, + isSpanContextValid, + getParentSpanContext, + setExtractedSpanContext, +} from '@opentelemetry/api'; +import { B3_DEBUG_FLAG_KEY } from './b3-common'; + +/** B3 single-header name */ +export const B3_CONTEXT_HEADER = 'b3'; + +const B3_CONTEXT_REGEX = /((?:[0-9a-f]{16}){1,2})-([0-9a-f]{16})(?:-([01d](?![0-9a-f])))?(?:-([0-9a-f]{16}))?/; +const PADDING = '0'.repeat(16); +const SAMPLED_VALUES = new Set(['d', '1']); +const DEBUG_STATE = 'd'; + +function convertToTraceId128(traceId: string): string { + return traceId.length == 32 ? traceId : `${PADDING}${traceId}`; +} + +function convertToTraceFlags(samplingState: string | undefined): TraceFlags { + if (samplingState && SAMPLED_VALUES.has(samplingState)) { + return TraceFlags.SAMPLED; + } + return TraceFlags.NONE; +} + +/** + * Propagator for the B3 single-header HTTP format. + * Based on: https://github.com/openzipkin/b3-propagation + */ +export class B3SinglePropagator implements TextMapPropagator { + inject(context: Context, carrier: unknown, setter: SetterFunction) { + const spanContext = getParentSpanContext(context); + if (!spanContext || !isSpanContextValid(spanContext)) return; + + const samplingState = + context.getValue(B3_DEBUG_FLAG_KEY) || spanContext.traceFlags & 0x1; + const value = `${spanContext.traceId}-${spanContext.spanId}-${samplingState}`; + setter(carrier, B3_CONTEXT_HEADER, value); + } + + extract(context: Context, carrier: unknown, getter: GetterFunction): Context { + const header = getter(carrier, B3_CONTEXT_HEADER); + const b3Context = Array.isArray(header) ? header[0] : header; + if (typeof b3Context !== 'string') return context; + + const match = b3Context.match(B3_CONTEXT_REGEX); + if (!match) return context; + + const [, extractedTraceId, spanId, samplingState] = match; + const traceId = convertToTraceId128(extractedTraceId); + + if (!isValidTraceId(traceId) || !isValidSpanId(spanId)) return context; + + const traceFlags = convertToTraceFlags(samplingState); + + if (samplingState === DEBUG_STATE) { + context = context.setValue(B3_DEBUG_FLAG_KEY, samplingState); + } + + return setExtractedSpanContext(context, { + traceId, + spanId, + isRemote: true, + traceFlags, + }); + } +} diff --git a/packages/opentelemetry-core/src/context/propagation/b3-common.ts b/packages/opentelemetry-core/src/context/propagation/b3-common.ts new file mode 100644 index 0000000000..dd0d49a365 --- /dev/null +++ b/packages/opentelemetry-core/src/context/propagation/b3-common.ts @@ -0,0 +1,22 @@ +/* + * Copyright The OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { createContextKey } from '@opentelemetry/api'; + +/** shared context for storing an extracted b3 debug flag */ +export const B3_DEBUG_FLAG_KEY = createContextKey( + 'OpenTelemetry Context Key B3 Debug Flag' +); diff --git a/packages/opentelemetry-core/src/index.ts b/packages/opentelemetry-core/src/index.ts index 4ac46ce314..569e4d9553 100644 --- a/packages/opentelemetry-core/src/index.ts +++ b/packages/opentelemetry-core/src/index.ts @@ -24,6 +24,8 @@ export * from './common/types'; export * from './ExportResult'; export * from './version'; export * from './context/propagation/B3Propagator'; +export * from './context/propagation/B3SinglePropagator'; +export * from './context/propagation/B3MultiPropagator'; export * from './context/propagation/composite'; export * from './context/propagation/HttpTraceContext'; export * from './context/propagation/types'; diff --git a/packages/opentelemetry-core/test/context/B3MultiPropagator.test.ts b/packages/opentelemetry-core/test/context/B3MultiPropagator.test.ts new file mode 100644 index 0000000000..62853630e2 --- /dev/null +++ b/packages/opentelemetry-core/test/context/B3MultiPropagator.test.ts @@ -0,0 +1,499 @@ +/* + * Copyright The OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + defaultGetter, + defaultSetter, + SpanContext, + TraceFlags, + getExtractedSpanContext, + setExtractedSpanContext, +} from '@opentelemetry/api'; +import { ROOT_CONTEXT } from '@opentelemetry/context-base'; +import * as assert from 'assert'; +import { + B3MultiPropagator, + X_B3_FLAGS, + X_B3_PARENT_SPAN_ID, + X_B3_SAMPLED, + X_B3_SPAN_ID, + X_B3_TRACE_ID, +} from '../../src/context/propagation/B3MultiPropagator'; +import { B3_DEBUG_FLAG_KEY } from '../../src/context/propagation/b3-common'; +import { TraceState } from '../../src/trace/TraceState'; + +describe('B3MultiPropagator', () => { + const b3Propagator = new B3MultiPropagator(); + let carrier: { [key: string]: unknown }; + + beforeEach(() => { + carrier = {}; + }); + + describe('.inject()', () => { + it('should set b3 traceId and spanId headers', () => { + const spanContext: SpanContext = { + traceId: 'd4cda95b652f4a1592b449d5929fda1b', + spanId: '6e0c63257de34c92', + traceFlags: TraceFlags.SAMPLED, + }; + + b3Propagator.inject( + setExtractedSpanContext(ROOT_CONTEXT, spanContext), + carrier, + defaultSetter + ); + assert.deepStrictEqual( + carrier[X_B3_TRACE_ID], + 'd4cda95b652f4a1592b449d5929fda1b' + ); + assert.deepStrictEqual(carrier[X_B3_SPAN_ID], '6e0c63257de34c92'); + assert.deepStrictEqual(carrier[X_B3_SAMPLED], '1'); + assert.deepStrictEqual(carrier[X_B3_FLAGS], undefined); + }); + + it('should set b3 traceId and spanId headers - ignore tracestate', () => { + const spanContext: SpanContext = { + traceId: 'd4cda95b652f4a1592b449d5929fda1b', + spanId: '6e0c63257de34c92', + traceFlags: TraceFlags.NONE, + traceState: new TraceState('foo=bar,baz=qux'), + isRemote: false, + }; + + b3Propagator.inject( + setExtractedSpanContext(ROOT_CONTEXT, spanContext), + carrier, + defaultSetter + ); + assert.deepStrictEqual( + carrier[X_B3_TRACE_ID], + 'd4cda95b652f4a1592b449d5929fda1b' + ); + assert.deepStrictEqual(carrier[X_B3_SPAN_ID], '6e0c63257de34c92'); + assert.deepStrictEqual(carrier[X_B3_SAMPLED], '0'); + assert.deepStrictEqual(carrier[X_B3_FLAGS], undefined); + }); + + it('should set flags headers', () => { + const spanContext: SpanContext = { + traceId: 'd4cda95b652f4a1592b449d5929fda1b', + spanId: '6e0c63257de34c92', + traceFlags: TraceFlags.NONE, + }; + const contextWithDebug = ROOT_CONTEXT.setValue(B3_DEBUG_FLAG_KEY, '1'); + + b3Propagator.inject( + setExtractedSpanContext(contextWithDebug, spanContext), + carrier, + defaultSetter + ); + assert.deepStrictEqual( + carrier[X_B3_TRACE_ID], + 'd4cda95b652f4a1592b449d5929fda1b' + ); + assert.deepStrictEqual(carrier[X_B3_SPAN_ID], '6e0c63257de34c92'); + assert.deepStrictEqual(carrier[X_B3_FLAGS], '1'); + assert.deepStrictEqual(carrier[X_B3_SAMPLED], undefined); + }); + + it('should not inject empty spancontext', () => { + const emptySpanContext = { + traceId: '', + spanId: '', + traceFlags: TraceFlags.NONE, + }; + b3Propagator.inject( + setExtractedSpanContext(ROOT_CONTEXT, emptySpanContext), + carrier, + defaultSetter + ); + 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); + }); + }); + + describe('.extract()', () => { + it('should extract context of a deferred span from carrier', () => { + carrier[X_B3_TRACE_ID] = '0af7651916cd43dd8448eb211c80319c'; + carrier[X_B3_SPAN_ID] = 'b7ad6b7169203331'; + const context = b3Propagator.extract( + ROOT_CONTEXT, + carrier, + defaultGetter + ); + const extractedSpanContext = getExtractedSpanContext(context); + assert.deepStrictEqual(extractedSpanContext, { + spanId: 'b7ad6b7169203331', + traceId: '0af7651916cd43dd8448eb211c80319c', + isRemote: true, + traceFlags: TraceFlags.NONE, + }); + assert.equal(context.getValue(B3_DEBUG_FLAG_KEY), undefined); + }); + + describe('when sampled flag is valid', () => { + describe('AND sampled flag is 1', () => { + 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 context = b3Propagator.extract( + ROOT_CONTEXT, + carrier, + defaultGetter + ); + const extractedSpanContext = getExtractedSpanContext(context); + + assert.deepStrictEqual(extractedSpanContext, { + spanId: 'b7ad6b7169203331', + traceId: '0af7651916cd43dd8448eb211c80319c', + isRemote: true, + traceFlags: TraceFlags.SAMPLED, + }); + assert.equal(context.getValue(B3_DEBUG_FLAG_KEY), undefined); + }); + }); + + describe('AND sampled flag is true', () => { + 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] = true; + const context = b3Propagator.extract( + ROOT_CONTEXT, + carrier, + defaultGetter + ); + const extractedSpanContext = getExtractedSpanContext(context); + + assert.deepStrictEqual(extractedSpanContext, { + spanId: 'b7ad6b7169203331', + traceId: '0af7651916cd43dd8448eb211c80319c', + isRemote: true, + traceFlags: TraceFlags.SAMPLED, + }); + assert.equal(context.getValue(B3_DEBUG_FLAG_KEY), undefined); + }); + }); + + describe('AND sampled flag is false', () => { + 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] = false; + const context = b3Propagator.extract( + ROOT_CONTEXT, + carrier, + defaultGetter + ); + const extractedSpanContext = getExtractedSpanContext(context); + + assert.deepStrictEqual(extractedSpanContext, { + spanId: 'b7ad6b7169203331', + traceId: '0af7651916cd43dd8448eb211c80319c', + isRemote: true, + traceFlags: TraceFlags.NONE, + }); + assert.equal(context.getValue(B3_DEBUG_FLAG_KEY), undefined); + }); + }); + }); + + describe('when debug flag is valid', () => { + describe('AND debug flag is 1', () => { + it('should extract context of a debug span from carrier', () => { + carrier[X_B3_TRACE_ID] = '0af7651916cd43dd8448eb211c80319c'; + carrier[X_B3_SPAN_ID] = 'b7ad6b7169203331'; + carrier[X_B3_FLAGS] = '1'; + const context = b3Propagator.extract( + ROOT_CONTEXT, + carrier, + defaultGetter + ); + const extractedSpanContext = getExtractedSpanContext(context); + + assert.deepStrictEqual(extractedSpanContext, { + spanId: 'b7ad6b7169203331', + traceId: '0af7651916cd43dd8448eb211c80319c', + isRemote: true, + traceFlags: TraceFlags.SAMPLED, + }); + assert.strictEqual(context.getValue(B3_DEBUG_FLAG_KEY), '1'); + }); + }); + }); + + describe('when debug flag is invalid', () => { + describe('AND debug flag is 0', () => { + it('should extract context of a span from carrier', () => { + carrier[X_B3_TRACE_ID] = '0af7651916cd43dd8448eb211c80319c'; + carrier[X_B3_SPAN_ID] = 'b7ad6b7169203331'; + carrier[X_B3_FLAGS] = '0'; + carrier[X_B3_SAMPLED] = '1'; + const context = b3Propagator.extract( + ROOT_CONTEXT, + carrier, + defaultGetter + ); + const extractedSpanContext = getExtractedSpanContext(context); + + assert.deepStrictEqual(extractedSpanContext, { + spanId: 'b7ad6b7169203331', + traceId: '0af7651916cd43dd8448eb211c80319c', + isRemote: true, + traceFlags: TraceFlags.SAMPLED, + }); + assert.equal(context.getValue(B3_DEBUG_FLAG_KEY), undefined); + }); + }); + + describe('AND debug flag is false', () => { + it('should extract context of a span from carrier', () => { + carrier[X_B3_TRACE_ID] = '0af7651916cd43dd8448eb211c80319c'; + carrier[X_B3_SPAN_ID] = 'b7ad6b7169203331'; + carrier[X_B3_FLAGS] = 'false'; + carrier[X_B3_SAMPLED] = '0'; + const context = b3Propagator.extract( + ROOT_CONTEXT, + carrier, + defaultGetter + ); + const extractedSpanContext = getExtractedSpanContext(context); + + assert.deepStrictEqual(extractedSpanContext, { + spanId: 'b7ad6b7169203331', + traceId: '0af7651916cd43dd8448eb211c80319c', + isRemote: true, + traceFlags: TraceFlags.NONE, + }); + assert.equal(context.getValue(B3_DEBUG_FLAG_KEY), undefined); + }); + }); + + describe('AND debug flag is true', () => { + it('should extract context of a span from carrier', () => { + carrier[X_B3_TRACE_ID] = '0af7651916cd43dd8448eb211c80319c'; + carrier[X_B3_SPAN_ID] = 'b7ad6b7169203331'; + carrier[X_B3_FLAGS] = 'true'; + carrier[X_B3_SAMPLED] = '0'; + const context = b3Propagator.extract( + ROOT_CONTEXT, + carrier, + defaultGetter + ); + const extractedSpanContext = getExtractedSpanContext(context); + + assert.deepStrictEqual(extractedSpanContext, { + spanId: 'b7ad6b7169203331', + traceId: '0af7651916cd43dd8448eb211c80319c', + isRemote: true, + traceFlags: TraceFlags.NONE, + }); + assert.equal(context.getValue(B3_DEBUG_FLAG_KEY), undefined); + }); + }); + + describe('AND debug flag is 2', () => { + it('should extract context of a span from carrier', () => { + carrier[X_B3_TRACE_ID] = '0af7651916cd43dd8448eb211c80319c'; + carrier[X_B3_SPAN_ID] = 'b7ad6b7169203331'; + carrier[X_B3_FLAGS] = '3'; + carrier[X_B3_SAMPLED] = '0'; + const context = b3Propagator.extract( + ROOT_CONTEXT, + carrier, + defaultGetter + ); + const extractedSpanContext = getExtractedSpanContext(context); + + assert.deepStrictEqual(extractedSpanContext, { + spanId: 'b7ad6b7169203331', + traceId: '0af7651916cd43dd8448eb211c80319c', + isRemote: true, + traceFlags: TraceFlags.NONE, + }); + assert.equal(context.getValue(B3_DEBUG_FLAG_KEY), undefined); + }); + }); + }); + + describe('when headers are invalid', () => { + describe('AND traceId is undefined', () => { + it('should return undefined', () => { + carrier[X_B3_TRACE_ID] = undefined; + carrier[X_B3_SPAN_ID] = 'b7ad6b7169203331'; + const context = getExtractedSpanContext( + b3Propagator.extract(ROOT_CONTEXT, carrier, defaultGetter) + ); + assert.deepStrictEqual(context, undefined); + }); + }); + + describe('AND spanId is undefined', () => { + it('should return undefined', () => { + carrier[X_B3_TRACE_ID] = '0af7651916cd43dd8448eb211c80319c'; + carrier[X_B3_SPAN_ID] = undefined; + const context = getExtractedSpanContext( + b3Propagator.extract(ROOT_CONTEXT, carrier, defaultGetter) + ); + assert.deepStrictEqual(context, undefined); + }); + }); + + describe('AND sample is 2', () => { + it('should return undefined', () => { + carrier[X_B3_TRACE_ID] = '0af7651916cd43dd8448eb211c80319c'; + carrier[X_B3_SPAN_ID] = 'b7ad6b7169203331'; + carrier[X_B3_SAMPLED] = '2'; + const context = getExtractedSpanContext( + b3Propagator.extract(ROOT_CONTEXT, carrier, defaultGetter) + ); + assert.deepStrictEqual(context, undefined); + }); + }); + + describe('AND b3 header is missing', () => { + it('should return undefined', () => { + const context = getExtractedSpanContext( + b3Propagator.extract(ROOT_CONTEXT, carrier, defaultGetter) + ); + assert.deepStrictEqual(context, undefined); + }); + }); + + describe('AND trace id is invalid', () => { + it('should return undefined', () => { + carrier[X_B3_TRACE_ID] = 'invalid!'; + carrier[X_B3_SPAN_ID] = 'b7ad6b7169203331'; + const context = getExtractedSpanContext( + b3Propagator.extract(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] = '1'; + const context = b3Propagator.extract( + ROOT_CONTEXT, + carrier, + defaultGetter + ); + const extractedSpanContext = getExtractedSpanContext(context); + assert.deepStrictEqual(extractedSpanContext, { + spanId: 'b7ad6b7169203331', + traceId: '0af7651916cd43dd8448eb211c80319c', + isRemote: true, + traceFlags: TraceFlags.SAMPLED, + }); + assert.equal(context.getValue(B3_DEBUG_FLAG_KEY), undefined); + }); + + it('should gracefully handle an invalid b3 header', () => { + // A set of test cases with different invalid combinations of a + // b3 header. These should all result in a `undefined` SpanContext + // value being extracted. + + const testCases: Record = { + invalidParts_tooShort: '00-ffffffffffffffffffffffffffffffff', + invalidParts_tooLong: + '00-ffffffffffffffffffffffffffffffff-ffffffffffffffff-00-01', + + invalidVersion_notHex: + '0x-ffffffffffffffffffffffffffffffff-ffffffffffffffff-00', + invalidVersion_tooShort: + '0-ffffffffffffffffffffffffffffffff-ffffffffffffffff-00', + invalidVersion_tooLong: + '000-ffffffffffffffffffffffffffffffff-ffffffffffffffff-00', + + invalidTraceId_empty: '00--ffffffffffffffff-01', + invalidTraceId_notHex: + '00-fffffffffffffffffffffffffffffffx-ffffffffffffffff-01', + invalidTraceId_allZeros: + '00-00000000000000000000000000000000-ffffffffffffffff-01', + invalidTraceId_tooShort: '00-ffffffff-ffffffffffffffff-01', + invalidTraceId_tooLong: + '00-ffffffffffffffffffffffffffffffff00-ffffffffffffffff-01', + + invalidSpanId_empty: '00-ffffffffffffffffffffffffffffffff--01', + invalidSpanId_notHex: + '00-ffffffffffffffffffffffffffffffff-fffffffffffffffx-01', + invalidSpanId_allZeros: + '00-ffffffffffffffffffffffffffffffff-0000000000000000-01', + invalidSpanId_tooShort: + '00-ffffffffffffffffffffffffffffffff-ffffffff-01', + invalidSpanId_tooLong: + '00-ffffffffffffffffffffffffffffffff-ffffffffffffffff0000-01', + }; + + Object.getOwnPropertyNames(testCases).forEach(testCase => { + carrier[X_B3_TRACE_ID] = testCases[testCase]; + const extractedSpanContext = getExtractedSpanContext( + b3Propagator.extract(ROOT_CONTEXT, carrier, defaultGetter) + ); + assert.deepStrictEqual(extractedSpanContext, undefined, testCase); + }); + }); + + it('should fail gracefully on bad responses from getter', () => { + const ctx1 = b3Propagator.extract( + ROOT_CONTEXT, + carrier, + (c, k) => 1 // not a number + ); + const ctx2 = b3Propagator.extract( + ROOT_CONTEXT, + carrier, + (c, k) => [] // empty array + ); + const ctx3 = b3Propagator.extract( + ROOT_CONTEXT, + carrier, + (c, k) => undefined // missing value + ); + + assert.ok(ctx1 === ROOT_CONTEXT); + assert.ok(ctx2 === ROOT_CONTEXT); + assert.ok(ctx3 === ROOT_CONTEXT); + }); + + it('should left-pad 64 bit trace ids with 0', () => { + carrier[X_B3_TRACE_ID] = '8448eb211c80319c'; + carrier[X_B3_SPAN_ID] = 'b7ad6b7169203331'; + carrier[X_B3_SAMPLED] = '1'; + const context = b3Propagator.extract( + ROOT_CONTEXT, + carrier, + defaultGetter + ); + const extractedSpanContext = getExtractedSpanContext(context); + + assert.deepStrictEqual(extractedSpanContext, { + spanId: 'b7ad6b7169203331', + traceId: '00000000000000008448eb211c80319c', + isRemote: true, + traceFlags: TraceFlags.SAMPLED, + }); + assert.equal(context.getValue(B3_DEBUG_FLAG_KEY), undefined); + }); + }); +}); diff --git a/packages/opentelemetry-core/test/context/B3Propagator.test.ts b/packages/opentelemetry-core/test/context/B3Propagator.test.ts index 6baf2076f6..566a2ee918 100644 --- a/packages/opentelemetry-core/test/context/B3Propagator.test.ts +++ b/packages/opentelemetry-core/test/context/B3Propagator.test.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +import * as assert from 'assert'; import { defaultGetter, defaultSetter, @@ -23,595 +24,130 @@ import { setExtractedSpanContext, } from '@opentelemetry/api'; import { ROOT_CONTEXT } from '@opentelemetry/context-base'; -import * as assert from 'assert'; import { B3Propagator, - X_B3_FLAGS, - X_B3_PARENT_SPAN_ID, + B3InjectEncoding, +} from '../../src/context/propagation/B3Propagator'; +import { B3_CONTEXT_HEADER } from '../../src/context/propagation/B3SinglePropagator'; +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'; +} from '../../src/context/propagation/B3MultiPropagator'; describe('B3Propagator', () => { - const b3Propagator = new B3Propagator(); + let propagator: B3Propagator; let carrier: { [key: string]: unknown }; beforeEach(() => { + propagator = new B3Propagator(); carrier = {}; }); describe('.inject()', () => { - it('should set b3 traceId and spanId headers', () => { + it('injects single header by default', () => { + propagator = new B3Propagator(); + const spanContext: SpanContext = { - traceId: 'd4cda95b652f4a1592b449d5929fda1b', - spanId: '6e0c63257de34c92', + traceId: '80f198ee56343ba864fe8b2a57d3eff7', + spanId: 'e457b5a2e4d86bd1', traceFlags: TraceFlags.SAMPLED, }; - b3Propagator.inject( + propagator.inject( setExtractedSpanContext(ROOT_CONTEXT, spanContext), carrier, defaultSetter ); - assert.deepStrictEqual( - carrier[X_B3_TRACE_ID], - 'd4cda95b652f4a1592b449d5929fda1b' - ); - 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', () => { - const spanContext: SpanContext = { - traceId: 'd4cda95b652f4a1592b449d5929fda1b', - spanId: '6e0c63257de34c92', - traceFlags: TraceFlags.NONE, - traceState: new TraceState('foo=bar,baz=qux'), - isRemote: false, - }; - b3Propagator.inject( - setExtractedSpanContext(ROOT_CONTEXT, spanContext), - carrier, - defaultSetter - ); - assert.deepStrictEqual( - carrier[X_B3_TRACE_ID], - 'd4cda95b652f4a1592b449d5929fda1b' - ); - 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); + const expected = '80f198ee56343ba864fe8b2a57d3eff7-e457b5a2e4d86bd1-1'; + assert.strictEqual(carrier[B3_CONTEXT_HEADER], expected); }); - it('should set flags headers', () => { - const spanContext: SpanContext = { - traceId: 'd4cda95b652f4a1592b449d5929fda1b', - spanId: '6e0c63257de34c92', - traceFlags: TraceFlags.NONE, - }; - const contextWithDebug = ROOT_CONTEXT.setValue(DEBUG_FLAG_KEY, '1'); - - b3Propagator.inject( - setExtractedSpanContext(contextWithDebug, spanContext), - carrier, - defaultSetter - ); - assert.deepStrictEqual( - carrier[X_B3_TRACE_ID], - 'd4cda95b652f4a1592b449d5929fda1b' - ); - 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('can be configured for multi header', () => { + propagator = new B3Propagator({ + injectEncoding: B3InjectEncoding.MULTI_HEADER, + }); - it('should set parentSpanId headers', () => { const spanContext: SpanContext = { - traceId: 'd4cda95b652f4a1592b449d5929fda1b', - spanId: '6e0c63257de34c92', - traceFlags: TraceFlags.NONE, + traceId: '80f198ee56343ba864fe8b2a57d3eff7', + spanId: 'e457b5a2e4d86bd1', + traceFlags: TraceFlags.SAMPLED, }; - const contextWithParentSpanId = ROOT_CONTEXT.setValue( - PARENT_SPAN_ID_KEY, - 'f4592dc481026a8c' - ); - b3Propagator.inject( - setExtractedSpanContext(contextWithParentSpanId, spanContext), + propagator.inject( + setExtractedSpanContext(ROOT_CONTEXT, spanContext), carrier, defaultSetter ); - assert.deepStrictEqual( - carrier[X_B3_TRACE_ID], - 'd4cda95b652f4a1592b449d5929fda1b' - ); - assert.deepStrictEqual(carrier[X_B3_PARENT_SPAN_ID], 'f4592dc481026a8c'); - assert.deepStrictEqual(carrier[X_B3_SPAN_ID], '6e0c63257de34c92'); - assert.deepStrictEqual(carrier[X_B3_FLAGS], undefined); - assert.deepStrictEqual(carrier[X_B3_SAMPLED], '0'); - }); - it('should not inject empty spancontext', () => { - const emptySpanContext = { - traceId: '', - spanId: '', - traceFlags: TraceFlags.NONE, - }; - b3Propagator.inject( - setExtractedSpanContext(ROOT_CONTEXT, emptySpanContext), - carrier, - defaultSetter + assert.strictEqual( + carrier[X_B3_TRACE_ID], + '80f198ee56343ba864fe8b2a57d3eff7' ); - 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); + assert.strictEqual(carrier[X_B3_SPAN_ID], 'e457b5a2e4d86bd1'); + assert.strictEqual(carrier[X_B3_SAMPLED], '1'); }); }); describe('.extract()', () => { - it('should extract context of a deferred span from carrier', () => { - carrier[X_B3_TRACE_ID] = '0af7651916cd43dd8448eb211c80319c'; - carrier[X_B3_SPAN_ID] = 'b7ad6b7169203331'; - const context = b3Propagator.extract( + const b3SingleCarrier = { + [B3_CONTEXT_HEADER]: + '80f198ee56343ba864fe8b2a57d3eff7-e457b5a2e4d86bd1-0', + }; + const b3MultiCarrier = { + [X_B3_TRACE_ID]: 'd4cda95b652f4a1592b449d5929fda1b', + [X_B3_SPAN_ID]: '6e0c63257de34c92', + [X_B3_SAMPLED]: '1', + }; + const b3MixedCarrier = { ...b3SingleCarrier, ...b3MultiCarrier }; + + it('extracts single header b3', () => { + const context = propagator.extract( ROOT_CONTEXT, - carrier, + b3SingleCarrier, defaultGetter ); + const extractedSpanContext = getExtractedSpanContext(context); assert.deepStrictEqual(extractedSpanContext, { - spanId: 'b7ad6b7169203331', - traceId: '0af7651916cd43dd8448eb211c80319c', + spanId: 'e457b5a2e4d86bd1', + traceId: '80f198ee56343ba864fe8b2a57d3eff7', isRemote: true, traceFlags: TraceFlags.NONE, }); - assert.equal(context.getValue(DEBUG_FLAG_KEY), undefined); - assert.equal(context.getValue(PARENT_SPAN_ID_KEY), undefined); - }); - - describe('when sampled flag is valid', () => { - describe('AND sampled flag is 1', () => { - 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 context = b3Propagator.extract( - ROOT_CONTEXT, - carrier, - defaultGetter - ); - const extractedSpanContext = getExtractedSpanContext(context); - - assert.deepStrictEqual(extractedSpanContext, { - spanId: 'b7ad6b7169203331', - traceId: '0af7651916cd43dd8448eb211c80319c', - isRemote: true, - traceFlags: TraceFlags.SAMPLED, - }); - assert.equal(context.getValue(DEBUG_FLAG_KEY), undefined); - assert.equal(context.getValue(PARENT_SPAN_ID_KEY), undefined); - }); - }); - - describe('AND sampled flag is true', () => { - 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] = true; - const context = b3Propagator.extract( - ROOT_CONTEXT, - carrier, - defaultGetter - ); - const extractedSpanContext = getExtractedSpanContext(context); - - assert.deepStrictEqual(extractedSpanContext, { - spanId: 'b7ad6b7169203331', - traceId: '0af7651916cd43dd8448eb211c80319c', - isRemote: true, - traceFlags: TraceFlags.SAMPLED, - }); - assert.equal(context.getValue(DEBUG_FLAG_KEY), undefined); - assert.equal(context.getValue(PARENT_SPAN_ID_KEY), undefined); - }); - }); - - describe('AND sampled flag is false', () => { - 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] = false; - const context = b3Propagator.extract( - ROOT_CONTEXT, - carrier, - defaultGetter - ); - const extractedSpanContext = getExtractedSpanContext(context); - - assert.deepStrictEqual(extractedSpanContext, { - spanId: 'b7ad6b7169203331', - traceId: '0af7651916cd43dd8448eb211c80319c', - isRemote: true, - traceFlags: TraceFlags.NONE, - }); - assert.equal(context.getValue(DEBUG_FLAG_KEY), undefined); - assert.equal(context.getValue(PARENT_SPAN_ID_KEY), undefined); - }); - }); - }); - - describe('when debug flag is valid', () => { - describe('AND debug flag is 1', () => { - it('should extract context of a debug span from carrier', () => { - carrier[X_B3_TRACE_ID] = '0af7651916cd43dd8448eb211c80319c'; - carrier[X_B3_SPAN_ID] = 'b7ad6b7169203331'; - carrier[X_B3_FLAGS] = '1'; - const context = b3Propagator.extract( - ROOT_CONTEXT, - carrier, - defaultGetter - ); - const extractedSpanContext = getExtractedSpanContext(context); - - assert.deepStrictEqual(extractedSpanContext, { - spanId: 'b7ad6b7169203331', - traceId: '0af7651916cd43dd8448eb211c80319c', - isRemote: true, - traceFlags: TraceFlags.SAMPLED, - }); - assert.strictEqual(context.getValue(DEBUG_FLAG_KEY), '1'); - assert.equal(context.getValue(PARENT_SPAN_ID_KEY), undefined); - }); - }); }); - describe('when debug flag is invalid', () => { - describe('AND debug flag is 0', () => { - it('should extract context of a span from carrier', () => { - carrier[X_B3_TRACE_ID] = '0af7651916cd43dd8448eb211c80319c'; - carrier[X_B3_SPAN_ID] = 'b7ad6b7169203331'; - carrier[X_B3_FLAGS] = '0'; - carrier[X_B3_SAMPLED] = '1'; - const context = b3Propagator.extract( - ROOT_CONTEXT, - carrier, - defaultGetter - ); - const extractedSpanContext = getExtractedSpanContext(context); - - assert.deepStrictEqual(extractedSpanContext, { - spanId: 'b7ad6b7169203331', - traceId: '0af7651916cd43dd8448eb211c80319c', - isRemote: true, - traceFlags: TraceFlags.SAMPLED, - }); - assert.equal(context.getValue(DEBUG_FLAG_KEY), undefined); - assert.equal(context.getValue(PARENT_SPAN_ID_KEY), undefined); - }); - }); - - describe('AND debug flag is false', () => { - it('should extract context of a span from carrier', () => { - carrier[X_B3_TRACE_ID] = '0af7651916cd43dd8448eb211c80319c'; - carrier[X_B3_SPAN_ID] = 'b7ad6b7169203331'; - carrier[X_B3_FLAGS] = 'false'; - carrier[X_B3_SAMPLED] = '0'; - const context = b3Propagator.extract( - ROOT_CONTEXT, - carrier, - defaultGetter - ); - const extractedSpanContext = getExtractedSpanContext(context); - - assert.deepStrictEqual(extractedSpanContext, { - spanId: 'b7ad6b7169203331', - traceId: '0af7651916cd43dd8448eb211c80319c', - isRemote: true, - traceFlags: TraceFlags.NONE, - }); - assert.equal(context.getValue(DEBUG_FLAG_KEY), undefined); - assert.equal(context.getValue(PARENT_SPAN_ID_KEY), undefined); - }); - }); - - describe('AND debug flag is true', () => { - it('should extract context of a span from carrier', () => { - carrier[X_B3_TRACE_ID] = '0af7651916cd43dd8448eb211c80319c'; - carrier[X_B3_SPAN_ID] = 'b7ad6b7169203331'; - carrier[X_B3_FLAGS] = 'true'; - carrier[X_B3_SAMPLED] = '0'; - const context = b3Propagator.extract( - ROOT_CONTEXT, - carrier, - defaultGetter - ); - const extractedSpanContext = getExtractedSpanContext(context); - - assert.deepStrictEqual(extractedSpanContext, { - spanId: 'b7ad6b7169203331', - traceId: '0af7651916cd43dd8448eb211c80319c', - isRemote: true, - traceFlags: TraceFlags.NONE, - }); - assert.equal(context.getValue(DEBUG_FLAG_KEY), undefined); - assert.equal(context.getValue(PARENT_SPAN_ID_KEY), undefined); - }); - }); - - describe('AND debug flag is 2', () => { - it('should extract context of a span from carrier', () => { - carrier[X_B3_TRACE_ID] = '0af7651916cd43dd8448eb211c80319c'; - carrier[X_B3_SPAN_ID] = 'b7ad6b7169203331'; - carrier[X_B3_FLAGS] = '3'; - carrier[X_B3_SAMPLED] = '0'; - const context = b3Propagator.extract( - ROOT_CONTEXT, - carrier, - defaultGetter - ); - const extractedSpanContext = getExtractedSpanContext(context); - - assert.deepStrictEqual(extractedSpanContext, { - spanId: 'b7ad6b7169203331', - traceId: '0af7651916cd43dd8448eb211c80319c', - isRemote: true, - traceFlags: TraceFlags.NONE, - }); - assert.equal(context.getValue(DEBUG_FLAG_KEY), undefined); - assert.equal(context.getValue(PARENT_SPAN_ID_KEY), undefined); - }); - }); - }); - - describe('when parent span id is valid', () => { - it('should extract context of a span from carrier', () => { - carrier[X_B3_TRACE_ID] = '0af7651916cd43dd8448eb211c80319c'; - carrier[X_B3_SPAN_ID] = 'b7ad6b7169203331'; - carrier[X_B3_PARENT_SPAN_ID] = 'f4592dc481026a8c'; - carrier[X_B3_FLAGS] = '0'; - carrier[X_B3_SAMPLED] = '1'; - const context = b3Propagator.extract( - ROOT_CONTEXT, - carrier, - defaultGetter - ); - const extractedSpanContext = getExtractedSpanContext(context); - - assert.deepStrictEqual(extractedSpanContext, { - spanId: 'b7ad6b7169203331', - traceId: '0af7651916cd43dd8448eb211c80319c', - isRemote: true, - traceFlags: TraceFlags.SAMPLED, - }); - assert.equal(context.getValue(DEBUG_FLAG_KEY), undefined); - assert.equal(context.getValue(PARENT_SPAN_ID_KEY), 'f4592dc481026a8c'); - }); - - describe('AND debug is 1', () => { - it('should extract context of a span from carrier', () => { - carrier[X_B3_TRACE_ID] = '0af7651916cd43dd8448eb211c80319c'; - carrier[X_B3_SPAN_ID] = 'b7ad6b7169203331'; - carrier[X_B3_PARENT_SPAN_ID] = 'f4592dc481026a8c'; - carrier[X_B3_FLAGS] = '1'; - carrier[X_B3_SAMPLED] = '0'; - const context = b3Propagator.extract( - ROOT_CONTEXT, - carrier, - defaultGetter - ); - const extractedSpanContext = getExtractedSpanContext(context); - - assert.deepStrictEqual(extractedSpanContext, { - spanId: 'b7ad6b7169203331', - traceId: '0af7651916cd43dd8448eb211c80319c', - isRemote: true, - traceFlags: TraceFlags.SAMPLED, - }); - assert.equal(context.getValue(DEBUG_FLAG_KEY), '1'); - assert.equal( - context.getValue(PARENT_SPAN_ID_KEY), - 'f4592dc481026a8c' - ); - }); - }); - }); - - describe('when headers are invalid', () => { - describe('AND traceId is undefined', () => { - it('should return undefined', () => { - carrier[X_B3_TRACE_ID] = undefined; - carrier[X_B3_SPAN_ID] = 'b7ad6b7169203331'; - const context = getExtractedSpanContext( - b3Propagator.extract(ROOT_CONTEXT, carrier, defaultGetter) - ); - assert.deepStrictEqual(context, undefined); - }); - }); - - describe('AND spanId is undefined', () => { - it('should return undefined', () => { - carrier[X_B3_TRACE_ID] = '0af7651916cd43dd8448eb211c80319c'; - carrier[X_B3_SPAN_ID] = undefined; - const context = getExtractedSpanContext( - b3Propagator.extract(ROOT_CONTEXT, carrier, defaultGetter) - ); - assert.deepStrictEqual(context, undefined); - }); - }); - - describe('AND parentSpanId is invalid', () => { - it('should return undefined', () => { - carrier[X_B3_TRACE_ID] = '0af7651916cd43dd8448eb211c80319c'; - carrier[X_B3_SPAN_ID] = 'b7ad6b7169203331'; - carrier[X_B3_PARENT_SPAN_ID] = 'invalid'; - const context = getExtractedSpanContext( - b3Propagator.extract(ROOT_CONTEXT, carrier, defaultGetter) - ); - assert.deepStrictEqual(context, undefined); - }); - }); - - describe('AND parentSpanId is a trace id', () => { - it('should return undefined', () => { - carrier[X_B3_TRACE_ID] = '0af7651916cd43dd8448eb211c80319c'; - carrier[X_B3_SPAN_ID] = 'b7ad6b7169203331'; - carrier[X_B3_PARENT_SPAN_ID] = '0af7651916cd43dd8448eb211c80319d'; - const context = getExtractedSpanContext( - b3Propagator.extract(ROOT_CONTEXT, carrier, defaultGetter) - ); - assert.deepStrictEqual(context, undefined); - }); - }); - - describe('AND sample is 2', () => { - it('should return undefined', () => { - carrier[X_B3_TRACE_ID] = '0af7651916cd43dd8448eb211c80319c'; - carrier[X_B3_SPAN_ID] = 'b7ad6b7169203331'; - carrier[X_B3_SAMPLED] = '2'; - const context = getExtractedSpanContext( - b3Propagator.extract(ROOT_CONTEXT, carrier, defaultGetter) - ); - assert.deepStrictEqual(context, undefined); - }); - }); - - describe('AND b3 header is missing', () => { - it('should return undefined', () => { - const context = getExtractedSpanContext( - b3Propagator.extract(ROOT_CONTEXT, carrier, defaultGetter) - ); - assert.deepStrictEqual(context, undefined); - }); - }); - - describe('AND trace id is invalid', () => { - it('should return undefined', () => { - carrier[X_B3_TRACE_ID] = 'invalid!'; - carrier[X_B3_SPAN_ID] = 'b7ad6b7169203331'; - const context = getExtractedSpanContext( - b3Propagator.extract(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] = '1'; - const context = b3Propagator.extract( + it('extracts multi header b3', () => { + const context = propagator.extract( ROOT_CONTEXT, - carrier, + b3MultiCarrier, defaultGetter ); + const extractedSpanContext = getExtractedSpanContext(context); assert.deepStrictEqual(extractedSpanContext, { - spanId: 'b7ad6b7169203331', - traceId: '0af7651916cd43dd8448eb211c80319c', + spanId: '6e0c63257de34c92', + traceId: 'd4cda95b652f4a1592b449d5929fda1b', 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', () => { - // A set of test cases with different invalid combinations of a - // b3 header. These should all result in a `undefined` SpanContext - // value being extracted. - - const testCases: Record = { - invalidParts_tooShort: '00-ffffffffffffffffffffffffffffffff', - invalidParts_tooLong: - '00-ffffffffffffffffffffffffffffffff-ffffffffffffffff-00-01', - - invalidVersion_notHex: - '0x-ffffffffffffffffffffffffffffffff-ffffffffffffffff-00', - invalidVersion_tooShort: - '0-ffffffffffffffffffffffffffffffff-ffffffffffffffff-00', - invalidVersion_tooLong: - '000-ffffffffffffffffffffffffffffffff-ffffffffffffffff-00', - - invalidTraceId_empty: '00--ffffffffffffffff-01', - invalidTraceId_notHex: - '00-fffffffffffffffffffffffffffffffx-ffffffffffffffff-01', - invalidTraceId_allZeros: - '00-00000000000000000000000000000000-ffffffffffffffff-01', - invalidTraceId_tooShort: '00-ffffffff-ffffffffffffffff-01', - invalidTraceId_tooLong: - '00-ffffffffffffffffffffffffffffffff00-ffffffffffffffff-01', - - invalidSpanId_empty: '00-ffffffffffffffffffffffffffffffff--01', - invalidSpanId_notHex: - '00-ffffffffffffffffffffffffffffffff-fffffffffffffffx-01', - invalidSpanId_allZeros: - '00-ffffffffffffffffffffffffffffffff-0000000000000000-01', - invalidSpanId_tooShort: - '00-ffffffffffffffffffffffffffffffff-ffffffff-01', - invalidSpanId_tooLong: - '00-ffffffffffffffffffffffffffffffff-ffffffffffffffff0000-01', - }; - - Object.getOwnPropertyNames(testCases).forEach(testCase => { - carrier[X_B3_TRACE_ID] = testCases[testCase]; - const extractedSpanContext = getExtractedSpanContext( - b3Propagator.extract(ROOT_CONTEXT, carrier, defaultGetter) - ); - assert.deepStrictEqual(extractedSpanContext, undefined, testCase); - }); - }); - - it('should fail gracefully on bad responses from getter', () => { - const ctx1 = b3Propagator.extract( - ROOT_CONTEXT, - carrier, - (c, k) => 1 // not a number - ); - const ctx2 = b3Propagator.extract( - ROOT_CONTEXT, - carrier, - (c, k) => [] // empty array - ); - const ctx3 = b3Propagator.extract( - ROOT_CONTEXT, - carrier, - (c, k) => undefined // missing value - ); - - assert.ok(ctx1 === ROOT_CONTEXT); - assert.ok(ctx2 === ROOT_CONTEXT); - assert.ok(ctx3 === ROOT_CONTEXT); }); - it('should left-pad 64 bit trace ids with 0', () => { - carrier[X_B3_TRACE_ID] = '8448eb211c80319c'; - carrier[X_B3_SPAN_ID] = 'b7ad6b7169203331'; - carrier[X_B3_SAMPLED] = '1'; - const context = b3Propagator.extract( + it('extracts single header over multi', () => { + const context = propagator.extract( ROOT_CONTEXT, - carrier, + b3MixedCarrier, defaultGetter ); - const extractedSpanContext = getExtractedSpanContext(context); + const extractedSpanContext = getExtractedSpanContext(context); assert.deepStrictEqual(extractedSpanContext, { - spanId: 'b7ad6b7169203331', - traceId: '00000000000000008448eb211c80319c', + spanId: 'e457b5a2e4d86bd1', + traceId: '80f198ee56343ba864fe8b2a57d3eff7', isRemote: true, - traceFlags: TraceFlags.SAMPLED, + traceFlags: TraceFlags.NONE, }); - assert.equal(context.getValue(DEBUG_FLAG_KEY), undefined); - assert.equal(context.getValue(PARENT_SPAN_ID_KEY), undefined); }); }); }); diff --git a/packages/opentelemetry-core/test/context/B3SinglePropagator.test.ts b/packages/opentelemetry-core/test/context/B3SinglePropagator.test.ts new file mode 100644 index 0000000000..b4d04569c1 --- /dev/null +++ b/packages/opentelemetry-core/test/context/B3SinglePropagator.test.ts @@ -0,0 +1,260 @@ +/* + * Copyright The OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as assert from 'assert'; +import { + defaultGetter, + defaultSetter, + SpanContext, + TraceFlags, + INVALID_SPANID, + INVALID_TRACEID, + getExtractedSpanContext, + setExtractedSpanContext, +} from '@opentelemetry/api'; +import { ROOT_CONTEXT } from '@opentelemetry/context-base'; +import { + B3SinglePropagator, + B3_CONTEXT_HEADER, +} from '../../src/context/propagation/B3SinglePropagator'; +import { B3_DEBUG_FLAG_KEY } from '../../src/context/propagation/b3-common'; + +describe('B3SinglePropagator', () => { + const propagator = new B3SinglePropagator(); + let carrier: { [key: string]: unknown }; + + beforeEach(() => { + carrier = {}; + }); + + describe('.inject()', () => { + it('injects context with sampled trace flags', () => { + const spanContext: SpanContext = { + traceId: '80f198ee56343ba864fe8b2a57d3eff7', + spanId: 'e457b5a2e4d86bd1', + traceFlags: TraceFlags.SAMPLED, + }; + + propagator.inject( + setExtractedSpanContext(ROOT_CONTEXT, spanContext), + carrier, + defaultSetter + ); + + const expected = '80f198ee56343ba864fe8b2a57d3eff7-e457b5a2e4d86bd1-1'; + assert.strictEqual(carrier[B3_CONTEXT_HEADER], expected); + }); + + it('injects context with unspecified trace flags', () => { + const spanContext: SpanContext = { + traceId: '80f198ee56343ba864fe8b2a57d3eff7', + spanId: 'e457b5a2e4d86bd1', + traceFlags: TraceFlags.NONE, + }; + + propagator.inject( + setExtractedSpanContext(ROOT_CONTEXT, spanContext), + carrier, + defaultSetter + ); + + const expected = '80f198ee56343ba864fe8b2a57d3eff7-e457b5a2e4d86bd1-0'; + assert.strictEqual(carrier[B3_CONTEXT_HEADER], expected); + }); + + it('injects debug flag when present', () => { + const spanContext: SpanContext = { + traceId: '80f198ee56343ba864fe8b2a57d3eff7', + spanId: 'e457b5a2e4d86bd1', + traceFlags: TraceFlags.SAMPLED, + }; + + const context = ROOT_CONTEXT.setValue(B3_DEBUG_FLAG_KEY, 'd'); + + propagator.inject( + setExtractedSpanContext(context, spanContext), + carrier, + defaultSetter + ); + + const expected = '80f198ee56343ba864fe8b2a57d3eff7-e457b5a2e4d86bd1-d'; + assert.strictEqual(carrier[B3_CONTEXT_HEADER], expected); + }); + + it('no-ops if traceid invalid', () => { + const spanContext: SpanContext = { + traceId: INVALID_TRACEID, + spanId: 'e457b5a2e4d86bd1', + traceFlags: TraceFlags.SAMPLED, + }; + + propagator.inject( + setExtractedSpanContext(ROOT_CONTEXT, spanContext), + carrier, + defaultSetter + ); + + assert.strictEqual(carrier[B3_CONTEXT_HEADER], undefined); + }); + + it('no-ops if spanid invalid', () => { + const spanContext: SpanContext = { + traceId: '80f198ee56343ba864fe8b2a57d3eff7', + spanId: INVALID_SPANID, + traceFlags: TraceFlags.SAMPLED, + }; + + propagator.inject( + setExtractedSpanContext(ROOT_CONTEXT, spanContext), + carrier, + defaultSetter + ); + + assert.strictEqual(carrier[B3_CONTEXT_HEADER], undefined); + }); + }); + + describe('.extract', () => { + it('extracts context with traceid, spanid, sampling flag, parent spanid', () => { + carrier = { + [B3_CONTEXT_HEADER]: + '80f198ee56343ba864fe8b2a57d3eff7-e457b5a2e4d86bd1-1-05e3ac9a4f6e3b90', + }; + + const context = propagator.extract(ROOT_CONTEXT, carrier, defaultGetter); + + const extractedSpanContext = getExtractedSpanContext(context); + assert.deepStrictEqual(extractedSpanContext, { + spanId: 'e457b5a2e4d86bd1', + traceId: '80f198ee56343ba864fe8b2a57d3eff7', + isRemote: true, + traceFlags: TraceFlags.SAMPLED, + }); + }); + + it('extracts context with traceid, spanid, sampling flag', () => { + carrier = { + [B3_CONTEXT_HEADER]: + '80f198ee56343ba864fe8b2a57d3eff7-e457b5a2e4d86bd1-1', + }; + + const context = propagator.extract(ROOT_CONTEXT, carrier, defaultGetter); + + const extractedSpanContext = getExtractedSpanContext(context); + assert.deepStrictEqual(extractedSpanContext, { + spanId: 'e457b5a2e4d86bd1', + traceId: '80f198ee56343ba864fe8b2a57d3eff7', + isRemote: true, + traceFlags: TraceFlags.SAMPLED, + }); + }); + + it('extracts context with traceid, spanid', () => { + carrier = { + [B3_CONTEXT_HEADER]: + '80f198ee56343ba864fe8b2a57d3eff7-e457b5a2e4d86bd1', + }; + + const context = propagator.extract(ROOT_CONTEXT, carrier, defaultGetter); + + const extractedSpanContext = getExtractedSpanContext(context); + assert.deepStrictEqual(extractedSpanContext, { + spanId: 'e457b5a2e4d86bd1', + traceId: '80f198ee56343ba864fe8b2a57d3eff7', + isRemote: true, + traceFlags: TraceFlags.NONE, + }); + }); + + it('converts 8-byte traceid', () => { + carrier = { + [B3_CONTEXT_HEADER]: '4aaba1a52cf8ee09-e457b5a2e4d86bd1', + }; + + const context = propagator.extract(ROOT_CONTEXT, carrier, defaultGetter); + + const extractedSpanContext = getExtractedSpanContext(context); + assert.deepStrictEqual(extractedSpanContext, { + spanId: 'e457b5a2e4d86bd1', + traceId: '00000000000000004aaba1a52cf8ee09', + isRemote: true, + traceFlags: TraceFlags.NONE, + }); + }); + + it('converts debug flag to sampled', () => { + carrier = { + [B3_CONTEXT_HEADER]: + '80f198ee56343ba864fe8b2a57d3eff7-e457b5a2e4d86bd1-d', + }; + + const context = propagator.extract(ROOT_CONTEXT, carrier, defaultGetter); + + const extractedSpanContext = getExtractedSpanContext(context); + assert.deepStrictEqual(extractedSpanContext, { + spanId: 'e457b5a2e4d86bd1', + traceId: '80f198ee56343ba864fe8b2a57d3eff7', + isRemote: true, + traceFlags: TraceFlags.SAMPLED, + }); + assert.strictEqual('d', context.getValue(B3_DEBUG_FLAG_KEY)); + }); + + it('handles malformed traceid', () => { + carrier = { + [B3_CONTEXT_HEADER]: 'abc123-e457b5a2e4d86bd1', + }; + + const context = propagator.extract(ROOT_CONTEXT, carrier, defaultGetter); + + const extractedSpanContext = getExtractedSpanContext(context); + assert.deepStrictEqual(undefined, extractedSpanContext); + }); + + it('handles malformed spanid', () => { + carrier = { + [B3_CONTEXT_HEADER]: '80f198ee56343ba864fe8b2a57d3eff7-abc123', + }; + + const context = propagator.extract(ROOT_CONTEXT, carrier, defaultGetter); + + const extractedSpanContext = getExtractedSpanContext(context); + assert.deepStrictEqual(undefined, extractedSpanContext); + }); + + it('handles invalid traceid', () => { + carrier = { + [B3_CONTEXT_HEADER]: `${INVALID_TRACEID}-e457b5a2e4d86bd1`, + }; + + const context = propagator.extract(ROOT_CONTEXT, carrier, defaultGetter); + + const extractedSpanContext = getExtractedSpanContext(context); + assert.deepStrictEqual(undefined, extractedSpanContext); + }); + + it('handles invalid spanid', () => { + carrier = { + [B3_CONTEXT_HEADER]: `80f198ee56343ba864fe8b2a57d3eff7-${INVALID_SPANID}`, + }; + + const context = propagator.extract(ROOT_CONTEXT, carrier, defaultGetter); + + const extractedSpanContext = getExtractedSpanContext(context); + assert.deepStrictEqual(undefined, extractedSpanContext); + }); + }); +}); diff --git a/packages/opentelemetry-core/test/context/composite.test.ts b/packages/opentelemetry-core/test/context/composite.test.ts index 0291c53250..f4f224bc73 100644 --- a/packages/opentelemetry-core/test/context/composite.test.ts +++ b/packages/opentelemetry-core/test/context/composite.test.ts @@ -30,11 +30,11 @@ import { RandomIdGenerator, } from '../../src'; import { - B3Propagator, + B3MultiPropagator, X_B3_SAMPLED, X_B3_SPAN_ID, X_B3_TRACE_ID, -} from '../../src/context/propagation/B3Propagator'; +} from '../../src/context/propagation/B3MultiPropagator'; import { TRACE_PARENT_HEADER, TRACE_STATE_HEADER, @@ -69,7 +69,7 @@ describe('Composite Propagator', () => { it('should inject context using all configured propagators', () => { const composite = new CompositePropagator({ - propagators: [new B3Propagator(), new HttpTraceContext()], + propagators: [new B3MultiPropagator(), new HttpTraceContext()], }); composite.inject(ctxWithSpanContext, carrier, defaultSetter); @@ -111,7 +111,7 @@ describe('Composite Propagator', () => { it('should extract context using all configured propagators', () => { const composite = new CompositePropagator({ - propagators: [new B3Propagator(), new HttpTraceContext()], + propagators: [new B3MultiPropagator(), new HttpTraceContext()], }); const spanContext = getExtractedSpanContext( composite.extract(ROOT_CONTEXT, carrier, defaultGetter) diff --git a/packages/opentelemetry-plugin-fetch/test/fetch.test.ts b/packages/opentelemetry-plugin-fetch/test/fetch.test.ts index 41e2a570c7..39ca5ab8f3 100644 --- a/packages/opentelemetry-plugin-fetch/test/fetch.test.ts +++ b/packages/opentelemetry-plugin-fetch/test/fetch.test.ts @@ -218,7 +218,11 @@ describe('fetch', () => { }); before(() => { - api.propagation.setGlobalPropagator(new core.B3Propagator()); + api.propagation.setGlobalPropagator( + new core.B3Propagator({ + injectEncoding: core.B3InjectEncoding.MULTI_HEADER, + }) + ); }); describe('when request is successful', () => { diff --git a/packages/opentelemetry-plugin-xml-http-request/test/xhr.test.ts b/packages/opentelemetry-plugin-xml-http-request/test/xhr.test.ts index 515598fbdd..44c636d719 100644 --- a/packages/opentelemetry-plugin-xml-http-request/test/xhr.test.ts +++ b/packages/opentelemetry-plugin-xml-http-request/test/xhr.test.ts @@ -23,6 +23,7 @@ import { X_B3_TRACE_ID, isWrapped, NoopLogger, + B3InjectEncoding, } from '@opentelemetry/core'; import { ZoneContextManager } from '@opentelemetry/context-zone'; import * as tracing from '@opentelemetry/tracing'; @@ -143,7 +144,9 @@ describe('xhr', () => { }); before(() => { - api.propagation.setGlobalPropagator(new B3Propagator()); + api.propagation.setGlobalPropagator( + new B3Propagator({ injectEncoding: B3InjectEncoding.MULTI_HEADER }) + ); }); describe('when request is successful', () => {