Skip to content

Commit 5c4c57e

Browse files
authored
Add getter and setter arguments to propagation API (open-telemetry#827)
* feat: add getter and setter arguments to propagation API * chore: add getter and setter to composite propagator
1 parent d16c691 commit 5c4c57e

17 files changed

Lines changed: 259 additions & 132 deletions

File tree

packages/opentelemetry-api/src/api/propagation.ts

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,10 @@
1515
*/
1616

1717
import { Context } from '@opentelemetry/scope-base';
18-
import { Carrier } from '../context/propagation/carrier';
18+
import { defaultGetter, GetterFunction } from '../context/propagation/getter';
1919
import { HttpTextFormat } from '../context/propagation/HttpTextFormat';
2020
import { NOOP_HTTP_TEXT_FORMAT } from '../context/propagation/NoopHttpTextFormat';
21+
import { defaultSetter, SetterFunction } from '../context/propagation/setter';
2122
import { ContextAPI } from './context';
2223

2324
const contextApi = ContextAPI.getInstance();
@@ -53,19 +54,29 @@ export class PropagationAPI {
5354
* Inject context into a carrier to be propagated inter-process
5455
*
5556
* @param carrier carrier to inject context into
57+
* @param setter Function used to set values on the carrier
5658
* @param context Context carrying tracing data to inject. Defaults to the currently active context.
5759
*/
58-
public inject(carrier: Carrier, context = contextApi.active()): void {
59-
return this._propagator.inject(context, carrier);
60+
public inject<Carrier>(
61+
carrier: Carrier,
62+
setter: SetterFunction<Carrier> = defaultSetter,
63+
context = contextApi.active()
64+
): void {
65+
return this._propagator.inject(context, carrier, setter);
6066
}
6167

6268
/**
6369
* Extract context from a carrier
6470
*
6571
* @param carrier Carrier to extract context from
72+
* @param getter Function used to extract keys from a carrier
6673
* @param context Context which the newly created context will inherit from. Defaults to the currently active context.
6774
*/
68-
public extract(carrier: Carrier, context = contextApi.active()): Context {
69-
return this._propagator.extract(context, carrier);
75+
public extract<Carrier>(
76+
carrier: Carrier,
77+
getter: GetterFunction<Carrier> = defaultGetter,
78+
context = contextApi.active()
79+
): Context {
80+
return this._propagator.extract(context, carrier, getter);
7081
}
7182
}

packages/opentelemetry-api/src/context/propagation/HttpTextFormat.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515
*/
1616

1717
import { Context } from '@opentelemetry/scope-base';
18-
import { Carrier } from './carrier';
18+
import { SetterFunction } from './setter';
19+
import { GetterFunction } from './getter';
1920

2021
/**
2122
* Injects {@link Context} into and extracts it from carriers that travel
@@ -37,8 +38,10 @@ export interface HttpTextFormat {
3738
* the wire.
3839
* @param carrier the carrier of propagation fields, such as http request
3940
* headers.
41+
* @param setter a function which accepts a carrier, key, and value, which
42+
* sets the key on the carrier to the value.
4043
*/
41-
inject(context: Context, carrier: Carrier): void;
44+
inject(context: Context, carrier: unknown, setter: SetterFunction): void;
4245

4346
/**
4447
* Given a {@link Context} and a carrier, extract context values from a
@@ -49,6 +52,8 @@ export interface HttpTextFormat {
4952
* the wire.
5053
* @param carrier the carrier of propagation fields, such as http request
5154
* headers.
55+
* @param getter a function which accepts a carrier and a key, and returns
56+
* the value from the carrier identified by the key.
5257
*/
53-
extract(context: Context, carrier: Carrier): Context;
58+
extract(context: Context, carrier: unknown, getter: GetterFunction): Context;
5459
}

packages/opentelemetry-api/src/context/propagation/NoopHttpTextFormat.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,16 @@
1515
*/
1616

1717
import { Context } from '@opentelemetry/scope-base';
18-
import { Carrier } from './carrier';
1918
import { HttpTextFormat } from './HttpTextFormat';
2019

2120
/**
2221
* No-op implementations of {@link HttpTextFormat}.
2322
*/
2423
export class NoopHttpTextFormat implements HttpTextFormat {
2524
/** Noop inject function does nothing */
26-
inject(context: Context, carrier: Carrier): void {}
25+
inject(context: Context, carrier: unknown, setter: Function): void {}
2726
/** Noop extract function does nothing and returns the input context */
28-
extract(context: Context, carrier: Carrier): Context {
27+
extract(context: Context, carrier: unknown, getter: Function): Context {
2928
return context;
3029
}
3130
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*!
2+
* Copyright 2020, OpenTelemetry Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
export type GetterFunction<Carrier = any> = (
18+
carrier: Carrier,
19+
key: string
20+
) => unknown;
21+
22+
/**
23+
* Default getter which just does a simple property access. Returns
24+
* undefined if the key is not set.
25+
*
26+
* @param carrier
27+
* @param key
28+
*/
29+
export function defaultGetter(carrier: any, key: string): unknown {
30+
return carrier[key];
31+
}

packages/opentelemetry-api/src/context/propagation/carrier.ts renamed to packages/opentelemetry-api/src/context/propagation/setter.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,18 @@
1414
* limitations under the License.
1515
*/
1616

17-
export type Carrier = {
18-
[key: string]: unknown;
19-
};
17+
export type SetterFunction<Carrier = any> = (
18+
carrier: Carrier,
19+
key: string,
20+
value: unknown
21+
) => void;
22+
23+
/**
24+
* Default setter which sets value via direct property access
25+
*
26+
* @param carrier
27+
* @param key
28+
*/
29+
export function defaultSetter(carrier: any, key: string, value: unknown) {
30+
carrier[key] = value;
31+
}

packages/opentelemetry-api/src/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@
1616

1717
export * from './common/Logger';
1818
export * from './common/Time';
19-
export * from './context/propagation/carrier';
19+
export * from './context/propagation/getter';
2020
export * from './context/propagation/HttpTextFormat';
21+
export * from './context/propagation/setter';
2122
export * from './correlation_context/CorrelationContext';
2223
export * from './correlation_context/EntryValue';
2324
export * from './metrics/BoundInstrument';

packages/opentelemetry-api/test/noop-implementations/noop-tracer.test.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,11 @@
1414
* limitations under the License.
1515
*/
1616

17+
import { Context } from '@opentelemetry/scope-base';
1718
import * as assert from 'assert';
1819
import { NoopTracer, NOOP_SPAN, SpanKind } from '../../src';
19-
import { Context } from '@opentelemetry/scope-base';
20+
import { defaultGetter } from '../../src/context/propagation/getter';
21+
import { defaultSetter } from '../../src/context/propagation/setter';
2022

2123
describe('NoopTracer', () => {
2224
it('should not crash', () => {
@@ -38,9 +40,9 @@ describe('NoopTracer', () => {
3840
const httpTextFormat = tracer.getHttpTextFormat();
3941
assert.ok(httpTextFormat);
4042

41-
httpTextFormat.inject(Context.ROOT_CONTEXT, {});
43+
httpTextFormat.inject(Context.ROOT_CONTEXT, {}, defaultSetter);
4244
assert.deepStrictEqual(
43-
httpTextFormat.extract(Context.ROOT_CONTEXT, {}),
45+
httpTextFormat.extract(Context.ROOT_CONTEXT, {}, defaultGetter),
4446
Context.ROOT_CONTEXT
4547
);
4648
});

packages/opentelemetry-core/src/context/propagation/B3Format.ts

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,10 @@
1515
*/
1616

1717
import {
18-
Carrier,
1918
Context,
19+
GetterFunction,
2020
HttpTextFormat,
21+
SetterFunction,
2122
TraceFlags,
2223
} from '@opentelemetry/api';
2324
import { getParentSpanContext, setExtractedSpanContext } from '../context';
@@ -42,29 +43,29 @@ function isValidSpanId(spanId: string): boolean {
4243
* Based on: https://github.com/openzipkin/b3-propagation
4344
*/
4445
export class B3Format implements HttpTextFormat {
45-
inject(context: Context, carrier: Carrier) {
46+
inject(context: Context, carrier: unknown, setter: SetterFunction) {
4647
const spanContext = getParentSpanContext(context);
4748
if (!spanContext) return;
4849

4950
if (
5051
isValidTraceId(spanContext.traceId) &&
5152
isValidSpanId(spanContext.spanId)
5253
) {
53-
carrier[X_B3_TRACE_ID] = spanContext.traceId;
54-
carrier[X_B3_SPAN_ID] = spanContext.spanId;
54+
setter(carrier, X_B3_TRACE_ID, spanContext.traceId);
55+
setter(carrier, X_B3_SPAN_ID, spanContext.spanId);
5556

5657
// We set the header only if there is an existing sampling decision.
5758
// Otherwise we will omit it => Absent.
5859
if (spanContext.traceFlags !== undefined) {
59-
carrier[X_B3_SAMPLED] = Number(spanContext.traceFlags);
60+
setter(carrier, X_B3_SAMPLED, Number(spanContext.traceFlags));
6061
}
6162
}
6263
}
6364

64-
extract(context: Context, carrier: Carrier): Context {
65-
const traceIdHeader = carrier[X_B3_TRACE_ID];
66-
const spanIdHeader = carrier[X_B3_SPAN_ID];
67-
const sampledHeader = carrier[X_B3_SAMPLED];
65+
extract(context: Context, carrier: unknown, getter: GetterFunction): Context {
66+
const traceIdHeader = getter(carrier, X_B3_TRACE_ID);
67+
const spanIdHeader = getter(carrier, X_B3_SPAN_ID);
68+
const sampledHeader = getter(carrier, X_B3_SAMPLED);
6869
if (!traceIdHeader || !spanIdHeader) return context;
6970
const traceId = Array.isArray(traceIdHeader)
7071
? traceIdHeader[0]

packages/opentelemetry-core/src/context/propagation/HttpTraceContext.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,10 @@
1515
*/
1616

1717
import {
18-
Carrier,
1918
Context,
19+
GetterFunction,
2020
HttpTextFormat,
21+
SetterFunction,
2122
SpanContext,
2223
TraceFlags,
2324
} from '@opentelemetry/api';
@@ -63,22 +64,22 @@ export function parseTraceParent(traceParent: string): SpanContext | null {
6364
* https://www.w3.org/TR/trace-context/
6465
*/
6566
export class HttpTraceContext implements HttpTextFormat {
66-
inject(context: Context, carrier: Carrier) {
67+
inject(context: Context, carrier: unknown, setter: SetterFunction) {
6768
const spanContext = getParentSpanContext(context);
6869
if (!spanContext) return;
6970

7071
const traceParent = `${VERSION}-${spanContext.traceId}-${
7172
spanContext.spanId
7273
}-0${Number(spanContext.traceFlags || TraceFlags.UNSAMPLED).toString(16)}`;
7374

74-
carrier[TRACE_PARENT_HEADER] = traceParent;
75+
setter(carrier, TRACE_PARENT_HEADER, traceParent);
7576
if (spanContext.traceState) {
76-
carrier[TRACE_STATE_HEADER] = spanContext.traceState.serialize();
77+
setter(carrier, TRACE_STATE_HEADER, spanContext.traceState.serialize());
7778
}
7879
}
7980

80-
extract(context: Context, carrier: Carrier): Context {
81-
const traceParentHeader = carrier[TRACE_PARENT_HEADER];
81+
extract(context: Context, carrier: unknown, getter: GetterFunction): Context {
82+
const traceParentHeader = getter(carrier, TRACE_PARENT_HEADER);
8283
if (!traceParentHeader) return context;
8384
const traceParent = Array.isArray(traceParentHeader)
8485
? traceParentHeader[0]
@@ -88,7 +89,7 @@ export class HttpTraceContext implements HttpTextFormat {
8889

8990
spanContext.isRemote = true;
9091

91-
const traceStateHeader = carrier[TRACE_STATE_HEADER];
92+
const traceStateHeader = getter(carrier, TRACE_STATE_HEADER);
9293
if (traceStateHeader) {
9394
// If more than one `tracestate` header is found, we merge them into a
9495
// single header.

packages/opentelemetry-core/src/context/propagation/composite.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,13 @@
1414
* limitations under the License.
1515
*/
1616

17-
import { Carrier, Context, HttpTextFormat, Logger } from '@opentelemetry/api';
17+
import {
18+
Context,
19+
GetterFunction,
20+
HttpTextFormat,
21+
Logger,
22+
SetterFunction,
23+
} from '@opentelemetry/api';
1824
import { NoopLogger } from '../../common/NoopLogger';
1925
import { CompositePropagatorConfig } from './types';
2026

@@ -42,10 +48,10 @@ export class CompositePropagator implements HttpTextFormat {
4248
* @param context Context to inject
4349
* @param carrier Carrier into which context will be injected
4450
*/
45-
inject(context: Context, carrier: Carrier) {
51+
inject(context: Context, carrier: unknown, setter: SetterFunction) {
4652
for (const propagator of this._propagators) {
4753
try {
48-
propagator.inject(context, carrier);
54+
propagator.inject(context, carrier, setter);
4955
} catch (err) {
5056
this._logger.warn(
5157
`Failed to inject with ${propagator.constructor.name}. Err: ${err.message}`
@@ -63,10 +69,10 @@ export class CompositePropagator implements HttpTextFormat {
6369
* @param context Context to add values to
6470
* @param carrier Carrier from which to extract context
6571
*/
66-
extract(context: Context, carrier: Carrier): Context {
72+
extract(context: Context, carrier: unknown, getter: GetterFunction): Context {
6773
return this._propagators.reduce((ctx, propagator) => {
6874
try {
69-
return propagator.extract(ctx, carrier);
75+
return propagator.extract(ctx, carrier, getter);
7076
} catch (err) {
7177
this._logger.warn(
7278
`Failed to inject with ${propagator.constructor.name}. Err: ${err.message}`

0 commit comments

Comments
 (0)