Skip to content

Commit 92b41bc

Browse files
authored
Separate context propagation (OTEP 66) (open-telemetry#769)
* feat: separate context propagation
1 parent d35ac4f commit 92b41bc

File tree

35 files changed

+664
-306
lines changed

35 files changed

+664
-306
lines changed

.circleci/config.yml

+11-6
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ mysql_service: &mysql_service
4040
MYSQL_ROOT_PASSWORD: rootpw
4141

4242
cache_1: &cache_1
43-
key: npm-cache-01-{{ .Environment.CIRCLE_JOB }}-{{ checksum "/tmp/checksums.txt" }}-FDF2088C
43+
key: npm-cache-01-{{ .Environment.CIRCLE_JOB }}-{{ checksum "/tmp/checksums.txt" }}-F267A71D
4444
paths:
4545
- ./node_modules
4646
- ./package-lock.json
@@ -60,7 +60,7 @@ cache_1: &cache_1
6060
- packages/opentelemetry-plugin-dns/node_modules
6161

6262
cache_2: &cache_2
63-
key: npm-cache-02-{{ .Environment.CIRCLE_JOB }}-{{ checksum "/tmp/checksums.txt" }}-FDF2088C
63+
key: npm-cache-02-{{ .Environment.CIRCLE_JOB }}-{{ checksum "/tmp/checksums.txt" }}-F267A71D
6464
paths:
6565
- packages/opentelemetry-plugin-grpc/node_modules
6666
- packages/opentelemetry-plugin-http/node_modules
@@ -95,10 +95,10 @@ node_unit_tests: &node_unit_tests
9595
echo "CIRCLE_NODE_VERSION=${CIRCLE_NODE_VERSION}"
9696
- restore_cache:
9797
keys:
98-
- npm-cache-01-{{ .Environment.CIRCLE_JOB }}-{{ checksum "/tmp/checksums.txt" }}-FDF2088C
98+
- npm-cache-01-{{ .Environment.CIRCLE_JOB }}-{{ checksum "/tmp/checksums.txt" }}-F267A71D
9999
- restore_cache:
100100
keys:
101-
- npm-cache-02-{{ .Environment.CIRCLE_JOB }}-{{ checksum "/tmp/checksums.txt" }}-FDF2088C
101+
- npm-cache-02-{{ .Environment.CIRCLE_JOB }}-{{ checksum "/tmp/checksums.txt" }}-F267A71D
102102
- run:
103103
name: Install Root Dependencies
104104
command: npm install --ignore-scripts
@@ -135,10 +135,10 @@ browsers_unit_tests: &browsers_unit_tests
135135
echo "CIRCLE_NODE_VERSION=${CIRCLE_NODE_VERSION}"
136136
- restore_cache:
137137
keys:
138-
- npm-cache-01-{{ .Environment.CIRCLE_JOB }}-{{ checksum "/tmp/checksums.txt" }}-FDF2088C
138+
- npm-cache-01-{{ .Environment.CIRCLE_JOB }}-{{ checksum "/tmp/checksums.txt" }}-F267A71D
139139
- restore_cache:
140140
keys:
141-
- npm-cache-02-{{ .Environment.CIRCLE_JOB }}-{{ checksum "/tmp/checksums.txt" }}-FDF2088C
141+
- npm-cache-02-{{ .Environment.CIRCLE_JOB }}-{{ checksum "/tmp/checksums.txt" }}-F267A71D
142142
- run:
143143
name: Install Root Dependencies
144144
command: npm install --ignore-scripts
@@ -160,6 +160,8 @@ jobs:
160160
lint_&_docs:
161161
docker:
162162
- image: node:12
163+
environment:
164+
NPM_CONFIG_UNSAFE_PERM: true
163165
steps:
164166
- checkout
165167
- run:
@@ -171,6 +173,9 @@ jobs:
171173
- run:
172174
name: Check code style and linting
173175
command: npm run check
176+
- run:
177+
name: Install API dependencies
178+
command: lerna bootstrap --scope @opentelemetry/api --include-filtered-dependencies
174179
- run:
175180
name: Docs tests
176181
command: npm run docs-test

packages/opentelemetry-api/package.json

+3
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@
4545
"publishConfig": {
4646
"access": "public"
4747
},
48+
"dependencies": {
49+
"@opentelemetry/scope-base": "^0.4.0"
50+
},
4851
"devDependencies": {
4952
"@types/mocha": "^5.2.7",
5053
"@types/node": "^12.6.8",

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

+11-16
Original file line numberDiff line numberDiff line change
@@ -14,40 +14,35 @@
1414
* limitations under the License.
1515
*/
1616

17-
import { SpanContext } from '../../trace/span_context';
17+
import { Context } from '@opentelemetry/scope-base';
1818
import { Carrier } from './carrier';
1919

2020
/**
21-
* Injects and extracts a value as text into carriers that travel in-band
22-
* across process boundaries. Encoding is expected to conform to the HTTP
21+
* Injects {@link Context} into and extracts it from carriers that travel
22+
* in-band across process boundaries. Encoding is expected to conform to the HTTP
2323
* Header Field semantics. Values are often encoded as RPC/HTTP request headers.
2424
*
2525
* The carrier of propagated data on both the client (injector) and server
26-
* (extractor) side is usually an http request. Propagation is usually
27-
* implemented via library- specific request interceptors, where the
28-
* client-side injects values and the server-side extracts them.
26+
* (extractor) side is usually an object such as http headers.
2927
*/
3028
export interface HttpTextFormat {
3129
/**
32-
* Injects the given {@link SpanContext} instance to transmit over the wire.
30+
* Injects values from a given {@link Context} into a carrier.
3331
*
3432
* OpenTelemetry defines a common set of format values (BinaryFormat and
3533
* HTTPTextFormat), and each has an expected `carrier` type.
3634
*
37-
* @param spanContext the SpanContext to transmit over the wire.
38-
* @param format the format of the carrier.
35+
* @param context the Context from which to extract values to transmit over the wire.
3936
* @param carrier the carrier of propagation fields, such as http request headers.
4037
*/
41-
inject(spanContext: SpanContext, format: string, carrier: Carrier): void;
38+
inject(context: Context, carrier: Carrier): void;
4239

4340
/**
44-
* Returns a {@link SpanContext} instance extracted from `carrier` in the
45-
* given format from upstream.
41+
* Given a {@link Context} and a carrier, extract context values from a carrier and
42+
* return a new context, created from the old context, with the extracted values.
4643
*
47-
* @param format the format of the carrier.
44+
* @param context the Context from which to extract values to transmit over the wire.
4845
* @param carrier the carrier of propagation fields, such as http request headers.
49-
* @returns SpanContext The extracted SpanContext, or null if no such
50-
* SpanContext could be found in carrier.
5146
*/
52-
extract(format: string, carrier: Carrier): SpanContext | null;
47+
extract(context: Context, carrier: Carrier): Context;
5348
}

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

+6-6
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,19 @@
1414
* limitations under the License.
1515
*/
1616

17-
import { SpanContext } from '../../trace/span_context';
17+
import { Context } from '@opentelemetry/scope-base';
1818
import { Carrier } from './carrier';
1919
import { HttpTextFormat } from './HttpTextFormat';
2020

2121
/**
2222
* No-op implementations of {@link HttpTextFormat}.
2323
*/
2424
export class NoopHttpTextFormat implements HttpTextFormat {
25-
// By default does nothing
26-
inject(spanContext: SpanContext, format: string, carrier: Carrier): void {}
27-
// By default does nothing
28-
extract(format: string, carrier: Carrier): SpanContext | null {
29-
return null;
25+
/** Noop inject function does nothing */
26+
inject(context: Context, carrier: Carrier): void {}
27+
/** Noop extract function does nothing and returns the input context */
28+
extract(context: Context, carrier: Carrier): Context {
29+
return context;
3030
}
3131
}
3232

packages/opentelemetry-api/src/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ export * from './trace/NoopTracerProvider';
4646
export * from './metrics/NoopMeterProvider';
4747
export * from './metrics/NoopMeter';
4848

49+
export { Context } from '@opentelemetry/scope-base';
50+
4951
import { TraceAPI } from './api/trace';
5052
/** Entrypoint for trace API */
5153
export const trace = TraceAPI.getInstance();

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

+7-2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
import * as assert from 'assert';
1818
import { NoopTracer, NOOP_SPAN, SpanKind } from '../../src';
19+
import { Context } from '@opentelemetry/scope-base';
1920

2021
describe('NoopTracer', () => {
2122
it('should not crash', () => {
@@ -38,8 +39,12 @@ describe('NoopTracer', () => {
3839
assert.deepStrictEqual(tracer.getCurrentSpan(), NOOP_SPAN);
3940
const httpTextFormat = tracer.getHttpTextFormat();
4041
assert.ok(httpTextFormat);
41-
httpTextFormat.inject(spanContext, 'HttpTextFormat', {});
42-
assert.deepStrictEqual(httpTextFormat.extract('HttpTextFormat', {}), null);
42+
43+
httpTextFormat.inject(Context.ROOT_CONTEXT, {});
44+
assert.deepStrictEqual(
45+
httpTextFormat.extract(Context.ROOT_CONTEXT, {}),
46+
Context.ROOT_CONTEXT
47+
);
4348

4449
const binaryFormat = tracer.getBinaryFormat();
4550
assert.ok(binaryFormat);

packages/opentelemetry-core/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@
7979
},
8080
"dependencies": {
8181
"@opentelemetry/api": "^0.4.0",
82+
"@opentelemetry/scope-base": "^0.4.0",
8283
"semver": "^6.3.0"
8384
}
8485
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
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+
import { Span, SpanContext } from '@opentelemetry/api';
18+
import { Context } from '@opentelemetry/scope-base';
19+
20+
const ACTIVE_SPAN_KEY = Context.createKey(
21+
'OpenTelemetry Context Key ACTIVE_SPAN'
22+
);
23+
const EXTRACTED_SPAN_CONTEXT_KEY = Context.createKey(
24+
'OpenTelemetry Context Key EXTRACTED_SPAN_CONTEXT'
25+
);
26+
27+
/**
28+
* Return the active span if one exists
29+
*
30+
* @param context context to get span from
31+
*/
32+
export function getActiveSpan(context: Context): Span | undefined {
33+
return (context.getValue(ACTIVE_SPAN_KEY) as Span) || undefined;
34+
}
35+
36+
/**
37+
* Set the active span on a context
38+
*
39+
* @param context context to use as parent
40+
* @param span span to set active
41+
*/
42+
export function setActiveSpan(context: Context, span: Span): Context {
43+
return context.setValue(ACTIVE_SPAN_KEY, span);
44+
}
45+
46+
/**
47+
* Get the extracted span context from a context
48+
*
49+
* @param context context to get span context from
50+
*/
51+
export function getExtractedSpanContext(
52+
context: Context
53+
): SpanContext | undefined {
54+
return (
55+
(context.getValue(EXTRACTED_SPAN_CONTEXT_KEY) as SpanContext) || undefined
56+
);
57+
}
58+
59+
/**
60+
* Set the extracted span context on a context
61+
*
62+
* @param context context to set span context on
63+
* @param spanContext span context to set
64+
*/
65+
export function setExtractedSpanContext(
66+
context: Context,
67+
spanContext: SpanContext
68+
): Context {
69+
return context.setValue(EXTRACTED_SPAN_CONTEXT_KEY, spanContext);
70+
}
71+
72+
/**
73+
* Get the span context of the parent span if it exists,
74+
* or the extracted span context if there is no active
75+
* span.
76+
*
77+
* @param context context to get values from
78+
*/
79+
export function getParentSpanContext(
80+
context: Context
81+
): SpanContext | undefined {
82+
return getActiveSpan(context)?.context() || getExtractedSpanContext(context);
83+
}

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

+11-7
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,11 @@
1616

1717
import {
1818
Carrier,
19+
Context,
1920
HttpTextFormat,
20-
SpanContext,
2121
TraceFlags,
2222
} from '@opentelemetry/api';
23+
import { getParentSpanContext, setExtractedSpanContext } from '../context';
2324

2425
export const X_B3_TRACE_ID = 'x-b3-traceid';
2526
export const X_B3_SPAN_ID = 'x-b3-spanid';
@@ -41,7 +42,10 @@ function isValidSpanId(spanId: string): boolean {
4142
* Based on: https://github.com/openzipkin/b3-propagation
4243
*/
4344
export class B3Format implements HttpTextFormat {
44-
inject(spanContext: SpanContext, format: string, carrier: Carrier): void {
45+
inject(context: Context, carrier: Carrier) {
46+
const spanContext = getParentSpanContext(context);
47+
if (!spanContext) return;
48+
4549
if (
4650
isValidTraceId(spanContext.traceId) &&
4751
isValidSpanId(spanContext.spanId)
@@ -57,11 +61,11 @@ export class B3Format implements HttpTextFormat {
5761
}
5862
}
5963

60-
extract(format: string, carrier: Carrier): SpanContext | null {
64+
extract(context: Context, carrier: Carrier): Context {
6165
const traceIdHeader = carrier[X_B3_TRACE_ID];
6266
const spanIdHeader = carrier[X_B3_SPAN_ID];
6367
const sampledHeader = carrier[X_B3_SAMPLED];
64-
if (!traceIdHeader || !spanIdHeader) return null;
68+
if (!traceIdHeader || !spanIdHeader) return context;
6569
const traceId = Array.isArray(traceIdHeader)
6670
? traceIdHeader[0]
6771
: traceIdHeader;
@@ -71,15 +75,15 @@ export class B3Format implements HttpTextFormat {
7175
: sampledHeader;
7276

7377
if (isValidTraceId(traceId) && isValidSpanId(spanId)) {
74-
return {
78+
return setExtractedSpanContext(context, {
7579
traceId,
7680
spanId,
7781
isRemote: true,
7882
traceFlags: isNaN(Number(options))
7983
? TraceFlags.UNSAMPLED
8084
: Number(options),
81-
};
85+
});
8286
}
83-
return null;
87+
return context;
8488
}
8589
}

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

+10-5
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,13 @@
1616

1717
import {
1818
Carrier,
19+
Context,
1920
HttpTextFormat,
2021
SpanContext,
2122
TraceFlags,
2223
} from '@opentelemetry/api';
2324
import { TraceState } from '../../trace/TraceState';
25+
import { getParentSpanContext, setExtractedSpanContext } from '../context';
2426

2527
export const TRACE_PARENT_HEADER = 'traceparent';
2628
export const TRACE_STATE_HEADER = 'tracestate';
@@ -61,7 +63,10 @@ export function parseTraceParent(traceParent: string): SpanContext | null {
6163
* https://www.w3.org/TR/trace-context/
6264
*/
6365
export class HttpTraceContext implements HttpTextFormat {
64-
inject(spanContext: SpanContext, format: string, carrier: Carrier) {
66+
inject(context: Context, carrier: Carrier) {
67+
const spanContext = getParentSpanContext(context);
68+
if (!spanContext) return;
69+
6570
const traceParent = `${VERSION}-${spanContext.traceId}-${
6671
spanContext.spanId
6772
}-0${Number(spanContext.traceFlags || TraceFlags.UNSAMPLED).toString(16)}`;
@@ -72,14 +77,14 @@ export class HttpTraceContext implements HttpTextFormat {
7277
}
7378
}
7479

75-
extract(format: string, carrier: Carrier): SpanContext | null {
80+
extract(context: Context, carrier: Carrier): Context {
7681
const traceParentHeader = carrier[TRACE_PARENT_HEADER];
77-
if (!traceParentHeader) return null;
82+
if (!traceParentHeader) return context;
7883
const traceParent = Array.isArray(traceParentHeader)
7984
? traceParentHeader[0]
8085
: traceParentHeader;
8186
const spanContext = parseTraceParent(traceParent);
82-
if (!spanContext) return null;
87+
if (!spanContext) return context;
8388

8489
spanContext.isRemote = true;
8590

@@ -92,6 +97,6 @@ export class HttpTraceContext implements HttpTextFormat {
9297
: traceStateHeader;
9398
spanContext.traceState = new TraceState(state as string);
9499
}
95-
return spanContext;
100+
return setExtractedSpanContext(context, spanContext);
96101
}
97102
}

packages/opentelemetry-core/src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export * from './common/NoopLogger';
1919
export * from './common/time';
2020
export * from './common/types';
2121
export * from './version';
22+
export * from './context/context';
2223
export * from './context/propagation/B3Format';
2324
export * from './context/propagation/BinaryTraceContext';
2425
export * from './context/propagation/HttpTraceContext';

0 commit comments

Comments
 (0)