diff --git a/packages/opentelemetry-api/src/api/propagation.ts b/packages/opentelemetry-api/src/api/propagation.ts index 60a950e4d6..0e62ad76eb 100644 --- a/packages/opentelemetry-api/src/api/propagation.ts +++ b/packages/opentelemetry-api/src/api/propagation.ts @@ -15,9 +15,10 @@ */ import { Context } from '@opentelemetry/scope-base'; -import { Carrier } from '../context/propagation/carrier'; +import { defaultGetter, GetterFunction } from '../context/propagation/getter'; import { HttpTextFormat } from '../context/propagation/HttpTextFormat'; import { NOOP_HTTP_TEXT_FORMAT } from '../context/propagation/NoopHttpTextFormat'; +import { defaultSetter, SetterFunction } from '../context/propagation/setter'; import { ContextAPI } from './context'; const contextApi = ContextAPI.getInstance(); @@ -53,19 +54,29 @@ export class PropagationAPI { * Inject context into a carrier to be propagated inter-process * * @param carrier carrier to inject context into + * @param setter Function used to set values on the carrier * @param context Context carrying tracing data to inject. Defaults to the currently active context. */ - public inject(carrier: Carrier, context = contextApi.active()): void { - return this._propagator.inject(context, carrier); + public inject( + carrier: Carrier, + setter: SetterFunction = defaultSetter, + context = contextApi.active() + ): void { + return this._propagator.inject(context, carrier, setter); } /** * Extract context from a carrier * * @param carrier Carrier to extract context from + * @param getter Function used to extract keys from a carrier * @param context Context which the newly created context will inherit from. Defaults to the currently active context. */ - public extract(carrier: Carrier, context = contextApi.active()): Context { - return this._propagator.extract(context, carrier); + public extract( + carrier: Carrier, + getter: GetterFunction = defaultGetter, + context = contextApi.active() + ): Context { + return this._propagator.extract(context, carrier, getter); } } diff --git a/packages/opentelemetry-api/src/context/propagation/HttpTextFormat.ts b/packages/opentelemetry-api/src/context/propagation/HttpTextFormat.ts index 6e14f94868..efd1e4efab 100644 --- a/packages/opentelemetry-api/src/context/propagation/HttpTextFormat.ts +++ b/packages/opentelemetry-api/src/context/propagation/HttpTextFormat.ts @@ -15,7 +15,8 @@ */ import { Context } from '@opentelemetry/scope-base'; -import { Carrier } from './carrier'; +import { SetterFunction } from './setter'; +import { GetterFunction } from './getter'; /** * Injects {@link Context} into and extracts it from carriers that travel @@ -37,8 +38,10 @@ export interface HttpTextFormat { * the wire. * @param carrier the carrier of propagation fields, such as http request * headers. + * @param setter a function which accepts a carrier, key, and value, which + * sets the key on the carrier to the value. */ - inject(context: Context, carrier: Carrier): void; + inject(context: Context, carrier: unknown, setter: SetterFunction): void; /** * Given a {@link Context} and a carrier, extract context values from a @@ -49,6 +52,8 @@ export interface HttpTextFormat { * the wire. * @param carrier the carrier of propagation fields, such as http request * headers. + * @param getter a function which accepts a carrier and a key, and returns + * the value from the carrier identified by the key. */ - extract(context: Context, carrier: Carrier): Context; + extract(context: Context, carrier: unknown, getter: GetterFunction): Context; } diff --git a/packages/opentelemetry-api/src/context/propagation/NoopHttpTextFormat.ts b/packages/opentelemetry-api/src/context/propagation/NoopHttpTextFormat.ts index 1f38729eb5..4978cc0fbc 100644 --- a/packages/opentelemetry-api/src/context/propagation/NoopHttpTextFormat.ts +++ b/packages/opentelemetry-api/src/context/propagation/NoopHttpTextFormat.ts @@ -15,7 +15,6 @@ */ import { Context } from '@opentelemetry/scope-base'; -import { Carrier } from './carrier'; import { HttpTextFormat } from './HttpTextFormat'; /** @@ -23,9 +22,9 @@ import { HttpTextFormat } from './HttpTextFormat'; */ export class NoopHttpTextFormat implements HttpTextFormat { /** Noop inject function does nothing */ - inject(context: Context, carrier: Carrier): void {} + inject(context: Context, carrier: unknown, setter: Function): void {} /** Noop extract function does nothing and returns the input context */ - extract(context: Context, carrier: Carrier): Context { + extract(context: Context, carrier: unknown, getter: Function): Context { return context; } } diff --git a/packages/opentelemetry-api/src/context/propagation/getter.ts b/packages/opentelemetry-api/src/context/propagation/getter.ts new file mode 100644 index 0000000000..158bd0fdd1 --- /dev/null +++ b/packages/opentelemetry-api/src/context/propagation/getter.ts @@ -0,0 +1,31 @@ +/*! + * Copyright 2020, 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. + */ + +export type GetterFunction = ( + carrier: Carrier, + key: string +) => unknown; + +/** + * Default getter which just does a simple property access. Returns + * undefined if the key is not set. + * + * @param carrier + * @param key + */ +export function defaultGetter(carrier: any, key: string): unknown { + return carrier[key]; +} diff --git a/packages/opentelemetry-api/src/context/propagation/carrier.ts b/packages/opentelemetry-api/src/context/propagation/setter.ts similarity index 65% rename from packages/opentelemetry-api/src/context/propagation/carrier.ts rename to packages/opentelemetry-api/src/context/propagation/setter.ts index 7644ace1af..29c6abed73 100644 --- a/packages/opentelemetry-api/src/context/propagation/carrier.ts +++ b/packages/opentelemetry-api/src/context/propagation/setter.ts @@ -14,6 +14,18 @@ * limitations under the License. */ -export type Carrier = { - [key: string]: unknown; -}; +export type SetterFunction = ( + carrier: Carrier, + key: string, + value: unknown +) => void; + +/** + * Default setter which sets value via direct property access + * + * @param carrier + * @param key + */ +export function defaultSetter(carrier: any, key: string, value: unknown) { + carrier[key] = value; +} diff --git a/packages/opentelemetry-api/src/index.ts b/packages/opentelemetry-api/src/index.ts index 2653d5af56..23f31f3ec6 100644 --- a/packages/opentelemetry-api/src/index.ts +++ b/packages/opentelemetry-api/src/index.ts @@ -16,8 +16,9 @@ export * from './common/Logger'; export * from './common/Time'; -export * from './context/propagation/carrier'; +export * from './context/propagation/getter'; export * from './context/propagation/HttpTextFormat'; +export * from './context/propagation/setter'; export * from './correlation_context/CorrelationContext'; export * from './correlation_context/EntryValue'; export * from './metrics/BoundInstrument'; diff --git a/packages/opentelemetry-api/test/noop-implementations/noop-tracer.test.ts b/packages/opentelemetry-api/test/noop-implementations/noop-tracer.test.ts index fc623d2526..f1e28be91f 100644 --- a/packages/opentelemetry-api/test/noop-implementations/noop-tracer.test.ts +++ b/packages/opentelemetry-api/test/noop-implementations/noop-tracer.test.ts @@ -14,9 +14,11 @@ * limitations under the License. */ +import { Context } from '@opentelemetry/scope-base'; import * as assert from 'assert'; import { NoopTracer, NOOP_SPAN, SpanKind } from '../../src'; -import { Context } from '@opentelemetry/scope-base'; +import { defaultGetter } from '../../src/context/propagation/getter'; +import { defaultSetter } from '../../src/context/propagation/setter'; describe('NoopTracer', () => { it('should not crash', () => { @@ -38,9 +40,9 @@ describe('NoopTracer', () => { const httpTextFormat = tracer.getHttpTextFormat(); assert.ok(httpTextFormat); - httpTextFormat.inject(Context.ROOT_CONTEXT, {}); + httpTextFormat.inject(Context.ROOT_CONTEXT, {}, defaultSetter); assert.deepStrictEqual( - httpTextFormat.extract(Context.ROOT_CONTEXT, {}), + httpTextFormat.extract(Context.ROOT_CONTEXT, {}, defaultGetter), Context.ROOT_CONTEXT ); }); diff --git a/packages/opentelemetry-core/src/context/propagation/B3Format.ts b/packages/opentelemetry-core/src/context/propagation/B3Format.ts index 2f2b7c9142..55681ddefe 100644 --- a/packages/opentelemetry-core/src/context/propagation/B3Format.ts +++ b/packages/opentelemetry-core/src/context/propagation/B3Format.ts @@ -15,9 +15,10 @@ */ import { - Carrier, Context, + GetterFunction, HttpTextFormat, + SetterFunction, TraceFlags, } from '@opentelemetry/api'; import { getParentSpanContext, setExtractedSpanContext } from '../context'; @@ -42,7 +43,7 @@ function isValidSpanId(spanId: string): boolean { * Based on: https://github.com/openzipkin/b3-propagation */ export class B3Format implements HttpTextFormat { - inject(context: Context, carrier: Carrier) { + inject(context: Context, carrier: unknown, setter: SetterFunction) { const spanContext = getParentSpanContext(context); if (!spanContext) return; @@ -50,21 +51,21 @@ export class B3Format implements HttpTextFormat { isValidTraceId(spanContext.traceId) && isValidSpanId(spanContext.spanId) ) { - carrier[X_B3_TRACE_ID] = spanContext.traceId; - carrier[X_B3_SPAN_ID] = spanContext.spanId; + setter(carrier, X_B3_TRACE_ID, spanContext.traceId); + setter(carrier, X_B3_SPAN_ID, spanContext.spanId); // We set the header only if there is an existing sampling decision. // Otherwise we will omit it => Absent. if (spanContext.traceFlags !== undefined) { - carrier[X_B3_SAMPLED] = Number(spanContext.traceFlags); + setter(carrier, X_B3_SAMPLED, Number(spanContext.traceFlags)); } } } - extract(context: Context, carrier: Carrier): Context { - const traceIdHeader = carrier[X_B3_TRACE_ID]; - const spanIdHeader = carrier[X_B3_SPAN_ID]; - const sampledHeader = carrier[X_B3_SAMPLED]; + extract(context: Context, carrier: unknown, getter: GetterFunction): Context { + const traceIdHeader = getter(carrier, X_B3_TRACE_ID); + const spanIdHeader = getter(carrier, X_B3_SPAN_ID); + const sampledHeader = getter(carrier, X_B3_SAMPLED); if (!traceIdHeader || !spanIdHeader) return context; const traceId = Array.isArray(traceIdHeader) ? traceIdHeader[0] diff --git a/packages/opentelemetry-core/src/context/propagation/HttpTraceContext.ts b/packages/opentelemetry-core/src/context/propagation/HttpTraceContext.ts index c1495579f1..12899eabf9 100644 --- a/packages/opentelemetry-core/src/context/propagation/HttpTraceContext.ts +++ b/packages/opentelemetry-core/src/context/propagation/HttpTraceContext.ts @@ -15,9 +15,10 @@ */ import { - Carrier, Context, + GetterFunction, HttpTextFormat, + SetterFunction, SpanContext, TraceFlags, } from '@opentelemetry/api'; @@ -63,7 +64,7 @@ export function parseTraceParent(traceParent: string): SpanContext | null { * https://www.w3.org/TR/trace-context/ */ export class HttpTraceContext implements HttpTextFormat { - inject(context: Context, carrier: Carrier) { + inject(context: Context, carrier: unknown, setter: SetterFunction) { const spanContext = getParentSpanContext(context); if (!spanContext) return; @@ -71,14 +72,14 @@ export class HttpTraceContext implements HttpTextFormat { spanContext.spanId }-0${Number(spanContext.traceFlags || TraceFlags.UNSAMPLED).toString(16)}`; - carrier[TRACE_PARENT_HEADER] = traceParent; + setter(carrier, TRACE_PARENT_HEADER, traceParent); if (spanContext.traceState) { - carrier[TRACE_STATE_HEADER] = spanContext.traceState.serialize(); + setter(carrier, TRACE_STATE_HEADER, spanContext.traceState.serialize()); } } - extract(context: Context, carrier: Carrier): Context { - const traceParentHeader = carrier[TRACE_PARENT_HEADER]; + extract(context: Context, carrier: unknown, getter: GetterFunction): Context { + const traceParentHeader = getter(carrier, TRACE_PARENT_HEADER); if (!traceParentHeader) return context; const traceParent = Array.isArray(traceParentHeader) ? traceParentHeader[0] @@ -88,7 +89,7 @@ export class HttpTraceContext implements HttpTextFormat { spanContext.isRemote = true; - const traceStateHeader = carrier[TRACE_STATE_HEADER]; + const traceStateHeader = getter(carrier, TRACE_STATE_HEADER); if (traceStateHeader) { // If more than one `tracestate` header is found, we merge them into a // single header. diff --git a/packages/opentelemetry-core/src/context/propagation/composite.ts b/packages/opentelemetry-core/src/context/propagation/composite.ts index c1a2906658..0979021fb6 100644 --- a/packages/opentelemetry-core/src/context/propagation/composite.ts +++ b/packages/opentelemetry-core/src/context/propagation/composite.ts @@ -14,7 +14,13 @@ * limitations under the License. */ -import { Carrier, Context, HttpTextFormat, Logger } from '@opentelemetry/api'; +import { + Context, + GetterFunction, + HttpTextFormat, + Logger, + SetterFunction, +} from '@opentelemetry/api'; import { NoopLogger } from '../../common/NoopLogger'; import { CompositePropagatorConfig } from './types'; @@ -42,10 +48,10 @@ export class CompositePropagator implements HttpTextFormat { * @param context Context to inject * @param carrier Carrier into which context will be injected */ - inject(context: Context, carrier: Carrier) { + inject(context: Context, carrier: unknown, setter: SetterFunction) { for (const propagator of this._propagators) { try { - propagator.inject(context, carrier); + propagator.inject(context, carrier, setter); } catch (err) { this._logger.warn( `Failed to inject with ${propagator.constructor.name}. Err: ${err.message}` @@ -63,10 +69,10 @@ export class CompositePropagator implements HttpTextFormat { * @param context Context to add values to * @param carrier Carrier from which to extract context */ - extract(context: Context, carrier: Carrier): Context { + extract(context: Context, carrier: unknown, getter: GetterFunction): Context { return this._propagators.reduce((ctx, propagator) => { try { - return propagator.extract(ctx, carrier); + return propagator.extract(ctx, carrier, getter); } catch (err) { this._logger.warn( `Failed to inject with ${propagator.constructor.name}. Err: ${err.message}` diff --git a/packages/opentelemetry-core/test/context/B3Format.test.ts b/packages/opentelemetry-core/test/context/B3Format.test.ts index df7a1e6162..22c327819d 100644 --- a/packages/opentelemetry-core/test/context/B3Format.test.ts +++ b/packages/opentelemetry-core/test/context/B3Format.test.ts @@ -14,13 +14,18 @@ * limitations under the License. */ -import { SpanContext, TraceFlags } from '@opentelemetry/api'; +import { + defaultGetter, + defaultSetter, + SpanContext, + TraceFlags, +} from '@opentelemetry/api'; +import { Context } from '@opentelemetry/scope-base'; import * as assert from 'assert'; import { - setExtractedSpanContext, getExtractedSpanContext, + setExtractedSpanContext, } from '../../src/context/context'; -import { Context } from '@opentelemetry/scope-base'; import { B3Format, X_B3_SAMPLED, @@ -47,7 +52,8 @@ describe('B3Format', () => { b3Format.inject( setExtractedSpanContext(Context.ROOT_CONTEXT, spanContext), - carrier + carrier, + defaultSetter ); assert.deepStrictEqual( carrier[X_B3_TRACE_ID], @@ -68,7 +74,8 @@ describe('B3Format', () => { b3Format.inject( setExtractedSpanContext(Context.ROOT_CONTEXT, spanContext), - carrier + carrier, + defaultSetter ); assert.deepStrictEqual( carrier[X_B3_TRACE_ID], @@ -85,7 +92,8 @@ describe('B3Format', () => { }; b3Format.inject( setExtractedSpanContext(Context.ROOT_CONTEXT, emptySpanContext), - carrier + carrier, + defaultSetter ); assert.deepStrictEqual(carrier[X_B3_TRACE_ID], undefined); assert.deepStrictEqual(carrier[X_B3_SPAN_ID], undefined); @@ -99,7 +107,8 @@ describe('B3Format', () => { b3Format.inject( setExtractedSpanContext(Context.ROOT_CONTEXT, spanContext), - carrier + carrier, + defaultSetter ); assert.deepStrictEqual( carrier[X_B3_TRACE_ID], @@ -115,7 +124,7 @@ describe('B3Format', () => { carrier[X_B3_TRACE_ID] = '0af7651916cd43dd8448eb211c80319c'; carrier[X_B3_SPAN_ID] = 'b7ad6b7169203331'; const extractedSpanContext = getExtractedSpanContext( - b3Format.extract(Context.ROOT_CONTEXT, carrier) + b3Format.extract(Context.ROOT_CONTEXT, carrier, defaultGetter) ); assert.deepStrictEqual(extractedSpanContext, { @@ -131,7 +140,7 @@ describe('B3Format', () => { carrier[X_B3_SPAN_ID] = 'b7ad6b7169203331'; carrier[X_B3_SAMPLED] = '1'; const extractedSpanContext = getExtractedSpanContext( - b3Format.extract(Context.ROOT_CONTEXT, carrier) + b3Format.extract(Context.ROOT_CONTEXT, carrier, defaultGetter) ); assert.deepStrictEqual(extractedSpanContext, { @@ -147,7 +156,7 @@ describe('B3Format', () => { carrier[X_B3_SPAN_ID] = 'b7ad6b7169203331'; carrier[X_B3_SAMPLED] = true; const extractedSpanContext = getExtractedSpanContext( - b3Format.extract(Context.ROOT_CONTEXT, carrier) + b3Format.extract(Context.ROOT_CONTEXT, carrier, defaultGetter) ); assert.deepStrictEqual(extractedSpanContext, { @@ -163,7 +172,7 @@ describe('B3Format', () => { carrier[X_B3_SPAN_ID] = 'b7ad6b7169203331'; carrier[X_B3_SAMPLED] = false; const extractedSpanContext = getExtractedSpanContext( - b3Format.extract(Context.ROOT_CONTEXT, carrier) + b3Format.extract(Context.ROOT_CONTEXT, carrier, defaultGetter) ); assert.deepStrictEqual(extractedSpanContext, { @@ -179,7 +188,7 @@ describe('B3Format', () => { carrier[X_B3_SPAN_ID] = undefined; assert.deepStrictEqual( getExtractedSpanContext( - b3Format.extract(Context.ROOT_CONTEXT, carrier) + b3Format.extract(Context.ROOT_CONTEXT, carrier, defaultGetter) ), undefined ); @@ -190,7 +199,7 @@ describe('B3Format', () => { carrier[X_B3_SPAN_ID] = undefined; assert.deepStrictEqual( getExtractedSpanContext( - b3Format.extract(Context.ROOT_CONTEXT, carrier) + b3Format.extract(Context.ROOT_CONTEXT, carrier, defaultGetter) ), undefined ); @@ -199,7 +208,7 @@ describe('B3Format', () => { it('returns undefined if b3 header is missing', () => { assert.deepStrictEqual( getExtractedSpanContext( - b3Format.extract(Context.ROOT_CONTEXT, carrier) + b3Format.extract(Context.ROOT_CONTEXT, carrier, defaultGetter) ), undefined ); @@ -209,7 +218,7 @@ describe('B3Format', () => { carrier[X_B3_TRACE_ID] = 'invalid!'; assert.deepStrictEqual( getExtractedSpanContext( - b3Format.extract(Context.ROOT_CONTEXT, carrier) + b3Format.extract(Context.ROOT_CONTEXT, carrier, defaultGetter) ), undefined ); @@ -220,7 +229,7 @@ describe('B3Format', () => { carrier[X_B3_SPAN_ID] = 'b7ad6b7169203331'; carrier[X_B3_SAMPLED] = '01'; const extractedSpanContext = getExtractedSpanContext( - b3Format.extract(Context.ROOT_CONTEXT, carrier) + b3Format.extract(Context.ROOT_CONTEXT, carrier, defaultGetter) ); assert.deepStrictEqual(extractedSpanContext, { spanId: 'b7ad6b7169203331', @@ -271,7 +280,7 @@ describe('B3Format', () => { carrier[X_B3_TRACE_ID] = testCases[testCase]; const extractedSpanContext = getExtractedSpanContext( - b3Format.extract(Context.ROOT_CONTEXT, carrier) + b3Format.extract(Context.ROOT_CONTEXT, carrier, defaultGetter) ); assert.deepStrictEqual(extractedSpanContext, undefined, testCase); }); diff --git a/packages/opentelemetry-core/test/context/HttpTraceContext.test.ts b/packages/opentelemetry-core/test/context/HttpTraceContext.test.ts index bc74b8d682..778ea9029c 100644 --- a/packages/opentelemetry-core/test/context/HttpTraceContext.test.ts +++ b/packages/opentelemetry-core/test/context/HttpTraceContext.test.ts @@ -14,7 +14,12 @@ * limitations under the License. */ -import { SpanContext, TraceFlags } from '@opentelemetry/api'; +import { + defaultGetter, + defaultSetter, + SpanContext, + TraceFlags, +} from '@opentelemetry/api'; import { Context } from '@opentelemetry/scope-base'; import * as assert from 'assert'; import { @@ -46,7 +51,8 @@ describe('HttpTraceContext', () => { httpTraceContext.inject( setExtractedSpanContext(Context.ROOT_CONTEXT, spanContext), - carrier + carrier, + defaultSetter ); assert.deepStrictEqual( carrier[TRACE_PARENT_HEADER], @@ -65,7 +71,8 @@ describe('HttpTraceContext', () => { httpTraceContext.inject( setExtractedSpanContext(Context.ROOT_CONTEXT, spanContext), - carrier + carrier, + defaultSetter ); assert.deepStrictEqual( carrier[TRACE_PARENT_HEADER], @@ -80,7 +87,7 @@ describe('HttpTraceContext', () => { carrier[TRACE_PARENT_HEADER] = '00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01'; const extractedSpanContext = getExtractedSpanContext( - httpTraceContext.extract(Context.ROOT_CONTEXT, carrier) + httpTraceContext.extract(Context.ROOT_CONTEXT, carrier, defaultGetter) ); assert.deepStrictEqual(extractedSpanContext, { @@ -94,7 +101,7 @@ describe('HttpTraceContext', () => { it('returns null if traceparent header is missing', () => { assert.deepStrictEqual( getExtractedSpanContext( - httpTraceContext.extract(Context.ROOT_CONTEXT, carrier) + httpTraceContext.extract(Context.ROOT_CONTEXT, carrier, defaultGetter) ), undefined ); @@ -104,7 +111,7 @@ describe('HttpTraceContext', () => { carrier[TRACE_PARENT_HEADER] = 'invalid!'; assert.deepStrictEqual( getExtractedSpanContext( - httpTraceContext.extract(Context.ROOT_CONTEXT, carrier) + httpTraceContext.extract(Context.ROOT_CONTEXT, carrier, defaultGetter) ), undefined ); @@ -115,7 +122,7 @@ describe('HttpTraceContext', () => { '00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01', ]; const extractedSpanContext = getExtractedSpanContext( - httpTraceContext.extract(Context.ROOT_CONTEXT, carrier) + httpTraceContext.extract(Context.ROOT_CONTEXT, carrier, defaultGetter) ); assert.deepStrictEqual(extractedSpanContext, { spanId: 'b7ad6b7169203331', @@ -130,7 +137,7 @@ describe('HttpTraceContext', () => { '00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01'; carrier[TRACE_STATE_HEADER] = 'foo=bar,baz=qux'; const extractedSpanContext = getExtractedSpanContext( - httpTraceContext.extract(Context.ROOT_CONTEXT, carrier) + httpTraceContext.extract(Context.ROOT_CONTEXT, carrier, defaultGetter) ); assert.deepStrictEqual( @@ -148,7 +155,7 @@ describe('HttpTraceContext', () => { '00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01'; carrier[TRACE_STATE_HEADER] = ['foo=bar,baz=qux', 'quux=quuz']; const extractedSpanContext = getExtractedSpanContext( - httpTraceContext.extract(Context.ROOT_CONTEXT, carrier) + httpTraceContext.extract(Context.ROOT_CONTEXT, carrier, defaultGetter) ); assert.deepStrictEqual(extractedSpanContext, { spanId: 'b7ad6b7169203331', @@ -200,7 +207,7 @@ describe('HttpTraceContext', () => { carrier[TRACE_PARENT_HEADER] = testCases[testCase]; const extractedSpanContext = getExtractedSpanContext( - httpTraceContext.extract(Context.ROOT_CONTEXT, carrier) + httpTraceContext.extract(Context.ROOT_CONTEXT, carrier, defaultGetter) ); assert.deepStrictEqual(extractedSpanContext, undefined, testCase); }); diff --git a/packages/opentelemetry-core/test/context/composite.test.ts b/packages/opentelemetry-core/test/context/composite.test.ts index 55b4b8ea8f..396f566558 100644 --- a/packages/opentelemetry-core/test/context/composite.test.ts +++ b/packages/opentelemetry-core/test/context/composite.test.ts @@ -14,6 +14,12 @@ * limitations under the License. */ +import { + defaultGetter, + defaultSetter, + HttpTextFormat, + SpanContext, +} from '@opentelemetry/api'; import { Context } from '@opentelemetry/scope-base'; import * as assert from 'assert'; import { @@ -23,21 +29,20 @@ import { randomTraceId, } from '../../src'; import { - setExtractedSpanContext, getExtractedSpanContext, + setExtractedSpanContext, } from '../../src/context/context'; import { B3Format, + X_B3_SAMPLED, X_B3_SPAN_ID, X_B3_TRACE_ID, - X_B3_SAMPLED, } from '../../src/context/propagation/B3Format'; import { TRACE_PARENT_HEADER, TRACE_STATE_HEADER, } from '../../src/context/propagation/HttpTraceContext'; import { TraceState } from '../../src/trace/TraceState'; -import { HttpTextFormat, SpanContext } from '@opentelemetry/api'; describe('Composite Propagator', () => { let traceId: string; @@ -71,7 +76,7 @@ describe('Composite Propagator', () => { const composite = new CompositePropagator({ propagators: [new B3Format(), new HttpTraceContext()], }); - composite.inject(ctxWithSpanContext, carrier); + composite.inject(ctxWithSpanContext, carrier, defaultSetter); assert.strictEqual(carrier[X_B3_TRACE_ID], traceId); assert.strictEqual(carrier[X_B3_SPAN_ID], spanId); @@ -87,7 +92,7 @@ describe('Composite Propagator', () => { const composite = new CompositePropagator({ propagators: [new ThrowingPropagator(), new HttpTraceContext()], }); - composite.inject(ctxWithSpanContext, carrier); + composite.inject(ctxWithSpanContext, carrier, defaultSetter); assert.strictEqual( carrier[TRACE_PARENT_HEADER], @@ -114,7 +119,7 @@ describe('Composite Propagator', () => { propagators: [new B3Format(), new HttpTraceContext()], }); const spanContext = getExtractedSpanContext( - composite.extract(Context.ROOT_CONTEXT, carrier) + composite.extract(Context.ROOT_CONTEXT, carrier, defaultGetter) ); if (!spanContext) { @@ -133,7 +138,7 @@ describe('Composite Propagator', () => { propagators: [new ThrowingPropagator(), new HttpTraceContext()], }); const spanContext = getExtractedSpanContext( - composite.extract(Context.ROOT_CONTEXT, carrier) + composite.extract(Context.ROOT_CONTEXT, carrier, defaultGetter) ); if (!spanContext) { diff --git a/packages/opentelemetry-plugin-grpc/src/grpc.ts b/packages/opentelemetry-plugin-grpc/src/grpc.ts index 3e08a5a168..f53cd493ae 100644 --- a/packages/opentelemetry-plugin-grpc/src/grpc.ts +++ b/packages/opentelemetry-plugin-grpc/src/grpc.ts @@ -124,11 +124,9 @@ export class GrpcPlugin extends BasePlugin { } private _setSpanContext(metadata: grpcTypes.Metadata): void { - const carrier = {}; - propagation.inject(carrier); - for (const [k, v] of Object.entries(carrier)) { - metadata.set(k, v as string); - } + propagation.inject(metadata, (metadata, k, v) => + metadata.set(k, v as grpcTypes.MetadataValue) + ); } private _patchServer() { @@ -170,40 +168,45 @@ export class GrpcPlugin extends BasePlugin { JSON.stringify(spanOptions) ); - context.with(propagation.extract(call.metadata.getMap()), () => { - const span = plugin._tracer - .startSpan(spanName, spanOptions) - .setAttributes({ - [AttributeNames.GRPC_KIND]: spanOptions.kind, - [AttributeNames.COMPONENT]: GrpcPlugin.component, + context.with( + propagation.extract(call.metadata, (carrier, key) => + carrier.get(key) + ), + () => { + const span = plugin._tracer + .startSpan(spanName, spanOptions) + .setAttributes({ + [AttributeNames.GRPC_KIND]: spanOptions.kind, + [AttributeNames.COMPONENT]: GrpcPlugin.component, + }); + + plugin._tracer.withSpan(span, () => { + switch (type) { + case 'unary': + case 'client_stream': + return plugin._clientStreamAndUnaryHandler( + plugin, + span, + call, + callback, + originalFunc, + self + ); + case 'server_stream': + case 'bidi': + return plugin._serverStreamAndBidiHandler( + plugin, + span, + call, + originalFunc, + self + ); + default: + break; + } }); - - plugin._tracer.withSpan(span, () => { - switch (type) { - case 'unary': - case 'client_stream': - return plugin._clientStreamAndUnaryHandler( - plugin, - span, - call, - callback, - originalFunc, - self - ); - case 'server_stream': - case 'bidi': - return plugin._serverStreamAndBidiHandler( - plugin, - span, - call, - originalFunc, - self - ); - default: - break; - } - }); - }); + } + ); }; } ); diff --git a/packages/opentelemetry-propagator-jaeger/src/JaegerHttpTraceFormat.ts b/packages/opentelemetry-propagator-jaeger/src/JaegerHttpTraceFormat.ts index be166154c0..45a85b7f0f 100644 --- a/packages/opentelemetry-propagator-jaeger/src/JaegerHttpTraceFormat.ts +++ b/packages/opentelemetry-propagator-jaeger/src/JaegerHttpTraceFormat.ts @@ -15,11 +15,12 @@ */ import { - Carrier, Context, HttpTextFormat, SpanContext, TraceFlags, + SetterFunction, + GetterFunction, } from '@opentelemetry/api'; import { getParentSpanContext, @@ -53,7 +54,7 @@ export class JaegerHttpTraceFormat implements HttpTextFormat { this._jaegerTraceHeader = customTraceHeader || UBER_TRACE_ID_HEADER; } - inject(context: Context, carrier: Carrier) { + inject(context: Context, carrier: unknown, setter: SetterFunction) { const spanContext = getParentSpanContext(context); if (!spanContext) return; @@ -61,13 +62,15 @@ export class JaegerHttpTraceFormat implements HttpTextFormat { spanContext.traceFlags || TraceFlags.UNSAMPLED ).toString(16)}`; - carrier[ - this._jaegerTraceHeader - ] = `${spanContext.traceId}:${spanContext.spanId}:0:${traceFlags}`; + setter( + carrier, + this._jaegerTraceHeader, + `${spanContext.traceId}:${spanContext.spanId}:0:${traceFlags}` + ); } - extract(context: Context, carrier: Carrier): Context { - const uberTraceIdHeader = carrier[this._jaegerTraceHeader]; + extract(context: Context, carrier: unknown, getter: GetterFunction): Context { + const uberTraceIdHeader = getter(carrier, this._jaegerTraceHeader); if (!uberTraceIdHeader) return context; const uberTraceId = Array.isArray(uberTraceIdHeader) ? uberTraceIdHeader[0] diff --git a/packages/opentelemetry-propagator-jaeger/test/JaegerHttpTraceFormat.test.ts b/packages/opentelemetry-propagator-jaeger/test/JaegerHttpTraceFormat.test.ts index c8e5e1b406..fb56229ad4 100644 --- a/packages/opentelemetry-propagator-jaeger/test/JaegerHttpTraceFormat.test.ts +++ b/packages/opentelemetry-propagator-jaeger/test/JaegerHttpTraceFormat.test.ts @@ -14,7 +14,13 @@ * limitations under the License. */ -import { Context, SpanContext, TraceFlags } from '@opentelemetry/api'; +import { + Context, + defaultGetter, + defaultSetter, + SpanContext, + TraceFlags, +} from '@opentelemetry/api'; import { getExtractedSpanContext, setExtractedSpanContext, @@ -45,7 +51,8 @@ describe('JaegerHttpTraceFormat', () => { jaegerHttpTraceFormat.inject( setExtractedSpanContext(Context.ROOT_CONTEXT, spanContext), - carrier + carrier, + defaultSetter ); assert.deepStrictEqual( carrier[UBER_TRACE_ID_HEADER], @@ -62,7 +69,8 @@ describe('JaegerHttpTraceFormat', () => { customJaegerHttpTraceFormat.inject( setExtractedSpanContext(Context.ROOT_CONTEXT, spanContext), - carrier + carrier, + defaultSetter ); assert.deepStrictEqual( carrier[customHeader], @@ -76,7 +84,11 @@ describe('JaegerHttpTraceFormat', () => { carrier[UBER_TRACE_ID_HEADER] = 'd4cda95b652f4a1592b449d5929fda1b:6e0c63257de34c92:0:01'; const extractedSpanContext = getExtractedSpanContext( - jaegerHttpTraceFormat.extract(Context.ROOT_CONTEXT, carrier) + jaegerHttpTraceFormat.extract( + Context.ROOT_CONTEXT, + carrier, + defaultGetter + ) ); assert.deepStrictEqual(extractedSpanContext, { @@ -91,7 +103,11 @@ describe('JaegerHttpTraceFormat', () => { carrier[UBER_TRACE_ID_HEADER] = '9c41e35aeb6d1272:45fd2a9709dadcf1:a13699e3fb724f40:1'; const extractedSpanContext = getExtractedSpanContext( - jaegerHttpTraceFormat.extract(Context.ROOT_CONTEXT, carrier) + jaegerHttpTraceFormat.extract( + Context.ROOT_CONTEXT, + carrier, + defaultGetter + ) ); assert.deepStrictEqual(extractedSpanContext, { @@ -106,7 +122,11 @@ describe('JaegerHttpTraceFormat', () => { carrier[UBER_TRACE_ID_HEADER] = 'ac1f3dc3c2c0b06e%3A5ac292c4a11a163e%3Ac086aaa825821068%3A1'; const extractedSpanContext = getExtractedSpanContext( - jaegerHttpTraceFormat.extract(Context.ROOT_CONTEXT, carrier) + jaegerHttpTraceFormat.extract( + Context.ROOT_CONTEXT, + carrier, + defaultGetter + ) ); assert.deepStrictEqual(extractedSpanContext, { @@ -121,7 +141,11 @@ describe('JaegerHttpTraceFormat', () => { carrier[customHeader] = 'd4cda95b652f4a1592b449d5929fda1b:6e0c63257de34c92:0:01'; const extractedSpanContext = getExtractedSpanContext( - customJaegerHttpTraceFormat.extract(Context.ROOT_CONTEXT, carrier) + customJaegerHttpTraceFormat.extract( + Context.ROOT_CONTEXT, + carrier, + defaultGetter + ) ); assert.deepStrictEqual(extractedSpanContext, { @@ -135,7 +159,11 @@ describe('JaegerHttpTraceFormat', () => { it('returns undefined if UBER_TRACE_ID_HEADER header is missing', () => { assert.deepStrictEqual( getExtractedSpanContext( - jaegerHttpTraceFormat.extract(Context.ROOT_CONTEXT, carrier) + jaegerHttpTraceFormat.extract( + Context.ROOT_CONTEXT, + carrier, + defaultGetter + ) ), undefined ); @@ -145,7 +173,11 @@ describe('JaegerHttpTraceFormat', () => { carrier[UBER_TRACE_ID_HEADER] = 'invalid!'; assert.deepStrictEqual( getExtractedSpanContext( - jaegerHttpTraceFormat.extract(Context.ROOT_CONTEXT, carrier) + jaegerHttpTraceFormat.extract( + Context.ROOT_CONTEXT, + carrier, + defaultGetter + ) ), undefined ); diff --git a/packages/opentelemetry-shim-opentracing/src/shim.ts b/packages/opentelemetry-shim-opentracing/src/shim.ts index a919393486..c854f4f5b0 100644 --- a/packages/opentelemetry-shim-opentracing/src/shim.ts +++ b/packages/opentelemetry-shim-opentracing/src/shim.ts @@ -22,6 +22,7 @@ import { setActiveSpan, } from '@opentelemetry/core'; import * as opentracing from 'opentracing'; +import { defaultSetter } from '@opentelemetry/api'; function translateReferences(references: opentracing.Reference[]): api.Link[] { const links: api.Link[] = []; @@ -134,7 +135,7 @@ export class TracerShim extends opentracing.Tracer { _inject( spanContext: opentracing.SpanContext, format: string, - carrier: api.Carrier + carrier: unknown ): void { const opentelemSpanContext: api.SpanContext = (spanContext as SpanContextShim).getSpanContext(); if (!carrier || typeof carrier !== 'object') return; @@ -144,6 +145,7 @@ export class TracerShim extends opentracing.Tracer { case opentracing.FORMAT_TEXT_MAP: api.propagation.inject( carrier, + defaultSetter, setExtractedSpanContext( api.Context.ROOT_CONTEXT, opentelemSpanContext @@ -160,10 +162,7 @@ export class TracerShim extends opentracing.Tracer { } } - _extract( - format: string, - carrier: api.Carrier - ): opentracing.SpanContext | null { + _extract(format: string, carrier: unknown): opentracing.SpanContext | null { switch (format) { // tslint:disable-next-line:no-switch-case-fall-through case opentracing.FORMAT_HTTP_HEADERS: