From 90e23f05216d6b7dbebc88c61b20edb8c6483968 Mon Sep 17 00:00:00 2001 From: Daniel Dyla Date: Thu, 6 Feb 2020 10:09:25 -0500 Subject: [PATCH 01/23] feat: separate context propagation --- packages/opentelemetry-api/package.json | 3 + .../src/context/propagation/HttpTextFormat.ts | 32 +++---- .../context/propagation/NoopHttpTextFormat.ts | 13 +-- .../src/context/propagation/carrier.ts | 19 ++++ .../distributed_context/DistributedContext.ts | 29 ------ .../src/distributed_context/EntryValue.ts | 45 --------- packages/opentelemetry-api/src/index.ts | 21 ++-- .../src/metrics/BoundInstrument.ts | 9 +- .../opentelemetry-api/src/metrics/Metric.ts | 14 +-- .../src/metrics/NoopMeter.ts | 20 +--- .../noop-implementations/noop-tracer.test.ts | 6 +- packages/opentelemetry-core/package.json | 1 + .../opentelemetry-core/src/context/context.ts | 81 ++++++++++++++++ .../src/context/propagation/B3Format.ts | 25 +++-- .../context/propagation/HttpTraceContext.ts | 21 ++-- packages/opentelemetry-core/src/index.ts | 1 + .../test/context/B3Format.test.ts | 91 +++++++++++++----- .../test/context/HttpTraceContext.test.ts | 55 ++++++----- .../src/BoundInstrument.ts | 6 +- .../opentelemetry-plugin-http/src/http.ts | 16 +++- .../test/utils/DummyPropagation.ts | 17 ++-- .../test/utils/DummyPropagation.ts | 17 ++-- .../src/xhr.ts | 6 +- .../package.json | 3 +- .../src/JaegerHttpTraceFormat.ts | 34 +++---- .../test/JaegerHttpTraceFormat.test.ts | 47 ++++++--- .../src/AsyncHooksScopeManager.ts | 20 ++-- .../test/AsyncHooksScopeManager.test.ts | 55 ++++++----- .../src/NoopScopeManager.ts | 9 +- .../opentelemetry-scope-base/src/context.ts | 74 ++++++++++++++ .../opentelemetry-scope-base/src/index.ts | 1 + .../opentelemetry-scope-base/src/types.ts | 8 +- .../test/NoopScopeManager.test.ts | 20 ++-- .../src/ZoneScopeManager.ts | 41 ++++---- .../test/ZoneScopeManager.test.ts | 96 ++++++++++++------- .../src/shim.ts | 24 +++-- packages/opentelemetry-tracing/src/Tracer.ts | 22 +++-- .../test/BasicTracerRegistry.test.ts | 6 +- .../src/StackScopeManager.ts | 24 ++--- .../test/StackScopeManager.test.ts | 48 ++++++---- 40 files changed, 636 insertions(+), 444 deletions(-) create mode 100644 packages/opentelemetry-api/src/context/propagation/carrier.ts delete mode 100644 packages/opentelemetry-api/src/distributed_context/DistributedContext.ts delete mode 100644 packages/opentelemetry-api/src/distributed_context/EntryValue.ts create mode 100644 packages/opentelemetry-core/src/context/context.ts create mode 100644 packages/opentelemetry-scope-base/src/context.ts diff --git a/packages/opentelemetry-api/package.json b/packages/opentelemetry-api/package.json index 5d5cc4218e..d640ed3c9b 100644 --- a/packages/opentelemetry-api/package.json +++ b/packages/opentelemetry-api/package.json @@ -45,6 +45,9 @@ "publishConfig": { "access": "public" }, + "dependencies": { + "@opentelemetry/scope-base": "^0.4.0" + }, "devDependencies": { "@types/mocha": "^5.2.7", "@types/node": "^12.6.8", diff --git a/packages/opentelemetry-api/src/context/propagation/HttpTextFormat.ts b/packages/opentelemetry-api/src/context/propagation/HttpTextFormat.ts index c3cf8c314e..fef9293ecf 100644 --- a/packages/opentelemetry-api/src/context/propagation/HttpTextFormat.ts +++ b/packages/opentelemetry-api/src/context/propagation/HttpTextFormat.ts @@ -14,39 +14,35 @@ * limitations under the License. */ -import { SpanContext } from '../../trace/span_context'; +import { Context } from '@opentelemetry/scope-base'; +import { Carrier } from './carrier'; /** - * Injects and extracts a value as text into carriers that travel in-band - * across process boundaries. Encoding is expected to conform to the HTTP + * Injects {@link Context} into and extracts it from carriers that travel + * in-band across process boundaries. Encoding is expected to conform to the HTTP * Header Field semantics. Values are often encoded as RPC/HTTP request headers. * * The carrier of propagated data on both the client (injector) and server - * (extractor) side is usually an http request. Propagation is usually - * implemented via library- specific request interceptors, where the - * client-side injects values and the server-side extracts them. + * (extractor) side is usually an object such as http headers. */ export interface HttpTextFormat { /** - * Injects the given {@link SpanContext} instance to transmit over the wire. + * Injects values from a given {@link Context} into a carrier. * * OpenTelemetry defines a common set of format values (BinaryFormat and * HTTPTextFormat), and each has an expected `carrier` type. * - * @param spanContext the SpanContext to transmit over the wire. - * @param format the format of the carrier. - * @param carrier the carrier of propagation fields, such as an http request. + * @param context the Context from which to extract values to transmit over the wire. + * @param carrier the carrier of propagation fields, such as http request headers. */ - inject(spanContext: SpanContext, format: string, carrier: unknown): void; + inject(context: Context, carrier: Carrier): void; /** - * Returns a {@link SpanContext} instance extracted from `carrier` in the - * given format from upstream. + * Given a {@link Context} and a carrier, extract context values from a carrier and + * return a new context, created from the old context, with the extracted values. * - * @param format the format of the carrier. - * @param carrier the carrier of propagation fields, such as an http request. - * @returns SpanContext The extracted SpanContext, or null if no such - * SpanContext could be found in carrier. + * @param context the Context from which to extract values to transmit over the wire. + * @param carrier the carrier of propagation fields, such as http request headers. */ - extract(format: string, carrier: unknown): SpanContext | null; + extract(context: Context, carrier: Carrier): Context; } diff --git a/packages/opentelemetry-api/src/context/propagation/NoopHttpTextFormat.ts b/packages/opentelemetry-api/src/context/propagation/NoopHttpTextFormat.ts index 7596dd96b3..1f38729eb5 100644 --- a/packages/opentelemetry-api/src/context/propagation/NoopHttpTextFormat.ts +++ b/packages/opentelemetry-api/src/context/propagation/NoopHttpTextFormat.ts @@ -14,18 +14,19 @@ * limitations under the License. */ -import { SpanContext } from '../../trace/span_context'; +import { Context } from '@opentelemetry/scope-base'; +import { Carrier } from './carrier'; import { HttpTextFormat } from './HttpTextFormat'; /** * No-op implementations of {@link HttpTextFormat}. */ export class NoopHttpTextFormat implements HttpTextFormat { - // By default does nothing - inject(spanContext: SpanContext, format: string, carrier: unknown): void {} - // By default does nothing - extract(format: string, carrier: unknown): SpanContext | null { - return null; + /** Noop inject function does nothing */ + inject(context: Context, carrier: Carrier): void {} + /** Noop extract function does nothing and returns the input context */ + extract(context: Context, carrier: Carrier): Context { + return context; } } diff --git a/packages/opentelemetry-api/src/context/propagation/carrier.ts b/packages/opentelemetry-api/src/context/propagation/carrier.ts new file mode 100644 index 0000000000..7644ace1af --- /dev/null +++ b/packages/opentelemetry-api/src/context/propagation/carrier.ts @@ -0,0 +1,19 @@ +/*! + * 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 Carrier = { + [key: string]: unknown; +}; diff --git a/packages/opentelemetry-api/src/distributed_context/DistributedContext.ts b/packages/opentelemetry-api/src/distributed_context/DistributedContext.ts deleted file mode 100644 index b06dcb8059..0000000000 --- a/packages/opentelemetry-api/src/distributed_context/DistributedContext.ts +++ /dev/null @@ -1,29 +0,0 @@ -/*! - * Copyright 2019, 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 { EntryValue } from './EntryValue'; - -/** - * DistributedContext represents collection of entries. Each key of - * DistributedContext is associated with exactly one value. DistributedContext - * is serializable, to facilitate propagating it not only inside the process - * but also across process boundaries. DistributedContext is used to annotate - * telemetry with the name:value pair Entry. Those values can be used to add - * dimension to the metric or additional contest properties to logs and traces. - */ -export interface DistributedContext { - [entryKey: string]: EntryValue; -} diff --git a/packages/opentelemetry-api/src/distributed_context/EntryValue.ts b/packages/opentelemetry-api/src/distributed_context/EntryValue.ts deleted file mode 100644 index cb0c58557e..0000000000 --- a/packages/opentelemetry-api/src/distributed_context/EntryValue.ts +++ /dev/null @@ -1,45 +0,0 @@ -/*! - * Copyright 2019, 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. - */ - -/** - * {@link EntryValue} contains properties associated with a {@link - * DistributedContext}. - */ -export interface EntryValue { - /** `String` value of the `EntryValue`. */ - value: string; - /** - * ttl is an integer that represents number of hops an entry can - * propagate. - */ - ttl?: EntryTtl; -} - -/** - * EntryTtl is an integer that represents number of hops an entry can propagate. - * - * For now, ONLY special values (0 and -1) are supported. - */ -export enum EntryTtl { - /** - * NO_PROPAGATION is considered to have local scope and is used within the - * process it created. - */ - NO_PROPAGATION = 0, - - /** UNLIMITED_PROPAGATION can propagate unlimited hops. */ - UNLIMITED_PROPAGATION = -1, -} diff --git a/packages/opentelemetry-api/src/index.ts b/packages/opentelemetry-api/src/index.ts index ab10bb7ed4..49a1d42e48 100644 --- a/packages/opentelemetry-api/src/index.ts +++ b/packages/opentelemetry-api/src/index.ts @@ -17,33 +17,32 @@ export * from './common/Logger'; export * from './common/Time'; export * from './context/propagation/BinaryFormat'; +export * from './context/propagation/carrier'; export * from './context/propagation/HttpTextFormat'; -export * from './distributed_context/DistributedContext'; -export * from './distributed_context/EntryValue'; export * from './metrics/BoundInstrument'; export * from './metrics/Meter'; export * from './metrics/MeterProvider'; export * from './metrics/Metric'; +export * from './metrics/NoopMeter'; +export * from './metrics/NoopMeterProvider'; export * from './trace/attributes'; export * from './trace/Event'; export * from './trace/instrumentation/Plugin'; export * from './trace/link'; +export * from './trace/NoopSpan'; +export * from './trace/NoopTracer'; +export * from './trace/NoopTracerProvider'; export * from './trace/Sampler'; -export * from './trace/span'; -export * from './trace/SpanOptions'; export * from './trace/span_context'; export * from './trace/span_kind'; +export * from './trace/span'; +export * from './trace/SpanOptions'; export * from './trace/status'; export * from './trace/TimedEvent'; -export * from './trace/tracer'; -export * from './trace/tracer_provider'; export * from './trace/trace_flags'; export * from './trace/trace_state'; -export * from './trace/NoopSpan'; -export * from './trace/NoopTracer'; -export * from './trace/NoopTracerProvider'; -export * from './metrics/NoopMeterProvider'; -export * from './metrics/NoopMeter'; +export * from './trace/tracer_provider'; +export * from './trace/tracer'; import { TraceAPI } from './api/trace'; /** Entrypoint for trace API */ diff --git a/packages/opentelemetry-api/src/metrics/BoundInstrument.ts b/packages/opentelemetry-api/src/metrics/BoundInstrument.ts index 83612d6b4d..284f6e4a8c 100644 --- a/packages/opentelemetry-api/src/metrics/BoundInstrument.ts +++ b/packages/opentelemetry-api/src/metrics/BoundInstrument.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import { DistributedContext } from '../distributed_context/DistributedContext'; import { SpanContext } from '../trace/span_context'; /** An Instrument for Counter Metric. */ @@ -40,15 +39,9 @@ export interface BoundMeasure { /** * Records the given value to this measure. * @param value the measurement to record. - * @param distContext the distContext associated with the measurements. * @param spanContext the {@link SpanContext} that identifies the {@link Span} * for which the measurements are associated with. */ record(value: number): void; - record(value: number, distContext: DistributedContext): void; - record( - value: number, - distContext: DistributedContext, - spanContext: SpanContext - ): void; + record(value: number, spanContext: SpanContext): void; } diff --git a/packages/opentelemetry-api/src/metrics/Metric.ts b/packages/opentelemetry-api/src/metrics/Metric.ts index 0c6ca61a70..d35da4fa7c 100644 --- a/packages/opentelemetry-api/src/metrics/Metric.ts +++ b/packages/opentelemetry-api/src/metrics/Metric.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import { DistributedContext } from '../distributed_context/DistributedContext'; import { SpanContext } from '../trace/span_context'; /** @@ -123,18 +122,7 @@ export interface MetricUtils { */ record(value: number, labelSet: LabelSet): void; - record( - value: number, - labelSet: LabelSet, - distContext: DistributedContext - ): void; - - record( - value: number, - labelSet: LabelSet, - distContext: DistributedContext, - spanContext: SpanContext - ): void; + record(value: number, labelSet: LabelSet, spanContext: SpanContext): void; } /** diff --git a/packages/opentelemetry-api/src/metrics/NoopMeter.ts b/packages/opentelemetry-api/src/metrics/NoopMeter.ts index c39a664aa5..0c3ecfce26 100644 --- a/packages/opentelemetry-api/src/metrics/NoopMeter.ts +++ b/packages/opentelemetry-api/src/metrics/NoopMeter.ts @@ -17,7 +17,6 @@ import { Meter } from './Meter'; import { MetricOptions, Metric, Labels, LabelSet, MetricUtils } from './Metric'; import { BoundMeasure, BoundCounter, BoundGauge } from './BoundInstrument'; -import { DistributedContext } from '../distributed_context/DistributedContext'; import { SpanContext } from '../trace/span_context'; /** @@ -119,18 +118,11 @@ export class NoopGaugeMetric extends NoopMetric export class NoopMeasureMetric extends NoopMetric implements Pick { - record( - value: number, - labelSet: LabelSet, - distContext?: DistributedContext, - spanContext?: SpanContext - ) { - if (typeof distContext === 'undefined') { + record(value: number, labelSet: LabelSet, spanContext?: SpanContext) { + if (typeof spanContext === 'undefined') { this.bind(labelSet).record(value); - } else if (typeof spanContext === 'undefined') { - this.bind(labelSet).record(value, distContext); } else { - this.bind(labelSet).record(value, distContext, spanContext); + this.bind(labelSet).record(value, spanContext); } } } @@ -148,11 +140,7 @@ export class NoopBoundGauge implements BoundGauge { } export class NoopBoundMeasure implements BoundMeasure { - record( - value: number, - distContext?: DistributedContext, - spanContext?: SpanContext - ): void { + record(value: number, spanContext?: SpanContext): void { return; } } 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 32dd6813ed..9fd5171dbd 100644 --- a/packages/opentelemetry-api/test/noop-implementations/noop-tracer.test.ts +++ b/packages/opentelemetry-api/test/noop-implementations/noop-tracer.test.ts @@ -16,6 +16,7 @@ import * as assert from 'assert'; import { NoopTracer, NOOP_SPAN, SpanKind } from '../../src'; +import { Context } from '@opentelemetry/scope-base'; describe('NoopTracer', () => { it('should not crash', () => { @@ -38,8 +39,9 @@ describe('NoopTracer', () => { assert.deepStrictEqual(tracer.getCurrentSpan(), NOOP_SPAN); const httpTextFormat = tracer.getHttpTextFormat(); assert.ok(httpTextFormat); - httpTextFormat.inject(spanContext, 'HttpTextFormat', {}); - assert.deepStrictEqual(httpTextFormat.extract('HttpTextFormat', {}), null); + + httpTextFormat.inject(Context.ROOT_CONTEXT, {}); + assert.deepStrictEqual(httpTextFormat.extract(Context.ROOT_CONTEXT, {}), Context.ROOT_CONTEXT); const binaryFormat = tracer.getBinaryFormat(); assert.ok(binaryFormat); diff --git a/packages/opentelemetry-core/package.json b/packages/opentelemetry-core/package.json index 42a02a24e6..ae59461fc8 100644 --- a/packages/opentelemetry-core/package.json +++ b/packages/opentelemetry-core/package.json @@ -79,6 +79,7 @@ }, "dependencies": { "@opentelemetry/api": "^0.4.0", + "@opentelemetry/scope-base": "^0.4.0", "semver": "^6.3.0" } } diff --git a/packages/opentelemetry-core/src/context/context.ts b/packages/opentelemetry-core/src/context/context.ts new file mode 100644 index 0000000000..b404664423 --- /dev/null +++ b/packages/opentelemetry-core/src/context/context.ts @@ -0,0 +1,81 @@ +/*! + * 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. + */ + +import { Span, SpanContext } from '@opentelemetry/api'; +import { Context as ContextBase } from '@opentelemetry/scope-base'; + +/** + * Context class with static helper functions for accessing values + * of cross-cutting concerns. + */ +export class Context extends ContextBase { + /** + * Return the active span if one exists + * + * @param context context to get span from + */ + static getActiveSpan(context: Context): Span | undefined { + return (context.getValue('ACTIVE_SPAN') as Span) || undefined; + } + + /** + * Set the active span on a context + * + * @param context context to use as parent + * @param span span to set active + */ + static setActiveSpan(context: Context, span: Span): Context { + return context.setValue('ACTIVE_SPAN', span); + } + + /** + * Get the extracted span context from a context + * + * @param context context to get span context from + */ + static getExtractedSpanContext(context: Context): SpanContext | undefined { + return ( + (context.getValue('EXTRACTED_SPAN_CONTEXT') as SpanContext) || undefined + ); + } + + /** + * Set the extracted span context on a context + * + * @param context context to set span context on + * @param spanContext span context to set + */ + static setExtractedSpanContext( + context: Context, + spanContext: SpanContext + ): Context { + return context.setValue('EXTRACTED_SPAN_CONTEXT', spanContext); + } + + /** + * Get the span context of the parent span if it exists, + * or the extracted span context if there is no active + * span. + * + * @param context context to get values from + */ + static getParentSpanContext(context: Context) { + return ( + Context.getActiveSpan(context)?.context() || + Context.getExtractedSpanContext(context) + ); + } +} diff --git a/packages/opentelemetry-core/src/context/propagation/B3Format.ts b/packages/opentelemetry-core/src/context/propagation/B3Format.ts index 2363edc3c7..5b3db96594 100644 --- a/packages/opentelemetry-core/src/context/propagation/B3Format.ts +++ b/packages/opentelemetry-core/src/context/propagation/B3Format.ts @@ -14,7 +14,8 @@ * limitations under the License. */ -import { SpanContext, HttpTextFormat, TraceFlags } from '@opentelemetry/api'; +import { HttpTextFormat, TraceFlags } from '@opentelemetry/api'; +import { Context } from '../context'; export const X_B3_TRACE_ID = 'x-b3-traceid'; export const X_B3_SPAN_ID = 'x-b3-spanid'; @@ -36,11 +37,10 @@ function isValidSpanId(spanId: string): boolean { * Based on: https://github.com/openzipkin/b3-propagation */ export class B3Format implements HttpTextFormat { - inject( - spanContext: SpanContext, - format: string, - carrier: { [key: string]: unknown } - ): void { + inject(context: Context, carrier: { [key: string]: unknown }) { + const spanContext = Context.getParentSpanContext(context); + if (!spanContext) return; + if ( isValidTraceId(spanContext.traceId) && isValidSpanId(spanContext.spanId) @@ -56,14 +56,11 @@ export class B3Format implements HttpTextFormat { } } - extract( - format: string, - carrier: { [key: string]: unknown } - ): SpanContext | null { + extract(context: Context, carrier: { [key: string]: unknown }): Context { const traceIdHeader = carrier[X_B3_TRACE_ID]; const spanIdHeader = carrier[X_B3_SPAN_ID]; const sampledHeader = carrier[X_B3_SAMPLED]; - if (!traceIdHeader || !spanIdHeader) return null; + if (!traceIdHeader || !spanIdHeader) return context; const traceId = Array.isArray(traceIdHeader) ? traceIdHeader[0] : traceIdHeader; @@ -73,15 +70,15 @@ export class B3Format implements HttpTextFormat { : sampledHeader; if (isValidTraceId(traceId) && isValidSpanId(spanId)) { - return { + return Context.setExtractedSpanContext(context, { traceId, spanId, isRemote: true, traceFlags: isNaN(Number(options)) ? TraceFlags.UNSAMPLED : Number(options), - }; + }); } - return null; + return context; } } diff --git a/packages/opentelemetry-core/src/context/propagation/HttpTraceContext.ts b/packages/opentelemetry-core/src/context/propagation/HttpTraceContext.ts index cf9860014f..fa70fbe1d2 100644 --- a/packages/opentelemetry-core/src/context/propagation/HttpTraceContext.ts +++ b/packages/opentelemetry-core/src/context/propagation/HttpTraceContext.ts @@ -16,6 +16,7 @@ import { HttpTextFormat, SpanContext, TraceFlags } from '@opentelemetry/api'; import { TraceState } from '../../trace/TraceState'; +import { Context } from '../context'; export const TRACE_PARENT_HEADER = 'traceparent'; export const TRACE_STATE_HEADER = 'tracestate'; @@ -56,11 +57,10 @@ export function parseTraceParent(traceParent: string): SpanContext | null { * https://www.w3.org/TR/trace-context/ */ export class HttpTraceContext implements HttpTextFormat { - inject( - spanContext: SpanContext, - format: string, - carrier: { [key: string]: unknown } - ) { + inject(context: Context, carrier: { [key: string]: unknown }) { + const spanContext = Context.getParentSpanContext(context); + if (!spanContext) return; + const traceParent = `${VERSION}-${spanContext.traceId}-${ spanContext.spanId }-0${Number(spanContext.traceFlags || TraceFlags.UNSAMPLED).toString(16)}`; @@ -71,17 +71,14 @@ export class HttpTraceContext implements HttpTextFormat { } } - extract( - format: string, - carrier: { [key: string]: unknown } - ): SpanContext | null { + extract(context: Context, carrier: { [key: string]: unknown }): Context { const traceParentHeader = carrier[TRACE_PARENT_HEADER]; - if (!traceParentHeader) return null; + if (!traceParentHeader) return context; const traceParent = Array.isArray(traceParentHeader) ? traceParentHeader[0] : traceParentHeader; const spanContext = parseTraceParent(traceParent); - if (!spanContext) return null; + if (!spanContext) return context; spanContext.isRemote = true; @@ -94,6 +91,6 @@ export class HttpTraceContext implements HttpTextFormat { : traceStateHeader; spanContext.traceState = new TraceState(state as string); } - return spanContext; + return Context.setExtractedSpanContext(context, spanContext); } } diff --git a/packages/opentelemetry-core/src/index.ts b/packages/opentelemetry-core/src/index.ts index f1d0611e27..3038ce0b95 100644 --- a/packages/opentelemetry-core/src/index.ts +++ b/packages/opentelemetry-core/src/index.ts @@ -19,6 +19,7 @@ export * from './common/NoopLogger'; export * from './common/time'; export * from './common/types'; export * from './version'; +export * from './context/context'; export * from './context/propagation/B3Format'; export * from './context/propagation/BinaryTraceContext'; export * from './context/propagation/HttpTraceContext'; diff --git a/packages/opentelemetry-core/test/context/B3Format.test.ts b/packages/opentelemetry-core/test/context/B3Format.test.ts index 97087e076a..ea352f24cc 100644 --- a/packages/opentelemetry-core/test/context/B3Format.test.ts +++ b/packages/opentelemetry-core/test/context/B3Format.test.ts @@ -14,14 +14,15 @@ * limitations under the License. */ +import { SpanContext, TraceFlags } from '@opentelemetry/api'; import * as assert from 'assert'; +import { Context } from '../../src/context/context'; import { B3Format, - X_B3_TRACE_ID, - X_B3_SPAN_ID, X_B3_SAMPLED, + X_B3_SPAN_ID, + X_B3_TRACE_ID, } from '../../src/context/propagation/B3Format'; -import { SpanContext, TraceFlags } from '@opentelemetry/api'; import { TraceState } from '../../src/trace/TraceState'; describe('B3Format', () => { @@ -40,7 +41,10 @@ describe('B3Format', () => { traceFlags: TraceFlags.SAMPLED, }; - b3Format.inject(spanContext, 'B3Format', carrier); + b3Format.inject( + Context.setExtractedSpanContext(Context.ROOT_CONTEXT, spanContext), + carrier + ); assert.deepStrictEqual( carrier[X_B3_TRACE_ID], 'd4cda95b652f4a1592b449d5929fda1b' @@ -58,7 +62,10 @@ describe('B3Format', () => { isRemote: false, }; - b3Format.inject(spanContext, 'B3Format', carrier); + b3Format.inject( + Context.setExtractedSpanContext(Context.ROOT_CONTEXT, spanContext), + carrier + ); assert.deepStrictEqual( carrier[X_B3_TRACE_ID], 'd4cda95b652f4a1592b449d5929fda1b' @@ -72,7 +79,10 @@ describe('B3Format', () => { traceId: '', spanId: '', }; - b3Format.inject(emptySpanContext, 'B3Format', carrier); + b3Format.inject( + Context.setExtractedSpanContext(Context.ROOT_CONTEXT, emptySpanContext), + carrier + ); assert.deepStrictEqual(carrier[X_B3_TRACE_ID], undefined); assert.deepStrictEqual(carrier[X_B3_SPAN_ID], undefined); }); @@ -83,7 +93,10 @@ describe('B3Format', () => { spanId: '6e0c63257de34c92', }; - b3Format.inject(spanContext, 'B3Format', carrier); + b3Format.inject( + Context.setExtractedSpanContext(Context.ROOT_CONTEXT, spanContext), + carrier + ); assert.deepStrictEqual( carrier[X_B3_TRACE_ID], 'd4cda95b652f4a1592b449d5929fda1b' @@ -97,7 +110,9 @@ describe('B3Format', () => { it('should extract context of a unsampled span from carrier', () => { carrier[X_B3_TRACE_ID] = '0af7651916cd43dd8448eb211c80319c'; carrier[X_B3_SPAN_ID] = 'b7ad6b7169203331'; - const extractedSpanContext = b3Format.extract('B3Format', carrier); + const extractedSpanContext = Context.getExtractedSpanContext( + b3Format.extract(Context.ROOT_CONTEXT, carrier) + ); assert.deepStrictEqual(extractedSpanContext, { spanId: 'b7ad6b7169203331', @@ -111,7 +126,9 @@ describe('B3Format', () => { carrier[X_B3_TRACE_ID] = '0af7651916cd43dd8448eb211c80319c'; carrier[X_B3_SPAN_ID] = 'b7ad6b7169203331'; carrier[X_B3_SAMPLED] = '1'; - const extractedSpanContext = b3Format.extract('B3Format', carrier); + const extractedSpanContext = Context.getExtractedSpanContext( + b3Format.extract(Context.ROOT_CONTEXT, carrier) + ); assert.deepStrictEqual(extractedSpanContext, { spanId: 'b7ad6b7169203331', @@ -125,7 +142,9 @@ describe('B3Format', () => { carrier[X_B3_TRACE_ID] = '0af7651916cd43dd8448eb211c80319c'; carrier[X_B3_SPAN_ID] = 'b7ad6b7169203331'; carrier[X_B3_SAMPLED] = true; - const extractedSpanContext = b3Format.extract('B3Format', carrier); + const extractedSpanContext = Context.getExtractedSpanContext( + b3Format.extract(Context.ROOT_CONTEXT, carrier) + ); assert.deepStrictEqual(extractedSpanContext, { spanId: 'b7ad6b7169203331', @@ -139,7 +158,9 @@ describe('B3Format', () => { carrier[X_B3_TRACE_ID] = '0af7651916cd43dd8448eb211c80319c'; carrier[X_B3_SPAN_ID] = 'b7ad6b7169203331'; carrier[X_B3_SAMPLED] = false; - const extractedSpanContext = b3Format.extract('B3Format', carrier); + const extractedSpanContext = Context.getExtractedSpanContext( + b3Format.extract(Context.ROOT_CONTEXT, carrier) + ); assert.deepStrictEqual(extractedSpanContext, { spanId: 'b7ad6b7169203331', @@ -149,32 +170,54 @@ describe('B3Format', () => { }); }); - it('should return null when traceId is undefined', () => { + it('should return undefined when traceId is undefined', () => { carrier[X_B3_TRACE_ID] = undefined; carrier[X_B3_SPAN_ID] = undefined; - assert.deepStrictEqual(b3Format.extract('B3Format', carrier), null); + assert.deepStrictEqual( + Context.getExtractedSpanContext( + b3Format.extract(Context.ROOT_CONTEXT, carrier) + ), + undefined + ); }); - it('should return null when options and spanId are undefined', () => { + it('should return undefined when options and spanId are undefined', () => { carrier[X_B3_TRACE_ID] = '0af7651916cd43dd8448eb211c80319c'; carrier[X_B3_SPAN_ID] = undefined; - assert.deepStrictEqual(b3Format.extract('B3Format', carrier), null); + assert.deepStrictEqual( + Context.getExtractedSpanContext( + b3Format.extract(Context.ROOT_CONTEXT, carrier) + ), + undefined + ); }); - it('returns null if b3 header is missing', () => { - assert.deepStrictEqual(b3Format.extract('B3Format', carrier), null); + it('returns undefined if b3 header is missing', () => { + assert.deepStrictEqual( + Context.getExtractedSpanContext( + b3Format.extract(Context.ROOT_CONTEXT, carrier) + ), + undefined + ); }); - it('returns null if b3 header is invalid', () => { + it('returns undefined if b3 header is invalid', () => { carrier[X_B3_TRACE_ID] = 'invalid!'; - assert.deepStrictEqual(b3Format.extract('B3Format', carrier), null); + assert.deepStrictEqual( + Context.getExtractedSpanContext( + b3Format.extract(Context.ROOT_CONTEXT, carrier) + ), + undefined + ); }); it('extracts b3 from list of header', () => { carrier[X_B3_TRACE_ID] = ['0af7651916cd43dd8448eb211c80319c']; carrier[X_B3_SPAN_ID] = 'b7ad6b7169203331'; carrier[X_B3_SAMPLED] = '01'; - const extractedSpanContext = b3Format.extract('B3Format', carrier); + const extractedSpanContext = Context.getExtractedSpanContext( + b3Format.extract(Context.ROOT_CONTEXT, carrier) + ); assert.deepStrictEqual(extractedSpanContext, { spanId: 'b7ad6b7169203331', traceId: '0af7651916cd43dd8448eb211c80319c', @@ -185,7 +228,7 @@ describe('B3Format', () => { 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 `null` SpanContext + // b3 header. These should all result in a `undefined` SpanContext // value being extracted. const testCases: Record = { @@ -223,8 +266,10 @@ describe('B3Format', () => { Object.getOwnPropertyNames(testCases).forEach(testCase => { carrier[X_B3_TRACE_ID] = testCases[testCase]; - const extractedSpanContext = b3Format.extract('B3Format', carrier); - assert.deepStrictEqual(extractedSpanContext, null, testCase); + const extractedSpanContext = Context.getExtractedSpanContext( + b3Format.extract(Context.ROOT_CONTEXT, carrier) + ); + 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 28cbf335b4..83dccac40b 100644 --- a/packages/opentelemetry-core/test/context/HttpTraceContext.test.ts +++ b/packages/opentelemetry-core/test/context/HttpTraceContext.test.ts @@ -14,13 +14,14 @@ * limitations under the License. */ +import { SpanContext, TraceFlags } from '@opentelemetry/api'; import * as assert from 'assert'; +import { Context } from '../../src/context/context'; import { HttpTraceContext, TRACE_PARENT_HEADER, TRACE_STATE_HEADER, } from '../../src/context/propagation/HttpTraceContext'; -import { SpanContext, TraceFlags } from '@opentelemetry/api'; import { TraceState } from '../../src/trace/TraceState'; describe('HttpTraceContext', () => { @@ -39,7 +40,10 @@ describe('HttpTraceContext', () => { traceFlags: TraceFlags.SAMPLED, }; - httpTraceContext.inject(spanContext, 'HttpTraceContext', carrier); + httpTraceContext.inject( + Context.setExtractedSpanContext(Context.ROOT_CONTEXT, spanContext), + carrier + ); assert.deepStrictEqual( carrier[TRACE_PARENT_HEADER], '00-d4cda95b652f4a1592b449d5929fda1b-6e0c63257de34c92-01' @@ -55,7 +59,10 @@ describe('HttpTraceContext', () => { traceState: new TraceState('foo=bar,baz=qux'), }; - httpTraceContext.inject(spanContext, '', carrier); + httpTraceContext.inject( + Context.setExtractedSpanContext(Context.ROOT_CONTEXT, spanContext), + carrier + ); assert.deepStrictEqual( carrier[TRACE_PARENT_HEADER], '00-d4cda95b652f4a1592b449d5929fda1b-6e0c63257de34c92-01' @@ -64,13 +71,12 @@ describe('HttpTraceContext', () => { }); }); - describe('.extract()', () => { + describe('.extract())', () => { it('should extract context of a sampled span from carrier', () => { carrier[TRACE_PARENT_HEADER] = '00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01'; - const extractedSpanContext = httpTraceContext.extract( - 'HttpTraceContext', - carrier + const extractedSpanContext = Context.getExtractedSpanContext( + httpTraceContext.extract(Context.ROOT_CONTEXT, carrier) ); assert.deepStrictEqual(extractedSpanContext, { @@ -83,16 +89,20 @@ describe('HttpTraceContext', () => { it('returns null if traceparent header is missing', () => { assert.deepStrictEqual( - httpTraceContext.extract('HttpTraceContext', carrier), - null + Context.getExtractedSpanContext( + httpTraceContext.extract(Context.ROOT_CONTEXT, carrier) + ), + undefined ); }); it('returns null if traceparent header is invalid', () => { carrier[TRACE_PARENT_HEADER] = 'invalid!'; assert.deepStrictEqual( - httpTraceContext.extract('HttpTraceContext', carrier), - null + Context.getExtractedSpanContext( + httpTraceContext.extract(Context.ROOT_CONTEXT, carrier) + ), + undefined ); }); @@ -100,9 +110,8 @@ describe('HttpTraceContext', () => { carrier[TRACE_PARENT_HEADER] = [ '00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01', ]; - const extractedSpanContext = httpTraceContext.extract( - 'HttpTraceContext', - carrier + const extractedSpanContext = Context.getExtractedSpanContext( + httpTraceContext.extract(Context.ROOT_CONTEXT, carrier) ); assert.deepStrictEqual(extractedSpanContext, { spanId: 'b7ad6b7169203331', @@ -116,10 +125,10 @@ describe('HttpTraceContext', () => { carrier[TRACE_PARENT_HEADER] = '00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01'; carrier[TRACE_STATE_HEADER] = 'foo=bar,baz=qux'; - const extractedSpanContext = httpTraceContext.extract( - 'HttpTraceContext', - carrier + const extractedSpanContext = Context.getExtractedSpanContext( + httpTraceContext.extract(Context.ROOT_CONTEXT, carrier) ); + assert.deepStrictEqual( extractedSpanContext!.traceState!.get('foo'), 'bar' @@ -134,9 +143,8 @@ describe('HttpTraceContext', () => { carrier[TRACE_PARENT_HEADER] = '00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01'; carrier[TRACE_STATE_HEADER] = ['foo=bar,baz=qux', 'quux=quuz']; - const extractedSpanContext = httpTraceContext.extract( - 'HttpTraceContext', - carrier + const extractedSpanContext = Context.getExtractedSpanContext( + httpTraceContext.extract(Context.ROOT_CONTEXT, carrier) ); assert.deepStrictEqual(extractedSpanContext, { spanId: 'b7ad6b7169203331', @@ -187,11 +195,10 @@ describe('HttpTraceContext', () => { Object.getOwnPropertyNames(testCases).forEach(testCase => { carrier[TRACE_PARENT_HEADER] = testCases[testCase]; - const extractedSpanContext = httpTraceContext.extract( - 'HttpTraceContext', - carrier + const extractedSpanContext = Context.getExtractedSpanContext( + httpTraceContext.extract(Context.ROOT_CONTEXT, carrier) ); - assert.deepStrictEqual(extractedSpanContext, null, testCase); + assert.deepStrictEqual(extractedSpanContext, undefined, testCase); }); }); }); diff --git a/packages/opentelemetry-metrics/src/BoundInstrument.ts b/packages/opentelemetry-metrics/src/BoundInstrument.ts index b72176fae1..e78dca9576 100644 --- a/packages/opentelemetry-metrics/src/BoundInstrument.ts +++ b/packages/opentelemetry-metrics/src/BoundInstrument.ts @@ -144,11 +144,7 @@ export class BoundMeasure extends BaseBoundInstrument super(labelSet); } - record( - value: number, - distContext?: types.DistributedContext, - spanContext?: types.SpanContext - ): void { + record(value: number, spanContext?: types.SpanContext): void { if (this._disabled) return; if (this._absolute && value < 0) { diff --git a/packages/opentelemetry-plugin-http/src/http.ts b/packages/opentelemetry-plugin-http/src/http.ts index ee4c7f44ca..e2abb4e369 100644 --- a/packages/opentelemetry-plugin-http/src/http.ts +++ b/packages/opentelemetry-plugin-http/src/http.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import { BasePlugin, isValid } from '@opentelemetry/core'; import { CanonicalCode, Span, @@ -22,6 +21,7 @@ import { SpanOptions, Status, } from '@opentelemetry/api'; +import { BasePlugin, Context, isValid } from '@opentelemetry/core'; import { ClientRequest, IncomingMessage, @@ -34,7 +34,6 @@ import * as semver from 'semver'; import * as shimmer from 'shimmer'; import * as url from 'url'; import { AttributeNames } from './enums/AttributeNames'; -import { Format } from './enums/Format'; import { Err, Func, @@ -298,7 +297,11 @@ export class HttpPlugin extends BasePlugin { }), }; - const spanContext = propagation.extract(Format.HTTP, headers); + // Using context direclty like this is temporary. In a future PR, context + // will be managed by the scope manager (which may be renamed to context manager?) + const spanContext = Context.getExtractedSpanContext( + propagation.extract(Context.ROOT_CONTEXT, headers) + ); if (spanContext && isValid(spanContext)) { spanOptions.parent = spanContext; } @@ -407,7 +410,12 @@ export class HttpPlugin extends BasePlugin { const span = plugin._startHttpSpan(operationName, spanOptions); plugin._tracer .getHttpTextFormat() - .inject(span.context(), Format.HTTP, options.headers); + // Using context direclty like this is temporary. In a future PR, context + // will be managed by the scope manager (which may be renamed to context manager?) + .inject( + Context.setActiveSpan(Context.ROOT_CONTEXT, span), + options.headers! + ); const request: ClientRequest = plugin._safeExecute( span, diff --git a/packages/opentelemetry-plugin-http/test/utils/DummyPropagation.ts b/packages/opentelemetry-plugin-http/test/utils/DummyPropagation.ts index 3e4ca727fc..9101ab7d3c 100644 --- a/packages/opentelemetry-plugin-http/test/utils/DummyPropagation.ts +++ b/packages/opentelemetry-plugin-http/test/utils/DummyPropagation.ts @@ -13,23 +13,22 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { SpanContext, HttpTextFormat } from '@opentelemetry/api'; +import { HttpTextFormat } from '@opentelemetry/api'; +import { Context } from '@opentelemetry/core'; import * as http from 'http'; export class DummyPropagation implements HttpTextFormat { static TRACE_CONTEXT_KEY = 'x-dummy-trace-id'; static SPAN_CONTEXT_KEY = 'x-dummy-span-id'; - extract(format: string, carrier: http.OutgoingHttpHeaders): SpanContext { - return { + extract(context: Context, carrier: http.OutgoingHttpHeaders) { + return Context.setExtractedSpanContext(Context.ROOT_CONTEXT, { traceId: carrier[DummyPropagation.TRACE_CONTEXT_KEY] as string, spanId: DummyPropagation.SPAN_CONTEXT_KEY, - }; + }); } - inject( - spanContext: SpanContext, - format: string, - headers: { [custom: string]: string } - ): void { + inject(context: Context, headers: { [custom: string]: string }): void { + const spanContext = Context.getParentSpanContext(context); + if (!spanContext) return; headers[DummyPropagation.TRACE_CONTEXT_KEY] = spanContext.traceId; headers[DummyPropagation.SPAN_CONTEXT_KEY] = spanContext.spanId; } diff --git a/packages/opentelemetry-plugin-https/test/utils/DummyPropagation.ts b/packages/opentelemetry-plugin-https/test/utils/DummyPropagation.ts index 3e4ca727fc..7bf3510ea7 100644 --- a/packages/opentelemetry-plugin-https/test/utils/DummyPropagation.ts +++ b/packages/opentelemetry-plugin-https/test/utils/DummyPropagation.ts @@ -13,23 +13,22 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { SpanContext, HttpTextFormat } from '@opentelemetry/api'; +import { HttpTextFormat } from '@opentelemetry/api'; +import { Context } from '@opentelemetry/core'; import * as http from 'http'; export class DummyPropagation implements HttpTextFormat { static TRACE_CONTEXT_KEY = 'x-dummy-trace-id'; static SPAN_CONTEXT_KEY = 'x-dummy-span-id'; - extract(format: string, carrier: http.OutgoingHttpHeaders): SpanContext { - return { + extract(context: Context, carrier: http.OutgoingHttpHeaders) { + return Context.setExtractedSpanContext(context, { traceId: carrier[DummyPropagation.TRACE_CONTEXT_KEY] as string, spanId: DummyPropagation.SPAN_CONTEXT_KEY, - }; + }); } - inject( - spanContext: SpanContext, - format: string, - headers: { [custom: string]: string } - ): void { + inject(context: Context, headers: { [custom: string]: string }): void { + const spanContext = Context.getParentSpanContext(context); + if (!spanContext) return; headers[DummyPropagation.TRACE_CONTEXT_KEY] = spanContext.traceId; headers[DummyPropagation.SPAN_CONTEXT_KEY] = spanContext.spanId; } diff --git a/packages/opentelemetry-plugin-xml-http-request/src/xhr.ts b/packages/opentelemetry-plugin-xml-http-request/src/xhr.ts index 3796dc4fa7..80c97c4ccf 100644 --- a/packages/opentelemetry-plugin-xml-http-request/src/xhr.ts +++ b/packages/opentelemetry-plugin-xml-http-request/src/xhr.ts @@ -16,6 +16,7 @@ import { BasePlugin, + Context, hrTime, isUrlIgnored, isWrapped, @@ -32,7 +33,6 @@ import { import * as shimmer from 'shimmer'; import { AttributeNames } from './enums/AttributeNames'; import { EventNames } from './enums/EventNames'; -import { Format } from './enums/Format'; import { OpenFunction, PropagateTraceHeaderCorsUrls, @@ -90,7 +90,9 @@ export class XMLHttpRequestPlugin extends BasePlugin { const headers: { [key: string]: unknown } = {}; this._tracer .getHttpTextFormat() - .inject(span.context(), Format.HTTP, headers); + // Using context direclty like this is temporary. In a future PR, context + // will be managed by the scope manager (which may be renamed to context manager?) + .inject(Context.setActiveSpan(Context.ROOT_CONTEXT, span), headers); Object.keys(headers).forEach(key => { xhr.setRequestHeader(key, String(headers[key])); diff --git a/packages/opentelemetry-propagator-jaeger/package.json b/packages/opentelemetry-propagator-jaeger/package.json index 565fe038ab..7cbe55f50b 100644 --- a/packages/opentelemetry-propagator-jaeger/package.json +++ b/packages/opentelemetry-propagator-jaeger/package.json @@ -70,6 +70,7 @@ "webpack": "^4.35.2" }, "dependencies": { - "@opentelemetry/api": "^0.4.0" + "@opentelemetry/api": "^0.4.0", + "@opentelemetry/core": "^0.4.0" } } diff --git a/packages/opentelemetry-propagator-jaeger/src/JaegerHttpTraceFormat.ts b/packages/opentelemetry-propagator-jaeger/src/JaegerHttpTraceFormat.ts index bcfd972492..6fb7c78b0a 100644 --- a/packages/opentelemetry-propagator-jaeger/src/JaegerHttpTraceFormat.ts +++ b/packages/opentelemetry-propagator-jaeger/src/JaegerHttpTraceFormat.ts @@ -14,7 +14,8 @@ * limitations under the License. */ -import { SpanContext, HttpTextFormat, TraceFlags } from '@opentelemetry/api'; +import { HttpTextFormat, SpanContext, TraceFlags } from '@opentelemetry/api'; +import { Context } from '@opentelemetry/core'; export const UBER_TRACE_ID_HEADER = 'uber-trace-id'; @@ -43,16 +44,10 @@ export class JaegerHttpTraceFormat implements HttpTextFormat { this._jaegerTraceHeader = customTraceHeader || UBER_TRACE_ID_HEADER; } - /** - * @param {SpanContext} spanContext - context from which we take information to inject in carrier. - * @param {string} format - unused. - * @param { [key: string]: unknown } carrier - a carrier to which span information will be injected. - **/ - inject( - spanContext: SpanContext, - format: string, - carrier: { [key: string]: unknown } - ) { + inject(context: Context, carrier: { [key: string]: unknown }) { + const spanContext = Context.getParentSpanContext(context); + if (!spanContext) return; + const traceFlags = `0${( spanContext.traceFlags || TraceFlags.UNSAMPLED ).toString(16)}`; @@ -62,22 +57,17 @@ export class JaegerHttpTraceFormat implements HttpTextFormat { ] = `${spanContext.traceId}:${spanContext.spanId}:0:${traceFlags}`; } - /** - * @param {string} format - unused. - * @param { [key: string]: unknown } carrier - a carrier from which span context will be constructed. - * @return {SpanContext} - returns a span context extracted from carrier. - **/ - extract( - format: string, - carrier: { [key: string]: unknown } - ): SpanContext | null { + extract(context: Context, carrier: { [key: string]: unknown }): Context { const uberTraceIdHeader = carrier[this._jaegerTraceHeader]; - if (!uberTraceIdHeader) return null; + if (!uberTraceIdHeader) return context; const uberTraceId = Array.isArray(uberTraceIdHeader) ? uberTraceIdHeader[0] : uberTraceIdHeader; - return deserializeSpanContext(uberTraceId); + const spanContext = deserializeSpanContext(uberTraceId); + if (!spanContext) return context; + + return Context.setExtractedSpanContext(context, spanContext); } } diff --git a/packages/opentelemetry-propagator-jaeger/test/JaegerHttpTraceFormat.test.ts b/packages/opentelemetry-propagator-jaeger/test/JaegerHttpTraceFormat.test.ts index 4e1ae9c9b6..f641ebdf6d 100644 --- a/packages/opentelemetry-propagator-jaeger/test/JaegerHttpTraceFormat.test.ts +++ b/packages/opentelemetry-propagator-jaeger/test/JaegerHttpTraceFormat.test.ts @@ -14,12 +14,13 @@ * limitations under the License. */ +import { SpanContext, TraceFlags } from '@opentelemetry/api'; +import { Context } from '@opentelemetry/core'; import * as assert from 'assert'; import { JaegerHttpTraceFormat, UBER_TRACE_ID_HEADER, } from '../src/JaegerHttpTraceFormat'; -import { SpanContext, TraceFlags } from '@opentelemetry/api'; describe('JaegerHttpTraceFormat', () => { const jaegerHttpTraceFormat = new JaegerHttpTraceFormat(); @@ -39,7 +40,10 @@ describe('JaegerHttpTraceFormat', () => { traceFlags: TraceFlags.SAMPLED, }; - jaegerHttpTraceFormat.inject(spanContext, '', carrier); + jaegerHttpTraceFormat.inject( + Context.setExtractedSpanContext(Context.ROOT_CONTEXT, spanContext), + carrier + ); assert.deepStrictEqual( carrier[UBER_TRACE_ID_HEADER], 'd4cda95b652f4a1592b449d5929fda1b:6e0c63257de34c92:0:01' @@ -53,7 +57,10 @@ describe('JaegerHttpTraceFormat', () => { traceFlags: TraceFlags.SAMPLED, }; - customJaegerHttpTraceFormat.inject(spanContext, '', carrier); + customJaegerHttpTraceFormat.inject( + Context.setExtractedSpanContext(Context.ROOT_CONTEXT, spanContext), + carrier + ); assert.deepStrictEqual( carrier[customHeader], 'd4cda95b652f4a1592b449d5929fda1b:6e0c63257de34c92:0:01' @@ -65,7 +72,9 @@ describe('JaegerHttpTraceFormat', () => { it('should extract context of a sampled span from carrier', () => { carrier[UBER_TRACE_ID_HEADER] = 'd4cda95b652f4a1592b449d5929fda1b:6e0c63257de34c92:0:01'; - const extractedSpanContext = jaegerHttpTraceFormat.extract('', carrier); + const extractedSpanContext = Context.getExtractedSpanContext( + jaegerHttpTraceFormat.extract(Context.ROOT_CONTEXT, carrier) + ); assert.deepStrictEqual(extractedSpanContext, { spanId: '6e0c63257de34c92', @@ -78,7 +87,9 @@ describe('JaegerHttpTraceFormat', () => { it('should extract context of a sampled span from carrier with 1 bit flag', () => { carrier[UBER_TRACE_ID_HEADER] = '9c41e35aeb6d1272:45fd2a9709dadcf1:a13699e3fb724f40:1'; - const extractedSpanContext = jaegerHttpTraceFormat.extract('', carrier); + const extractedSpanContext = Context.getExtractedSpanContext( + jaegerHttpTraceFormat.extract(Context.ROOT_CONTEXT, carrier) + ); assert.deepStrictEqual(extractedSpanContext, { spanId: '45fd2a9709dadcf1', @@ -91,7 +102,9 @@ describe('JaegerHttpTraceFormat', () => { it('should extract context of a sampled span from UTF-8 encoded carrier', () => { carrier[UBER_TRACE_ID_HEADER] = 'ac1f3dc3c2c0b06e%3A5ac292c4a11a163e%3Ac086aaa825821068%3A1'; - const extractedSpanContext = jaegerHttpTraceFormat.extract('', carrier); + const extractedSpanContext = Context.getExtractedSpanContext( + jaegerHttpTraceFormat.extract(Context.ROOT_CONTEXT, carrier) + ); assert.deepStrictEqual(extractedSpanContext, { spanId: '5ac292c4a11a163e', @@ -104,9 +117,8 @@ describe('JaegerHttpTraceFormat', () => { it('should use custom header if provided', () => { carrier[customHeader] = 'd4cda95b652f4a1592b449d5929fda1b:6e0c63257de34c92:0:01'; - const extractedSpanContext = customJaegerHttpTraceFormat.extract( - '', - carrier + const extractedSpanContext = Context.getExtractedSpanContext( + customJaegerHttpTraceFormat.extract(Context.ROOT_CONTEXT, carrier) ); assert.deepStrictEqual(extractedSpanContext, { @@ -117,15 +129,22 @@ describe('JaegerHttpTraceFormat', () => { }); }); - it('returns null if UBER_TRACE_ID_HEADER header is missing', () => { - assert.deepStrictEqual(jaegerHttpTraceFormat.extract('', carrier), null); + it('returns undefined if UBER_TRACE_ID_HEADER header is missing', () => { + assert.deepStrictEqual( + Context.getExtractedSpanContext( + jaegerHttpTraceFormat.extract(Context.ROOT_CONTEXT, carrier) + ), + undefined + ); }); - it('returns null if UBER_TRACE_ID_HEADER header is invalid', () => { + it('returns undefined if UBER_TRACE_ID_HEADER header is invalid', () => { carrier[UBER_TRACE_ID_HEADER] = 'invalid!'; assert.deepStrictEqual( - jaegerHttpTraceFormat.extract('HttpTraceContext', carrier), - null + Context.getExtractedSpanContext( + jaegerHttpTraceFormat.extract(Context.ROOT_CONTEXT, carrier) + ), + undefined ); }); }); diff --git a/packages/opentelemetry-scope-async-hooks/src/AsyncHooksScopeManager.ts b/packages/opentelemetry-scope-async-hooks/src/AsyncHooksScopeManager.ts index f3f4ee7f78..b0d1df0c21 100644 --- a/packages/opentelemetry-scope-async-hooks/src/AsyncHooksScopeManager.ts +++ b/packages/opentelemetry-scope-async-hooks/src/AsyncHooksScopeManager.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { ScopeManager } from '@opentelemetry/scope-base'; +import { ScopeManager, Context } from '@opentelemetry/scope-base'; import * as asyncHooks from 'async_hooks'; import { EventEmitter } from 'events'; @@ -39,7 +39,9 @@ const ADD_LISTENER_METHODS = [ export class AsyncHooksScopeManager implements ScopeManager { private _asyncHook: asyncHooks.AsyncHook; - private _scopes: { [uid: number]: unknown } = Object.create(null); + private _scopes: { + [uid: number]: Context | undefined | null; + } = Object.create(null); constructor() { this._asyncHook = asyncHooks.createHook({ @@ -49,12 +51,12 @@ export class AsyncHooksScopeManager implements ScopeManager { }); } - active(): unknown { - return this._scopes[asyncHooks.executionAsyncId()] || undefined; + active(): Context { + return this._scopes[asyncHooks.executionAsyncId()] || Context.ROOT_CONTEXT; } with ReturnType>( - scope: unknown, + scope: Context, fn: T ): ReturnType { const uid = asyncHooks.executionAsyncId(); @@ -73,7 +75,7 @@ export class AsyncHooksScopeManager implements ScopeManager { } } - bind(target: T, scope?: unknown): T { + bind(target: T, scope: Context): T { // if no specific scope to propagate is given, we use the current one if (scope === undefined) { scope = this.active(); @@ -97,7 +99,7 @@ export class AsyncHooksScopeManager implements ScopeManager { return this; } - private _bindFunction(target: T, scope?: unknown): T { + private _bindFunction(target: T, scope: Context): T { const manager = this; const contextWrapper = function(this: {}, ...args: unknown[]) { return manager.with(scope, () => target.apply(this, args)); @@ -125,7 +127,7 @@ export class AsyncHooksScopeManager implements ScopeManager { */ private _bindEventEmitter( target: T, - scope?: unknown + scope: Context ): T { const ee = (target as unknown) as PatchedEventEmitter; if (ee.__ot_listeners !== undefined) return target; @@ -205,7 +207,7 @@ export class AsyncHooksScopeManager implements ScopeManager { private _patchAddListener( ee: PatchedEventEmitter, original: Function, - scope?: unknown + scope: Context ) { const scopeManager = this; return function(this: {}, event: string, listener: Func) { diff --git a/packages/opentelemetry-scope-async-hooks/test/AsyncHooksScopeManager.test.ts b/packages/opentelemetry-scope-async-hooks/test/AsyncHooksScopeManager.test.ts index c5333786f4..6927810b6e 100644 --- a/packages/opentelemetry-scope-async-hooks/test/AsyncHooksScopeManager.test.ts +++ b/packages/opentelemetry-scope-async-hooks/test/AsyncHooksScopeManager.test.ts @@ -17,6 +17,7 @@ import * as assert from 'assert'; import { AsyncHooksScopeManager } from '../src'; import { EventEmitter } from 'events'; +import { Context } from '@opentelemetry/scope-base'; describe('AsyncHooksScopeManager', () => { let scopeManager: AsyncHooksScopeManager; @@ -50,11 +51,11 @@ describe('AsyncHooksScopeManager', () => { describe('.with()', () => { it('should run the callback (null as target)', done => { - scopeManager.with(null, done); + scopeManager.with(Context.ROOT_CONTEXT, done); }); it('should run the callback (object as target)', done => { - const test = { a: 1 }; + const test = Context.ROOT_CONTEXT.setValue('a', 1); scopeManager.with(test, () => { assert.strictEqual(scopeManager.active(), test, 'should have scope'); return done(); @@ -63,7 +64,7 @@ describe('AsyncHooksScopeManager', () => { it('should run the callback (when disabled)', done => { scopeManager.disable(); - scopeManager.with(null, () => { + scopeManager.with(Context.ROOT_CONTEXT, () => { scopeManager.enable(); return done(); }); @@ -71,7 +72,7 @@ describe('AsyncHooksScopeManager', () => { it('should rethrow errors', done => { assert.throws(() => { - scopeManager.with(null, () => { + scopeManager.with(Context.ROOT_CONTEXT, () => { throw new Error('This should be rethrown'); }); }); @@ -79,14 +80,14 @@ describe('AsyncHooksScopeManager', () => { }); it('should finally restore an old scope', done => { - const scope1 = 'scope1'; - const scope2 = 'scope2'; + const scope1 = Context.ROOT_CONTEXT.setValue('name', 'scope1'); + const scope2 = Context.ROOT_CONTEXT.setValue('name', 'scope2'); scopeManager.with(scope1, () => { - assert.strictEqual(scopeManager.active(), 'scope1'); + assert.strictEqual(scopeManager.active(), scope1); scopeManager.with(scope2, () => { - assert.strictEqual(scopeManager.active(), 'scope2'); + assert.strictEqual(scopeManager.active(), scope2); }); - assert.strictEqual(scopeManager.active(), 'scope1'); + assert.strictEqual(scopeManager.active(), scope1); return done(); }); }); @@ -95,18 +96,24 @@ describe('AsyncHooksScopeManager', () => { describe('.bind(function)', () => { it('should return the same target (when enabled)', () => { const test = { a: 1 }; - assert.deepStrictEqual(scopeManager.bind(test), test); + assert.deepStrictEqual( + scopeManager.bind(test, Context.ROOT_CONTEXT), + test + ); }); it('should return the same target (when disabled)', () => { scopeManager.disable(); const test = { a: 1 }; - assert.deepStrictEqual(scopeManager.bind(test), test); + assert.deepStrictEqual( + scopeManager.bind(test, Context.ROOT_CONTEXT), + test + ); scopeManager.enable(); }); it('should return current scope (when enabled)', done => { - const scope = { a: 1 }; + const scope = Context.ROOT_CONTEXT.setValue('a', 1); const fn = scopeManager.bind(() => { assert.strictEqual(scopeManager.active(), scope, 'should have scope'); return done(); @@ -120,7 +127,7 @@ describe('AsyncHooksScopeManager', () => { */ it('should return current scope (when disabled)', done => { scopeManager.disable(); - const scope = { a: 1 }; + const scope = Context.ROOT_CONTEXT.setValue('a', 1); const fn = scopeManager.bind(() => { assert.strictEqual(scopeManager.active(), scope, 'should have scope'); return done(); @@ -130,12 +137,12 @@ describe('AsyncHooksScopeManager', () => { it('should fail to return current scope (when disabled + async op)', done => { scopeManager.disable(); - const scope = { a: 1 }; + const scope = Context.ROOT_CONTEXT.setValue('a', 1); const fn = scopeManager.bind(() => { setTimeout(() => { assert.strictEqual( scopeManager.active(), - undefined, + Context.ROOT_CONTEXT, 'should have no scope' ); return done(); @@ -146,7 +153,7 @@ describe('AsyncHooksScopeManager', () => { it('should return current scope (when re-enabled + async op)', done => { scopeManager.enable(); - const scope = { a: 1 }; + const scope = Context.ROOT_CONTEXT.setValue('a', 1); const fn = scopeManager.bind(() => { setTimeout(() => { assert.strictEqual(scopeManager.active(), scope, 'should have scope'); @@ -160,19 +167,19 @@ describe('AsyncHooksScopeManager', () => { describe('.bind(event-emitter)', () => { it('should return the same target (when enabled)', () => { const ee = new EventEmitter(); - assert.deepStrictEqual(scopeManager.bind(ee), ee); + assert.deepStrictEqual(scopeManager.bind(ee, Context.ROOT_CONTEXT), ee); }); it('should return the same target (when disabled)', () => { const ee = new EventEmitter(); scopeManager.disable(); - assert.deepStrictEqual(scopeManager.bind(ee), ee); + assert.deepStrictEqual(scopeManager.bind(ee, Context.ROOT_CONTEXT), ee); scopeManager.enable(); }); it('should return current scope and removeListener (when enabled)', done => { const ee = new EventEmitter(); - const scope = { a: 2 }; + const scope = Context.ROOT_CONTEXT.setValue('a', 1); const patchedEe = scopeManager.bind(ee, scope); const handler = () => { assert.deepStrictEqual(scopeManager.active(), scope); @@ -187,7 +194,7 @@ describe('AsyncHooksScopeManager', () => { it('should return current scope and removeAllListener (when enabled)', done => { const ee = new EventEmitter(); - const scope = { a: 2 }; + const scope = Context.ROOT_CONTEXT.setValue('a', 1); const patchedEe = scopeManager.bind(ee, scope); const handler = () => { assert.deepStrictEqual(scopeManager.active(), scope); @@ -207,7 +214,7 @@ describe('AsyncHooksScopeManager', () => { it('should return scope (when disabled)', done => { scopeManager.disable(); const ee = new EventEmitter(); - const scope = { a: 2 }; + const scope = Context.ROOT_CONTEXT.setValue('a', 1); const patchedEe = scopeManager.bind(ee, scope); const handler = () => { assert.deepStrictEqual(scopeManager.active(), scope); @@ -224,11 +231,11 @@ describe('AsyncHooksScopeManager', () => { it('should not return current scope (when disabled + async op)', done => { scopeManager.disable(); const ee = new EventEmitter(); - const scope = { a: 3 }; + const scope = Context.ROOT_CONTEXT.setValue('a', 1); const patchedEe = scopeManager.bind(ee, scope); const handler = () => { setImmediate(() => { - assert.deepStrictEqual(scopeManager.active(), undefined); + assert.deepStrictEqual(scopeManager.active(), Context.ROOT_CONTEXT); patchedEe.removeAllListeners('test'); assert.strictEqual(patchedEe.listeners('test').length, 0); return done(); @@ -242,7 +249,7 @@ describe('AsyncHooksScopeManager', () => { it('should return current scope (when enabled + async op)', done => { scopeManager.enable(); const ee = new EventEmitter(); - const scope = { a: 3 }; + const scope = Context.ROOT_CONTEXT.setValue('a', 1); const patchedEe = scopeManager.bind(ee, scope); const handler = () => { setImmediate(() => { diff --git a/packages/opentelemetry-scope-base/src/NoopScopeManager.ts b/packages/opentelemetry-scope-base/src/NoopScopeManager.ts index ac32db311f..027f0a2e14 100644 --- a/packages/opentelemetry-scope-base/src/NoopScopeManager.ts +++ b/packages/opentelemetry-scope-base/src/NoopScopeManager.ts @@ -15,20 +15,21 @@ */ import * as types from './types'; +import { Context } from './context'; export class NoopScopeManager implements types.ScopeManager { - active(): unknown { - return undefined; + active(): Context { + return Context.ROOT_CONTEXT; } with ReturnType>( - scope: unknown, + scope: Context, fn: T ): ReturnType { return fn(); } - bind(target: T, scope?: unknown): T { + bind(target: T, scope?: Context): T { return target; } diff --git a/packages/opentelemetry-scope-base/src/context.ts b/packages/opentelemetry-scope-base/src/context.ts new file mode 100644 index 0000000000..6b413e88b2 --- /dev/null +++ b/packages/opentelemetry-scope-base/src/context.ts @@ -0,0 +1,74 @@ +/*! + * 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. + */ + +/** Map of identifiers to an unknown value used internally to store context */ +type Store = { [identifer: string]: unknown }; + +/** + * Class which stores and manages current context values. All methods which + * update context such as get and delete do not modify an existing context, + * but create a new one with updated values. + */ +export class Context { + private _context: Store; + + public static readonly ROOT_CONTEXT = new Context(); + + /** + * Construct a new context which inherits values from an optional parent context. + * + * @param context a context from which to inherit values + */ + protected constructor(context: Context | Store = {}) { + const storage = context instanceof Context ? context._context : context; + this._context = Object.assign(Object.create(null), storage); + } + + /** + * Get a value from the context. + * + * @param key key which identifies a context value + */ + getValue(key: string): unknown { + return this._context[key]; + } + + /** + * Create a new context which inherits from this context and has + * the given key set to the given value. + * + * @param key context key for which to set the value + * @param value value to set for the given key + */ + setValue(key: string, value: unknown): Context { + return new Context({ + ...this._context, + [key]: value, + }); + } + + /** + * Return a new context which inherits from this context but does + * not contain a value for the given key. + * + * @param key context key for which to clear a value + */ + deleteValue(key: string): Context { + const context = new Context(this); + delete context._context[key]; + return context; + } +} diff --git a/packages/opentelemetry-scope-base/src/index.ts b/packages/opentelemetry-scope-base/src/index.ts index 46f028acc9..3f0c230358 100644 --- a/packages/opentelemetry-scope-base/src/index.ts +++ b/packages/opentelemetry-scope-base/src/index.ts @@ -15,4 +15,5 @@ */ export * from './types'; +export * from './context'; export * from './NoopScopeManager'; diff --git a/packages/opentelemetry-scope-base/src/types.ts b/packages/opentelemetry-scope-base/src/types.ts index 4071603c91..9bba83b504 100644 --- a/packages/opentelemetry-scope-base/src/types.ts +++ b/packages/opentelemetry-scope-base/src/types.ts @@ -14,11 +14,13 @@ * limitations under the License. */ +import { Context } from './context'; + export interface ScopeManager { /** * Get the current active scope */ - active(): unknown; + active(): Context; /** * Run the fn callback with object set as the current active scope @@ -26,7 +28,7 @@ export interface ScopeManager { * @param fn A callback to be immediately run within a specific scope */ with ReturnType>( - scope: unknown, + scope: Context, fn: T ): ReturnType; @@ -35,7 +37,7 @@ export interface ScopeManager { * @param target Any object to which a scope need to be set * @param [scope] Optionally specify the scope which you want to assign */ - bind(target: T, scope?: unknown): T; + bind(target: T, scope?: Context): T; /** * Enable scope management diff --git a/packages/opentelemetry-scope-base/test/NoopScopeManager.test.ts b/packages/opentelemetry-scope-base/test/NoopScopeManager.test.ts index 4767f5793f..5a9245b666 100644 --- a/packages/opentelemetry-scope-base/test/NoopScopeManager.test.ts +++ b/packages/opentelemetry-scope-base/test/NoopScopeManager.test.ts @@ -15,7 +15,7 @@ */ import * as assert from 'assert'; -import { NoopScopeManager } from '../src'; +import { NoopScopeManager, Context } from '../src'; describe('NoopScopeManager', () => { let scopeManager: NoopScopeManager; @@ -39,16 +39,16 @@ describe('NoopScopeManager', () => { }); describe('.with()', () => { - it('should run the callback (null as target)', done => { - scopeManager.with(null, done); + it('should run the callback (Context.ROOT_CONTEXT as target)', done => { + scopeManager.with(Context.ROOT_CONTEXT, done); }); it('should run the callback (object as target)', done => { - const test = { a: 1 }; + const test = Context.ROOT_CONTEXT.setValue('a', 1); scopeManager.with(test, () => { assert.strictEqual( scopeManager.active(), - undefined, + Context.ROOT_CONTEXT, 'should not have scope' ); return done(); @@ -57,7 +57,7 @@ describe('NoopScopeManager', () => { it('should run the callback (when disabled)', done => { scopeManager.disable(); - scopeManager.with(null, () => { + scopeManager.with(Context.ROOT_CONTEXT, () => { scopeManager.enable(); return done(); }); @@ -65,19 +65,19 @@ describe('NoopScopeManager', () => { }); describe('.active()', () => { - it('should always return null (when enabled)', () => { + it('should always return Context.ROOT_CONTEXT (when enabled)', () => { assert.strictEqual( scopeManager.active(), - undefined, + Context.ROOT_CONTEXT, 'should not have scope' ); }); - it('should always return null (when disabled)', () => { + it('should always return Context.ROOT_CONTEXT (when disabled)', () => { scopeManager.disable(); assert.strictEqual( scopeManager.active(), - undefined, + Context.ROOT_CONTEXT, 'should not have scope' ); scopeManager.enable(); diff --git a/packages/opentelemetry-scope-zone-peer-dep/src/ZoneScopeManager.ts b/packages/opentelemetry-scope-zone-peer-dep/src/ZoneScopeManager.ts index d735e73c7d..71b3c6be45 100644 --- a/packages/opentelemetry-scope-zone-peer-dep/src/ZoneScopeManager.ts +++ b/packages/opentelemetry-scope-zone-peer-dep/src/ZoneScopeManager.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { ScopeManager } from '@opentelemetry/scope-base'; +import { ScopeManager, Context } from '@opentelemetry/scope-base'; import { Func, TargetWithEvents } from './types'; import { isListenerObject } from './util'; @@ -45,20 +45,20 @@ export class ZoneScopeManager implements ScopeManager { * Returns the active scope from certain zone name * @param activeZone */ - private _activeScopeFromZone( - activeZone: Zone | undefined - ): unknown | undefined { - return activeZone && activeZone.get(ZONE_SCOPE_KEY); + private _activeScopeFromZone(activeZone: Zone | undefined): Context { + return ( + (activeZone && activeZone.get(ZONE_SCOPE_KEY)) || Context.ROOT_CONTEXT + ); } /** * @param target Function to be executed within the scope * @param scope A scope (span) to be executed within target function */ - private _bindFunction(target: T, scope?: unknown): T { + private _bindFunction(target: T, scope: Context): T { const manager = this; const contextWrapper = function(this: any, ...args: unknown[]) { - return manager.with(scope, () => target.apply(this || scope, args)); + return manager.with(scope, () => target.apply(this, args)); }; Object.defineProperty(contextWrapper, 'length', { enumerable: false, @@ -73,7 +73,7 @@ export class ZoneScopeManager implements ScopeManager { * @param obj target object on which the listeners will be patched * @param scope A scope (span) to be bind to target */ - private _bindListener(obj: T, scope?: unknown): T { + private _bindListener(obj: T, scope: Context): T { const target = (obj as unknown) as TargetWithEvents; if (target.__ot_listeners !== undefined) { return obj; @@ -137,7 +137,7 @@ export class ZoneScopeManager implements ScopeManager { private _patchAddEventListener( target: TargetWithEvents, original: Function, - scope?: unknown + scope: Context ) { const scopeManager = this; @@ -183,17 +183,18 @@ export class ZoneScopeManager implements ScopeManager { /** * Returns the active scope */ - active(): unknown | undefined { + active(): Context { + if (!this._enabled) { + return Context.ROOT_CONTEXT; + } const activeZone = this._getActiveZone(); const active = this._activeScopeFromZone(activeZone); if (active) { return active; } - if (this._enabled) { - return window; - } - return undefined; + + return Context.ROOT_CONTEXT; } /** @@ -201,7 +202,7 @@ export class ZoneScopeManager implements ScopeManager { * @param target * @param scope A scope (span) to be bind to target */ - bind(target: T | TargetWithEvents, scope?: unknown): T { + bind(target: T | TargetWithEvents, scope: Context): T { // if no specific scope to propagate is given, we use the current one if (scope === undefined) { scope = this.active(); @@ -226,9 +227,6 @@ export class ZoneScopeManager implements ScopeManager { * Enables the scope manager and creates a default(root) scope */ enable(): this { - if (this._enabled) { - return this; - } this._enabled = true; return this; } @@ -241,14 +239,9 @@ export class ZoneScopeManager implements ScopeManager { * @param fn Callback function */ with ReturnType>( - scope: unknown, + scope: Context | null, fn: () => ReturnType ): ReturnType { - // if no scope use active from active zone - if (typeof scope === 'undefined' || scope === null) { - scope = this.active(); - } - const zoneName = this._createZoneName(); const newZone = this._createZone(zoneName, scope); diff --git a/packages/opentelemetry-scope-zone-peer-dep/test/ZoneScopeManager.test.ts b/packages/opentelemetry-scope-zone-peer-dep/test/ZoneScopeManager.test.ts index 490d5bfe45..8973c7e06e 100644 --- a/packages/opentelemetry-scope-zone-peer-dep/test/ZoneScopeManager.test.ts +++ b/packages/opentelemetry-scope-zone-peer-dep/test/ZoneScopeManager.test.ts @@ -18,6 +18,7 @@ import 'zone.js'; import * as sinon from 'sinon'; import * as assert from 'assert'; import { ZoneScopeManager } from '../src'; +import { Context } from '@opentelemetry/scope-base'; let clock: any; @@ -37,18 +38,27 @@ describe('ZoneScopeManager', () => { describe('.enable()', () => { it('should work', () => { + const ctx = Context.ROOT_CONTEXT.setValue('a', 1); assert.doesNotThrow(() => { assert(scopeManager.enable() === scopeManager, 'should return this'); - assert(scopeManager.active() === window, 'should has root scope'); + scopeManager.with(ctx, () => { + assert(scopeManager.active() === ctx, 'should has root scope'); + }); }); }); }); describe('.disable()', () => { it('should work', () => { + const ctx = Context.ROOT_CONTEXT.setValue('a', 1); assert.doesNotThrow(() => { assert(scopeManager.disable() === scopeManager, 'should return this'); - assert(scopeManager.active() === undefined, 'should has no scope'); + scopeManager.with(ctx, () => { + assert( + scopeManager.active() === Context.ROOT_CONTEXT, + 'should has root scope' + ); + }); }); }); }); @@ -59,7 +69,7 @@ describe('ZoneScopeManager', () => { }); it('should run the callback (object as target)', done => { - const test = { a: 1 }; + const test = Context.ROOT_CONTEXT.setValue('a', 1); scopeManager.with(test, () => { assert.strictEqual(scopeManager.active(), test, 'should have scope'); return done(); @@ -84,22 +94,22 @@ describe('ZoneScopeManager', () => { }); it('should finally restore an old scope, including the async task', done => { - const scope1 = 'scope1'; - const scope2 = 'scope2'; - const scope3 = 'scope3'; + const scope1 = Context.ROOT_CONTEXT.setValue('scope', 'scope1'); + const scope2 = Context.ROOT_CONTEXT.setValue('scope', 'scope2'); + const scope3 = Context.ROOT_CONTEXT.setValue('scope', 'scope3'); scopeManager.with(scope1, () => { - assert.strictEqual(scopeManager.active(), 'scope1'); + assert.strictEqual(scopeManager.active(), scope1); scopeManager.with(scope2, () => { - assert.strictEqual(scopeManager.active(), 'scope2'); + assert.strictEqual(scopeManager.active(), scope2); scopeManager.with(scope3, () => { - assert.strictEqual(scopeManager.active(), 'scope3'); + assert.strictEqual(scopeManager.active(), scope3); }); - assert.strictEqual(scopeManager.active(), 'scope2'); + assert.strictEqual(scopeManager.active(), scope2); }); - assert.strictEqual(scopeManager.active(), 'scope1'); + assert.strictEqual(scopeManager.active(), scope1); setTimeout(() => { - assert.strictEqual(scopeManager.active(), 'scope1'); + assert.strictEqual(scopeManager.active(), scope1); done(); }, 500); clock.tick(500); @@ -108,9 +118,9 @@ describe('ZoneScopeManager', () => { }); it('should finally restore an old scope when scope is an object, including the async task', done => { - const scope1 = { a: 1 }; - const scope2 = { a: 2 }; - const scope3 = { a: 3 }; + const scope1 = Context.ROOT_CONTEXT.setValue('x', 1); + const scope2 = Context.ROOT_CONTEXT.setValue('x', 2); + const scope3 = Context.ROOT_CONTEXT.setValue('x', 3); scopeManager.with(scope1, () => { assert.strictEqual(scopeManager.active(), scope1); scopeManager.with(scope2, () => { @@ -130,20 +140,29 @@ describe('ZoneScopeManager', () => { assert.strictEqual(scopeManager.active(), window); }); it('should correctly return the scopes for 3 parallel actions', () => { - const rootSpan = { name: 'rootSpan' }; + const rootSpan = Context.ROOT_CONTEXT.setValue('name', 'root'); scopeManager.with(rootSpan, () => { assert.ok( - scopeManager.active() === rootSpan, + scopeManager.active().getValue('name') === 'root', 'Current span is rootSpan' ); - const concurrentSpan1 = { name: 'concurrentSpan1' }; - const concurrentSpan2 = { name: 'concurrentSpan2' }; - const concurrentSpan3 = { name: 'concurrentSpan3' }; + const concurrentSpan1 = Context.ROOT_CONTEXT.setValue( + 'span', + 'concurrentSpan1' + ); + const concurrentSpan2 = Context.ROOT_CONTEXT.setValue( + 'span', + 'concurrentSpan2' + ); + const concurrentSpan3 = Context.ROOT_CONTEXT.setValue( + 'span', + 'concurrentSpan3' + ); scopeManager.with(concurrentSpan1, () => { setTimeout(() => { assert.ok( - scopeManager.active() === concurrentSpan1, + scopeManager.active().getValue('span') === concurrentSpan1, 'Current span is concurrentSpan1' ); }, 10); @@ -152,7 +171,7 @@ describe('ZoneScopeManager', () => { scopeManager.with(concurrentSpan2, () => { setTimeout(() => { assert.ok( - scopeManager.active() === concurrentSpan2, + scopeManager.active().getValue('span') === concurrentSpan2, 'Current span is concurrentSpan2' ); }, 20); @@ -161,7 +180,7 @@ describe('ZoneScopeManager', () => { scopeManager.with(concurrentSpan3, () => { setTimeout(() => { assert.ok( - scopeManager.active() === concurrentSpan3, + scopeManager.active().getValue('span') === concurrentSpan3, 'Current span is concurrentSpan3' ); }, 30); @@ -180,31 +199,38 @@ describe('ZoneScopeManager', () => { } getTitle() { - return this.title; + return (scopeManager.active().getValue('obj') as Obj).title; } } const obj1 = new Obj('a1'); + const ctx = Context.ROOT_CONTEXT.setValue('obj', obj1); obj1.title = 'a2'; const obj2 = new Obj('b1'); - const wrapper: any = scopeManager.bind(obj2.getTitle, obj1); + const wrapper: any = scopeManager.bind(obj2.getTitle, ctx); assert.ok(wrapper(), 'a2'); }); it('should return the same target (when enabled)', () => { const test = { a: 1 }; - assert.deepStrictEqual(scopeManager.bind(test), test); + assert.deepStrictEqual( + scopeManager.bind(test, Context.ROOT_CONTEXT), + test + ); }); it('should return the same target (when disabled)', () => { scopeManager.disable(); const test = { a: 1 }; - assert.deepStrictEqual(scopeManager.bind(test), test); + assert.deepStrictEqual( + scopeManager.bind(test, Context.ROOT_CONTEXT), + test + ); scopeManager.enable(); }); it('should return current scope (when enabled)', done => { - const scope = { a: 1 }; + const scope = Context.ROOT_CONTEXT.setValue('ctx', { a: 1 }); const fn: any = scopeManager.bind(() => { assert.strictEqual(scopeManager.active(), scope, 'should have scope'); return done(); @@ -212,18 +238,22 @@ describe('ZoneScopeManager', () => { fn(); }); - it('should return current scope (when disabled)', done => { + it('should return root scope (when disabled)', done => { scopeManager.disable(); - const scope = { a: 1 }; + const scope = Context.ROOT_CONTEXT.setValue('ctx', { a: 1 }); const fn: any = scopeManager.bind(() => { - assert.strictEqual(scopeManager.active(), scope, 'should have scope'); + assert.strictEqual( + scopeManager.active(), + Context.ROOT_CONTEXT, + 'should have scope' + ); return done(); }, scope); fn(); }); it('should bind the the certain scope to the target "addEventListener" function', done => { - const scope1 = { a: 1 }; + const scope1 = Context.ROOT_CONTEXT.setValue('a', 1); const element = document.createElement('div'); scopeManager.bind(element, scope1); @@ -247,7 +277,7 @@ describe('ZoneScopeManager', () => { }); it('should preserve zone when creating new click event inside zone', done => { - const scope1 = { a: 1 }; + const scope1 = Context.ROOT_CONTEXT.setValue('a', 1); const element = document.createElement('div'); scopeManager.bind(element, scope1); diff --git a/packages/opentelemetry-shim-opentracing/src/shim.ts b/packages/opentelemetry-shim-opentracing/src/shim.ts index 778f440c57..7fdf9b1044 100644 --- a/packages/opentelemetry-shim-opentracing/src/shim.ts +++ b/packages/opentelemetry-shim-opentracing/src/shim.ts @@ -15,7 +15,8 @@ */ import * as types from '@opentelemetry/api'; -import { NoopLogger } from '@opentelemetry/core'; +import { Carrier } from '@opentelemetry/api'; +import { Context, NoopLogger } from '@opentelemetry/core'; import * as opentracing from 'opentracing'; function translateReferences( @@ -120,16 +121,23 @@ export class TracerShim extends opentracing.Tracer { _inject( spanContext: opentracing.SpanContext, format: string, - carrier: unknown + carrier: Carrier ): void { const opentelemSpanContext: types.SpanContext = (spanContext as SpanContextShim).getSpanContext(); + if (!carrier || typeof carrier !== 'object') return; switch (format) { // tslint:disable-next-line:no-switch-case-fall-through case opentracing.FORMAT_HTTP_HEADERS: case opentracing.FORMAT_TEXT_MAP: this._tracer .getHttpTextFormat() - .inject(opentelemSpanContext, format, carrier); + .inject( + Context.setExtractedSpanContext( + Context.ROOT_CONTEXT, + opentelemSpanContext + ), + carrier + ); return; case opentracing.FORMAT_BINARY: this._logger.warn( @@ -141,14 +149,16 @@ export class TracerShim extends opentracing.Tracer { } } - _extract(format: string, carrier: unknown): opentracing.SpanContext | null { + _extract(format: string, carrier: Carrier): opentracing.SpanContext | null { switch (format) { // tslint:disable-next-line:no-switch-case-fall-through case opentracing.FORMAT_HTTP_HEADERS: case opentracing.FORMAT_TEXT_MAP: - const context = this._tracer - .getHttpTextFormat() - .extract(format, carrier); + const context = Context.getExtractedSpanContext( + this._tracer + .getHttpTextFormat() + .extract(Context.ROOT_CONTEXT, carrier) + ); if (!context) { return null; } diff --git a/packages/opentelemetry-tracing/src/Tracer.ts b/packages/opentelemetry-tracing/src/Tracer.ts index baf4a375b3..3cccbfce04 100644 --- a/packages/opentelemetry-tracing/src/Tracer.ts +++ b/packages/opentelemetry-tracing/src/Tracer.ts @@ -21,6 +21,7 @@ import { randomSpanId, NoRecordingSpan, ConsoleLogger, + Context, } from '@opentelemetry/core'; import { TracerConfig, TraceParams } from './types'; import { ScopeManager } from '@opentelemetry/scope-base'; @@ -106,16 +107,11 @@ export class Tracer implements types.Tracer { /** * Returns the current Span from the current context. * - * If there is no Span associated with the current context, null is returned. + * If there is no Span associated with the current context, undefined is returned. */ getCurrentSpan(): types.Span | undefined { // Get the current Span from the context or null if none found. - const current = this._scopeManager.active(); - if (current === null || current === undefined) { - return; - } else { - return current as types.Span; - } + return Context.getActiveSpan(this._scopeManager.active()); } /** @@ -126,14 +122,22 @@ export class Tracer implements types.Tracer { fn: T ): ReturnType { // Set given span to context. - return this._scopeManager.with(span, fn); + return this._scopeManager.with( + Context.setActiveSpan(this._scopeManager.active(), span), + fn + ); } /** * Bind a span (or the current one) to the target's scope */ bind(target: T, span?: types.Span): T { - return this._scopeManager.bind(target, span); + return this._scopeManager.bind( + target, + span + ? Context.setActiveSpan(this._scopeManager.active(), span) + : this._scopeManager.active() + ); } /** diff --git a/packages/opentelemetry-tracing/test/BasicTracerRegistry.test.ts b/packages/opentelemetry-tracing/test/BasicTracerRegistry.test.ts index ad29673636..7f8e8047ce 100644 --- a/packages/opentelemetry-tracing/test/BasicTracerRegistry.test.ts +++ b/packages/opentelemetry-tracing/test/BasicTracerRegistry.test.ts @@ -14,9 +14,11 @@ * limitations under the License. */ +import { TraceFlags } from '@opentelemetry/api'; import { ALWAYS_SAMPLER, BinaryTraceContext, + Context, HttpTraceContext, NEVER_SAMPLER, NoopLogger, @@ -24,7 +26,6 @@ import { TraceState, } from '@opentelemetry/core'; import { NoopScopeManager, ScopeManager } from '@opentelemetry/scope-base'; -import { TraceFlags } from '@opentelemetry/api'; import * as assert from 'assert'; import { BasicTracerProvider, Span } from '../src'; @@ -315,7 +316,8 @@ describe('BasicTracerProvider', () => { it('should return current span when it exists', () => { const tracer = new BasicTracerProvider({ scopeManager: { - active: () => 'foo', + active: () => + Context.setActiveSpan(Context.ROOT_CONTEXT, ('foo' as any) as Span), } as ScopeManager, }).getTracer('default'); assert.deepStrictEqual(tracer.getCurrentSpan(), 'foo'); diff --git a/packages/opentelemetry-web/src/StackScopeManager.ts b/packages/opentelemetry-web/src/StackScopeManager.ts index c6dc866c20..5fe39459e8 100644 --- a/packages/opentelemetry-web/src/StackScopeManager.ts +++ b/packages/opentelemetry-web/src/StackScopeManager.ts @@ -15,6 +15,7 @@ */ import { ScopeManager } from '@opentelemetry/scope-base'; +import { Context } from '@opentelemetry/core'; /** * Stack Scope Manager for managing the state in web @@ -29,14 +30,17 @@ export class StackScopeManager implements ScopeManager { /** * Keeps the reference to current scope */ - public _currentScope: unknown; + public _currentScope = Context.ROOT_CONTEXT; /** * * @param target Function to be executed within the scope * @param scope */ - private _bindFunction(target: T, scope?: unknown): T { + private _bindFunction( + target: T, + scope = Context.ROOT_CONTEXT + ): T { const manager = this; const contextWrapper = function(...args: unknown[]) { return manager.with(scope, () => target.apply(scope, args)); @@ -53,7 +57,7 @@ export class StackScopeManager implements ScopeManager { /** * Returns the active scope */ - active(): unknown { + active(): Context { return this._currentScope; } @@ -62,7 +66,7 @@ export class StackScopeManager implements ScopeManager { * @param target * @param scope */ - bind(target: T, scope?: unknown): T { + bind(target: T, scope = Context.ROOT_CONTEXT): T { // if no specific scope to propagate is given, we use the current one if (scope === undefined) { scope = this.active(); @@ -77,7 +81,7 @@ export class StackScopeManager implements ScopeManager { * Disable the scope manager (clears the current scope) */ disable(): this { - this._currentScope = undefined; + this._currentScope = Context.ROOT_CONTEXT; this._enabled = false; return this; } @@ -90,7 +94,7 @@ export class StackScopeManager implements ScopeManager { return this; } this._enabled = true; - this._currentScope = window; + this._currentScope = Context.ROOT_CONTEXT; return this; } @@ -101,15 +105,11 @@ export class StackScopeManager implements ScopeManager { * @param fn Callback function */ with ReturnType>( - scope: unknown, + scope: Context | null, fn: () => ReturnType ): ReturnType { - if (typeof scope === 'undefined' || scope === null) { - scope = window; - } - const previousScope = this._currentScope; - this._currentScope = scope; + this._currentScope = scope || Context.ROOT_CONTEXT; try { return fn.apply(scope); diff --git a/packages/opentelemetry-web/test/StackScopeManager.test.ts b/packages/opentelemetry-web/test/StackScopeManager.test.ts index 781dfa87f3..680334a591 100644 --- a/packages/opentelemetry-web/test/StackScopeManager.test.ts +++ b/packages/opentelemetry-web/test/StackScopeManager.test.ts @@ -16,6 +16,7 @@ import * as assert from 'assert'; import { StackScopeManager } from '../src'; +import { Context } from '@opentelemetry/core'; describe('StackScopeManager', () => { let scopeManager: StackScopeManager; @@ -33,7 +34,10 @@ describe('StackScopeManager', () => { it('should work', () => { assert.doesNotThrow(() => { assert(scopeManager.enable() === scopeManager, 'should return this'); - assert(scopeManager.active() === window, 'should has root scope'); + assert( + scopeManager.active() === Context.ROOT_CONTEXT, + 'should has root scope' + ); }); }); }); @@ -42,7 +46,10 @@ describe('StackScopeManager', () => { it('should work', () => { assert.doesNotThrow(() => { assert(scopeManager.disable() === scopeManager, 'should return this'); - assert(scopeManager.active() === undefined, 'should has no scope'); + assert( + scopeManager.active() === Context.ROOT_CONTEXT, + 'should has no scope' + ); }); }); }); @@ -53,7 +60,7 @@ describe('StackScopeManager', () => { }); it('should run the callback (object as target)', done => { - const test = { a: 1 }; + const test = Context.ROOT_CONTEXT.setValue('a', 1); scopeManager.with(test, () => { assert.strictEqual(scopeManager.active(), test, 'should have scope'); return done(); @@ -78,28 +85,28 @@ describe('StackScopeManager', () => { }); it('should finally restore an old scope', done => { - const scope1 = 'scope1'; - const scope2 = 'scope2'; - const scope3 = 'scope3'; + const scope1 = Context.ROOT_CONTEXT.setValue('name', 'scope1'); + const scope2 = Context.ROOT_CONTEXT.setValue('name', 'scope2'); + const scope3 = Context.ROOT_CONTEXT.setValue('name', 'scope3'); scopeManager.with(scope1, () => { - assert.strictEqual(scopeManager.active(), 'scope1'); + assert.strictEqual(scopeManager.active(), scope1); scopeManager.with(scope2, () => { - assert.strictEqual(scopeManager.active(), 'scope2'); + assert.strictEqual(scopeManager.active(), scope2); scopeManager.with(scope3, () => { - assert.strictEqual(scopeManager.active(), 'scope3'); + assert.strictEqual(scopeManager.active(), scope3); }); - assert.strictEqual(scopeManager.active(), 'scope2'); + assert.strictEqual(scopeManager.active(), scope2); }); - assert.strictEqual(scopeManager.active(), 'scope1'); + assert.strictEqual(scopeManager.active(), scope1); return done(); }); assert.strictEqual(scopeManager.active(), window); }); it('should finally restore an old scope when scope is an object', done => { - const scope1 = { a: 1 }; - const scope2 = { a: 2 }; - const scope3 = { a: 3 }; + const scope1 = Context.ROOT_CONTEXT.setValue('a', 1); + const scope2 = Context.ROOT_CONTEXT.setValue('a', 2); + const scope3 = Context.ROOT_CONTEXT.setValue('a', 3); scopeManager.with(scope1, () => { assert.strictEqual(scopeManager.active(), scope1); scopeManager.with(scope2, () => { @@ -126,31 +133,32 @@ describe('StackScopeManager', () => { } getTitle() { - return this.title; + return (scopeManager.active().getValue('obj') as Obj).title; } } const obj1 = new Obj('a1'); + const ctx = Context.ROOT_CONTEXT.setValue('obj', obj1); obj1.title = 'a2'; const obj2 = new Obj('b1'); - const wrapper: any = scopeManager.bind(obj2.getTitle, obj1); + const wrapper: any = scopeManager.bind(obj2.getTitle, ctx); assert.ok(wrapper(), 'a2'); }); it('should return the same target (when enabled)', () => { - const test = { a: 1 }; + const test = Context.ROOT_CONTEXT.setValue('a', 1); assert.deepStrictEqual(scopeManager.bind(test), test); }); it('should return the same target (when disabled)', () => { scopeManager.disable(); - const test = { a: 1 }; + const test = Context.ROOT_CONTEXT.setValue('a', 1); assert.deepStrictEqual(scopeManager.bind(test), test); scopeManager.enable(); }); it('should return current scope (when enabled)', done => { - const scope = { a: 1 }; + const scope = Context.ROOT_CONTEXT.setValue('a', 1); const fn: any = scopeManager.bind(() => { assert.strictEqual(scopeManager.active(), scope, 'should have scope'); return done(); @@ -160,7 +168,7 @@ describe('StackScopeManager', () => { it('should return current scope (when disabled)', done => { scopeManager.disable(); - const scope = { a: 1 }; + const scope = Context.ROOT_CONTEXT.setValue('a', 1); const fn: any = scopeManager.bind(() => { assert.strictEqual(scopeManager.active(), scope, 'should have scope'); return done(); From 5a03016af52dd849aaf956e355c76cfe04287fb5 Mon Sep 17 00:00:00 2001 From: Daniel Dyla Date: Thu, 6 Feb 2020 10:30:19 -0500 Subject: [PATCH 02/23] chore: fix api dependencies on builds --- .circleci/config.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 800faeed2d..264a16d65a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -165,6 +165,9 @@ jobs: - run: name: Install minimal doc and lint modules globally command: npm i -g tslint lerna typedoc linkinator typescript gts tslint-consistent-codestyle tslint-microsoft-contrib + - run: + name: Install API dependencies + command: lerna bootstrap --scope @opentelemetry/api --include-filtered-dependencies - run: name: Symlink global modules into all lerna packages command: lerna exec 'npm link tslint lerna typedoc linkinator typescript gts tslint-consistent-codestyle tslint-microsoft-contrib' From 04fb334828709da3ed27066e6563f3e1ddf99d09 Mon Sep 17 00:00:00 2001 From: Daniel Dyla Date: Thu, 6 Feb 2020 10:38:49 -0500 Subject: [PATCH 03/23] chore: use bootstrap for doc deps --- .circleci/config.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 264a16d65a..39b402a342 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -164,13 +164,10 @@ jobs: - checkout - run: name: Install minimal doc and lint modules globally - command: npm i -g tslint lerna typedoc linkinator typescript gts tslint-consistent-codestyle tslint-microsoft-contrib + command: npm i -g lerna - run: name: Install API dependencies command: lerna bootstrap --scope @opentelemetry/api --include-filtered-dependencies - - run: - name: Symlink global modules into all lerna packages - command: lerna exec 'npm link tslint lerna typedoc linkinator typescript gts tslint-consistent-codestyle tslint-microsoft-contrib' - run: name: Check code style and linting command: npm run check From 33912072f6dd99ba14f6bc35ddbdaef602515fdc Mon Sep 17 00:00:00 2001 From: Daniel Dyla Date: Thu, 6 Feb 2020 10:54:47 -0500 Subject: [PATCH 04/23] chore: allow builds as root in docs --- .circleci/config.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 39b402a342..2ef49b3c10 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -160,6 +160,8 @@ jobs: lint_&_docs: docker: - image: node:12 + environment: + NPM_CONFIG_UNSAFE_PERM: true steps: - checkout - run: @@ -167,7 +169,7 @@ jobs: command: npm i -g lerna - run: name: Install API dependencies - command: lerna bootstrap --scope @opentelemetry/api --include-filtered-dependencies + command: lerna bootstrap --no-ci -- --only=dev - run: name: Check code style and linting command: npm run check From 8467706bf2a43bc1d703216357937904c21db9b6 Mon Sep 17 00:00:00 2001 From: Daniel Dyla Date: Thu, 6 Feb 2020 11:05:06 -0500 Subject: [PATCH 05/23] chore: bootstrap docs after checking code --- .circleci/config.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 2ef49b3c10..140a524a71 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -160,19 +160,20 @@ jobs: lint_&_docs: docker: - image: node:12 - environment: - NPM_CONFIG_UNSAFE_PERM: true steps: - checkout - run: name: Install minimal doc and lint modules globally - command: npm i -g lerna + command: npm i -g tslint lerna typedoc linkinator typescript gts tslint-consistent-codestyle tslint-microsoft-contrib - run: - name: Install API dependencies - command: lerna bootstrap --no-ci -- --only=dev + name: Symlink global modules into all lerna packages + command: lerna exec 'npm link tslint lerna typedoc linkinator typescript gts tslint-consistent-codestyle tslint-microsoft-contrib' - run: name: Check code style and linting command: npm run check + - run: + name: Install API dependencies + command: lerna bootstrap --scope @opentelemetry/api --include-filtered-dependencies - run: name: Docs tests command: npm run docs-test From 707655328738b2a6578792721ca2a1ed2dde5e46 Mon Sep 17 00:00:00 2001 From: Daniel Dyla Date: Thu, 6 Feb 2020 11:08:27 -0500 Subject: [PATCH 06/23] chore: allow unsafe builds in docs --- .circleci/config.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 140a524a71..e04d9f22ba 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -160,6 +160,8 @@ jobs: lint_&_docs: docker: - image: node:12 + environment: + NPM_CONFIG_UNSAFE_PERM: true steps: - checkout - run: From 13180003dcb88cd2c923f454b8a295bbb0cf9802 Mon Sep 17 00:00:00 2001 From: Daniel Dyla Date: Thu, 6 Feb 2020 12:03:09 -0500 Subject: [PATCH 07/23] chore: fix api test build --- .../test/noop-implementations/noop-meter.test.ts | 2 -- packages/opentelemetry-api/tsconfig.json | 3 ++- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/opentelemetry-api/test/noop-implementations/noop-meter.test.ts b/packages/opentelemetry-api/test/noop-implementations/noop-meter.test.ts index 052854e82b..bbbd0643b3 100644 --- a/packages/opentelemetry-api/test/noop-implementations/noop-meter.test.ts +++ b/packages/opentelemetry-api/test/noop-implementations/noop-meter.test.ts @@ -50,10 +50,8 @@ describe('NoopMeter', () => { const measure = meter.createMeasure('some-name'); measure.getDefaultBound().record(1); - measure.getDefaultBound().record(1, { key: { value: 'value' } }); measure.getDefaultBound().record( 1, - { key: { value: 'value' } }, { traceId: 'a3cda95b652f4a1592b449d5929fda1b', spanId: '5e0c63257de34c92', diff --git a/packages/opentelemetry-api/tsconfig.json b/packages/opentelemetry-api/tsconfig.json index 2b05ed8413..df95d9cc57 100644 --- a/packages/opentelemetry-api/tsconfig.json +++ b/packages/opentelemetry-api/tsconfig.json @@ -5,7 +5,8 @@ "outDir": "build" }, "include": [ - "src/**/*.ts" + "src/**/*.ts", + "test/**/*.ts" ], "typedocOptions": { "name": "OpenTelemetry API for JavaScript", From 55338813d9569932ffa0c818e2b8ceb1a6689744 Mon Sep 17 00:00:00 2001 From: Daniel Dyla Date: Thu, 6 Feb 2020 12:06:40 -0500 Subject: [PATCH 08/23] chore: lint --- .../opentelemetry-api/test/api/api.test.ts | 20 +++++++++++-------- .../noop-implementations/noop-meter.test.ts | 14 +++++-------- .../noop-implementations/noop-span.test.ts | 8 +++++++- .../noop-implementations/noop-tracer.test.ts | 5 ++++- 4 files changed, 28 insertions(+), 19 deletions(-) diff --git a/packages/opentelemetry-api/test/api/api.test.ts b/packages/opentelemetry-api/test/api/api.test.ts index c0ecb9baf9..031fa6f976 100644 --- a/packages/opentelemetry-api/test/api/api.test.ts +++ b/packages/opentelemetry-api/test/api/api.test.ts @@ -15,7 +15,14 @@ */ import * as assert from 'assert'; -import api, { TraceFlags, NoopSpan, NoopTracerProvider, NoopTracer, SpanOptions, Span } from '../../src'; +import api, { + TraceFlags, + NoopSpan, + NoopTracerProvider, + NoopTracer, + SpanOptions, + Span, +} from '../../src'; describe('API', () => { const functions = [ @@ -27,7 +34,7 @@ describe('API', () => { ]; it('should expose a tracer provider via getTracerProvider', () => { - const tracer = api.trace.getTracerProvider(); + const tracer = api.trace.getTracerProvider(); assert.ok(tracer); assert.strictEqual(typeof tracer, 'object'); }); @@ -46,7 +53,7 @@ describe('API', () => { it('should not crash', () => { functions.forEach(fn => { - const tracer = api.trace.getTracerProvider(); + const tracer = api.trace.getTracerProvider(); try { ((tracer as unknown) as { [fn: string]: Function })[fn](); // Try to run the function assert.ok(true, fn); @@ -60,16 +67,13 @@ describe('API', () => { it('should use the global tracer provider', () => { api.trace.initGlobalTracerProvider(new TestTracerProvider()); - const tracer = api.trace.getTracerProvider().getTracer('name'); + const tracer = api.trace.getTracerProvider().getTracer('name'); const span = tracer.startSpan('test'); assert.deepStrictEqual(span, dummySpan); }); class TestTracer extends NoopTracer { - startSpan( - name: string, - options?: SpanOptions | undefined - ): Span { + startSpan(name: string, options?: SpanOptions | undefined): Span { return dummySpan; } } diff --git a/packages/opentelemetry-api/test/noop-implementations/noop-meter.test.ts b/packages/opentelemetry-api/test/noop-implementations/noop-meter.test.ts index bbbd0643b3..21cb6171f0 100644 --- a/packages/opentelemetry-api/test/noop-implementations/noop-meter.test.ts +++ b/packages/opentelemetry-api/test/noop-implementations/noop-meter.test.ts @@ -23,10 +23,9 @@ import { NOOP_BOUND_MEASURE, NOOP_COUNTER_METRIC, NOOP_GAUGE_METRIC, - NOOP_MEASURE_METRIC + NOOP_MEASURE_METRIC, } from '../../src'; - describe('NoopMeter', () => { it('should not crash', () => { const meter = new NoopMeterProvider().getMeter('test-noop'); @@ -50,13 +49,10 @@ describe('NoopMeter', () => { const measure = meter.createMeasure('some-name'); measure.getDefaultBound().record(1); - measure.getDefaultBound().record( - 1, - { - traceId: 'a3cda95b652f4a1592b449d5929fda1b', - spanId: '5e0c63257de34c92', - } - ); + measure.getDefaultBound().record(1, { + traceId: 'a3cda95b652f4a1592b449d5929fda1b', + spanId: '5e0c63257de34c92', + }); // ensure the correct noop const is returned assert.strictEqual(measure, NOOP_MEASURE_METRIC); diff --git a/packages/opentelemetry-api/test/noop-implementations/noop-span.test.ts b/packages/opentelemetry-api/test/noop-implementations/noop-span.test.ts index 0350a9ab53..a42d471189 100644 --- a/packages/opentelemetry-api/test/noop-implementations/noop-span.test.ts +++ b/packages/opentelemetry-api/test/noop-implementations/noop-span.test.ts @@ -15,7 +15,13 @@ */ import * as assert from 'assert'; -import { CanonicalCode, INVALID_SPAN_ID, INVALID_TRACE_ID, NoopSpan, TraceFlags } from '../../src'; +import { + CanonicalCode, + INVALID_SPAN_ID, + INVALID_TRACE_ID, + NoopSpan, + TraceFlags, +} from '../../src'; describe('NoopSpan', () => { it('do not crash', () => { 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 9fd5171dbd..ed017dda99 100644 --- a/packages/opentelemetry-api/test/noop-implementations/noop-tracer.test.ts +++ b/packages/opentelemetry-api/test/noop-implementations/noop-tracer.test.ts @@ -41,7 +41,10 @@ describe('NoopTracer', () => { assert.ok(httpTextFormat); httpTextFormat.inject(Context.ROOT_CONTEXT, {}); - assert.deepStrictEqual(httpTextFormat.extract(Context.ROOT_CONTEXT, {}), Context.ROOT_CONTEXT); + assert.deepStrictEqual( + httpTextFormat.extract(Context.ROOT_CONTEXT, {}), + Context.ROOT_CONTEXT + ); const binaryFormat = tracer.getBinaryFormat(); assert.ok(binaryFormat); From 02605b21d6b24d9c0d2b96bca9def78e619fb0c5 Mon Sep 17 00:00:00 2001 From: Daniel Dyla Date: Thu, 6 Feb 2020 12:37:00 -0500 Subject: [PATCH 09/23] chore: bust cache --- .circleci/config.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index e04d9f22ba..f5e726e02b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -40,7 +40,7 @@ mysql_service: &mysql_service MYSQL_ROOT_PASSWORD: rootpw cache_1: &cache_1 - key: npm-cache-01-{{ .Environment.CIRCLE_JOB }}-{{ checksum "/tmp/checksums.txt" }}-FDF2088C + key: npm-cache-01-{{ .Environment.CIRCLE_JOB }}-{{ checksum "/tmp/checksums.txt" }}-F267A71D paths: - ./node_modules - ./package-lock.json @@ -60,7 +60,7 @@ cache_1: &cache_1 - packages/opentelemetry-plugin-dns/node_modules cache_2: &cache_2 - key: npm-cache-02-{{ .Environment.CIRCLE_JOB }}-{{ checksum "/tmp/checksums.txt" }}-FDF2088C + key: npm-cache-02-{{ .Environment.CIRCLE_JOB }}-{{ checksum "/tmp/checksums.txt" }}-F267A71D paths: - packages/opentelemetry-plugin-grpc/node_modules - packages/opentelemetry-plugin-http/node_modules @@ -95,10 +95,10 @@ node_unit_tests: &node_unit_tests echo "CIRCLE_NODE_VERSION=${CIRCLE_NODE_VERSION}" - restore_cache: keys: - - npm-cache-01-{{ .Environment.CIRCLE_JOB }}-{{ checksum "/tmp/checksums.txt" }}-FDF2088C + - npm-cache-01-{{ .Environment.CIRCLE_JOB }}-{{ checksum "/tmp/checksums.txt" }}-F267A71D - restore_cache: keys: - - npm-cache-02-{{ .Environment.CIRCLE_JOB }}-{{ checksum "/tmp/checksums.txt" }}-FDF2088C + - npm-cache-02-{{ .Environment.CIRCLE_JOB }}-{{ checksum "/tmp/checksums.txt" }}-F267A71D - run: name: Install Root Dependencies command: npm install --ignore-scripts @@ -135,10 +135,10 @@ browsers_unit_tests: &browsers_unit_tests echo "CIRCLE_NODE_VERSION=${CIRCLE_NODE_VERSION}" - restore_cache: keys: - - npm-cache-01-{{ .Environment.CIRCLE_JOB }}-{{ checksum "/tmp/checksums.txt" }}-FDF2088C + - npm-cache-01-{{ .Environment.CIRCLE_JOB }}-{{ checksum "/tmp/checksums.txt" }}-F267A71D - restore_cache: keys: - - npm-cache-02-{{ .Environment.CIRCLE_JOB }}-{{ checksum "/tmp/checksums.txt" }}-FDF2088C + - npm-cache-02-{{ .Environment.CIRCLE_JOB }}-{{ checksum "/tmp/checksums.txt" }}-F267A71D - run: name: Install Root Dependencies command: npm install --ignore-scripts From 7961494a25496be6b0429c0fd76838ebde933a38 Mon Sep 17 00:00:00 2001 From: Daniel Dyla Date: Thu, 6 Feb 2020 14:09:09 -0500 Subject: [PATCH 10/23] chore: revert unnecessary changes from api package --- .../distributed_context/DistributedContext.ts | 29 ++++++++++++ .../src/distributed_context/EntryValue.ts | 45 +++++++++++++++++++ packages/opentelemetry-api/src/index.ts | 22 ++++----- .../src/metrics/BoundInstrument.ts | 9 +++- .../opentelemetry-api/src/metrics/Metric.ts | 14 +++++- .../src/metrics/NoopMeter.ts | 20 +++++++-- .../opentelemetry-api/test/api/api.test.ts | 20 ++++----- .../noop-implementations/noop-meter.test.ts | 16 ++++--- .../noop-implementations/noop-span.test.ts | 8 +--- packages/opentelemetry-api/tsconfig.json | 3 +- .../src/BoundInstrument.ts | 6 ++- 11 files changed, 149 insertions(+), 43 deletions(-) create mode 100644 packages/opentelemetry-api/src/distributed_context/DistributedContext.ts create mode 100644 packages/opentelemetry-api/src/distributed_context/EntryValue.ts diff --git a/packages/opentelemetry-api/src/distributed_context/DistributedContext.ts b/packages/opentelemetry-api/src/distributed_context/DistributedContext.ts new file mode 100644 index 0000000000..b06dcb8059 --- /dev/null +++ b/packages/opentelemetry-api/src/distributed_context/DistributedContext.ts @@ -0,0 +1,29 @@ +/*! + * Copyright 2019, 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 { EntryValue } from './EntryValue'; + +/** + * DistributedContext represents collection of entries. Each key of + * DistributedContext is associated with exactly one value. DistributedContext + * is serializable, to facilitate propagating it not only inside the process + * but also across process boundaries. DistributedContext is used to annotate + * telemetry with the name:value pair Entry. Those values can be used to add + * dimension to the metric or additional contest properties to logs and traces. + */ +export interface DistributedContext { + [entryKey: string]: EntryValue; +} diff --git a/packages/opentelemetry-api/src/distributed_context/EntryValue.ts b/packages/opentelemetry-api/src/distributed_context/EntryValue.ts new file mode 100644 index 0000000000..cb0c58557e --- /dev/null +++ b/packages/opentelemetry-api/src/distributed_context/EntryValue.ts @@ -0,0 +1,45 @@ +/*! + * Copyright 2019, 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. + */ + +/** + * {@link EntryValue} contains properties associated with a {@link + * DistributedContext}. + */ +export interface EntryValue { + /** `String` value of the `EntryValue`. */ + value: string; + /** + * ttl is an integer that represents number of hops an entry can + * propagate. + */ + ttl?: EntryTtl; +} + +/** + * EntryTtl is an integer that represents number of hops an entry can propagate. + * + * For now, ONLY special values (0 and -1) are supported. + */ +export enum EntryTtl { + /** + * NO_PROPAGATION is considered to have local scope and is used within the + * process it created. + */ + NO_PROPAGATION = 0, + + /** UNLIMITED_PROPAGATION can propagate unlimited hops. */ + UNLIMITED_PROPAGATION = -1, +} diff --git a/packages/opentelemetry-api/src/index.ts b/packages/opentelemetry-api/src/index.ts index 49a1d42e48..e35ac4e746 100644 --- a/packages/opentelemetry-api/src/index.ts +++ b/packages/opentelemetry-api/src/index.ts @@ -16,33 +16,35 @@ export * from './common/Logger'; export * from './common/Time'; -export * from './context/propagation/BinaryFormat'; export * from './context/propagation/carrier'; +export * from './context/propagation/BinaryFormat'; export * from './context/propagation/HttpTextFormat'; +export * from './distributed_context/DistributedContext'; +export * from './distributed_context/EntryValue'; export * from './metrics/BoundInstrument'; export * from './metrics/Meter'; export * from './metrics/MeterProvider'; export * from './metrics/Metric'; -export * from './metrics/NoopMeter'; -export * from './metrics/NoopMeterProvider'; export * from './trace/attributes'; export * from './trace/Event'; export * from './trace/instrumentation/Plugin'; export * from './trace/link'; -export * from './trace/NoopSpan'; -export * from './trace/NoopTracer'; -export * from './trace/NoopTracerProvider'; export * from './trace/Sampler'; -export * from './trace/span_context'; -export * from './trace/span_kind'; export * from './trace/span'; export * from './trace/SpanOptions'; +export * from './trace/span_context'; +export * from './trace/span_kind'; export * from './trace/status'; export * from './trace/TimedEvent'; +export * from './trace/tracer'; +export * from './trace/tracer_provider'; export * from './trace/trace_flags'; export * from './trace/trace_state'; -export * from './trace/tracer_provider'; -export * from './trace/tracer'; +export * from './trace/NoopSpan'; +export * from './trace/NoopTracer'; +export * from './trace/NoopTracerProvider'; +export * from './metrics/NoopMeterProvider'; +export * from './metrics/NoopMeter'; import { TraceAPI } from './api/trace'; /** Entrypoint for trace API */ diff --git a/packages/opentelemetry-api/src/metrics/BoundInstrument.ts b/packages/opentelemetry-api/src/metrics/BoundInstrument.ts index 284f6e4a8c..83612d6b4d 100644 --- a/packages/opentelemetry-api/src/metrics/BoundInstrument.ts +++ b/packages/opentelemetry-api/src/metrics/BoundInstrument.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +import { DistributedContext } from '../distributed_context/DistributedContext'; import { SpanContext } from '../trace/span_context'; /** An Instrument for Counter Metric. */ @@ -39,9 +40,15 @@ export interface BoundMeasure { /** * Records the given value to this measure. * @param value the measurement to record. + * @param distContext the distContext associated with the measurements. * @param spanContext the {@link SpanContext} that identifies the {@link Span} * for which the measurements are associated with. */ record(value: number): void; - record(value: number, spanContext: SpanContext): void; + record(value: number, distContext: DistributedContext): void; + record( + value: number, + distContext: DistributedContext, + spanContext: SpanContext + ): void; } diff --git a/packages/opentelemetry-api/src/metrics/Metric.ts b/packages/opentelemetry-api/src/metrics/Metric.ts index d35da4fa7c..0c6ca61a70 100644 --- a/packages/opentelemetry-api/src/metrics/Metric.ts +++ b/packages/opentelemetry-api/src/metrics/Metric.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +import { DistributedContext } from '../distributed_context/DistributedContext'; import { SpanContext } from '../trace/span_context'; /** @@ -122,7 +123,18 @@ export interface MetricUtils { */ record(value: number, labelSet: LabelSet): void; - record(value: number, labelSet: LabelSet, spanContext: SpanContext): void; + record( + value: number, + labelSet: LabelSet, + distContext: DistributedContext + ): void; + + record( + value: number, + labelSet: LabelSet, + distContext: DistributedContext, + spanContext: SpanContext + ): void; } /** diff --git a/packages/opentelemetry-api/src/metrics/NoopMeter.ts b/packages/opentelemetry-api/src/metrics/NoopMeter.ts index 0c3ecfce26..c39a664aa5 100644 --- a/packages/opentelemetry-api/src/metrics/NoopMeter.ts +++ b/packages/opentelemetry-api/src/metrics/NoopMeter.ts @@ -17,6 +17,7 @@ import { Meter } from './Meter'; import { MetricOptions, Metric, Labels, LabelSet, MetricUtils } from './Metric'; import { BoundMeasure, BoundCounter, BoundGauge } from './BoundInstrument'; +import { DistributedContext } from '../distributed_context/DistributedContext'; import { SpanContext } from '../trace/span_context'; /** @@ -118,11 +119,18 @@ export class NoopGaugeMetric extends NoopMetric export class NoopMeasureMetric extends NoopMetric implements Pick { - record(value: number, labelSet: LabelSet, spanContext?: SpanContext) { - if (typeof spanContext === 'undefined') { + record( + value: number, + labelSet: LabelSet, + distContext?: DistributedContext, + spanContext?: SpanContext + ) { + if (typeof distContext === 'undefined') { this.bind(labelSet).record(value); + } else if (typeof spanContext === 'undefined') { + this.bind(labelSet).record(value, distContext); } else { - this.bind(labelSet).record(value, spanContext); + this.bind(labelSet).record(value, distContext, spanContext); } } } @@ -140,7 +148,11 @@ export class NoopBoundGauge implements BoundGauge { } export class NoopBoundMeasure implements BoundMeasure { - record(value: number, spanContext?: SpanContext): void { + record( + value: number, + distContext?: DistributedContext, + spanContext?: SpanContext + ): void { return; } } diff --git a/packages/opentelemetry-api/test/api/api.test.ts b/packages/opentelemetry-api/test/api/api.test.ts index 031fa6f976..c0ecb9baf9 100644 --- a/packages/opentelemetry-api/test/api/api.test.ts +++ b/packages/opentelemetry-api/test/api/api.test.ts @@ -15,14 +15,7 @@ */ import * as assert from 'assert'; -import api, { - TraceFlags, - NoopSpan, - NoopTracerProvider, - NoopTracer, - SpanOptions, - Span, -} from '../../src'; +import api, { TraceFlags, NoopSpan, NoopTracerProvider, NoopTracer, SpanOptions, Span } from '../../src'; describe('API', () => { const functions = [ @@ -34,7 +27,7 @@ describe('API', () => { ]; it('should expose a tracer provider via getTracerProvider', () => { - const tracer = api.trace.getTracerProvider(); + const tracer = api.trace.getTracerProvider(); assert.ok(tracer); assert.strictEqual(typeof tracer, 'object'); }); @@ -53,7 +46,7 @@ describe('API', () => { it('should not crash', () => { functions.forEach(fn => { - const tracer = api.trace.getTracerProvider(); + const tracer = api.trace.getTracerProvider(); try { ((tracer as unknown) as { [fn: string]: Function })[fn](); // Try to run the function assert.ok(true, fn); @@ -67,13 +60,16 @@ describe('API', () => { it('should use the global tracer provider', () => { api.trace.initGlobalTracerProvider(new TestTracerProvider()); - const tracer = api.trace.getTracerProvider().getTracer('name'); + const tracer = api.trace.getTracerProvider().getTracer('name'); const span = tracer.startSpan('test'); assert.deepStrictEqual(span, dummySpan); }); class TestTracer extends NoopTracer { - startSpan(name: string, options?: SpanOptions | undefined): Span { + startSpan( + name: string, + options?: SpanOptions | undefined + ): Span { return dummySpan; } } diff --git a/packages/opentelemetry-api/test/noop-implementations/noop-meter.test.ts b/packages/opentelemetry-api/test/noop-implementations/noop-meter.test.ts index 21cb6171f0..052854e82b 100644 --- a/packages/opentelemetry-api/test/noop-implementations/noop-meter.test.ts +++ b/packages/opentelemetry-api/test/noop-implementations/noop-meter.test.ts @@ -23,9 +23,10 @@ import { NOOP_BOUND_MEASURE, NOOP_COUNTER_METRIC, NOOP_GAUGE_METRIC, - NOOP_MEASURE_METRIC, + NOOP_MEASURE_METRIC } from '../../src'; + describe('NoopMeter', () => { it('should not crash', () => { const meter = new NoopMeterProvider().getMeter('test-noop'); @@ -49,10 +50,15 @@ describe('NoopMeter', () => { const measure = meter.createMeasure('some-name'); measure.getDefaultBound().record(1); - measure.getDefaultBound().record(1, { - traceId: 'a3cda95b652f4a1592b449d5929fda1b', - spanId: '5e0c63257de34c92', - }); + measure.getDefaultBound().record(1, { key: { value: 'value' } }); + measure.getDefaultBound().record( + 1, + { key: { value: 'value' } }, + { + traceId: 'a3cda95b652f4a1592b449d5929fda1b', + spanId: '5e0c63257de34c92', + } + ); // ensure the correct noop const is returned assert.strictEqual(measure, NOOP_MEASURE_METRIC); diff --git a/packages/opentelemetry-api/test/noop-implementations/noop-span.test.ts b/packages/opentelemetry-api/test/noop-implementations/noop-span.test.ts index a42d471189..0350a9ab53 100644 --- a/packages/opentelemetry-api/test/noop-implementations/noop-span.test.ts +++ b/packages/opentelemetry-api/test/noop-implementations/noop-span.test.ts @@ -15,13 +15,7 @@ */ import * as assert from 'assert'; -import { - CanonicalCode, - INVALID_SPAN_ID, - INVALID_TRACE_ID, - NoopSpan, - TraceFlags, -} from '../../src'; +import { CanonicalCode, INVALID_SPAN_ID, INVALID_TRACE_ID, NoopSpan, TraceFlags } from '../../src'; describe('NoopSpan', () => { it('do not crash', () => { diff --git a/packages/opentelemetry-api/tsconfig.json b/packages/opentelemetry-api/tsconfig.json index df95d9cc57..2b05ed8413 100644 --- a/packages/opentelemetry-api/tsconfig.json +++ b/packages/opentelemetry-api/tsconfig.json @@ -5,8 +5,7 @@ "outDir": "build" }, "include": [ - "src/**/*.ts", - "test/**/*.ts" + "src/**/*.ts" ], "typedocOptions": { "name": "OpenTelemetry API for JavaScript", diff --git a/packages/opentelemetry-metrics/src/BoundInstrument.ts b/packages/opentelemetry-metrics/src/BoundInstrument.ts index e78dca9576..b72176fae1 100644 --- a/packages/opentelemetry-metrics/src/BoundInstrument.ts +++ b/packages/opentelemetry-metrics/src/BoundInstrument.ts @@ -144,7 +144,11 @@ export class BoundMeasure extends BaseBoundInstrument super(labelSet); } - record(value: number, spanContext?: types.SpanContext): void { + record( + value: number, + distContext?: types.DistributedContext, + spanContext?: types.SpanContext + ): void { if (this._disabled) return; if (this._absolute && value < 0) { From 02e07cb6962ba9104ae2409809255ad766556333 Mon Sep 17 00:00:00 2001 From: Daniel Dyla Date: Thu, 6 Feb 2020 14:50:55 -0500 Subject: [PATCH 11/23] chore: revert carrier so it can be a separate pr --- .../src/context/propagation/HttpTextFormat.ts | 5 ++--- .../context/propagation/NoopHttpTextFormat.ts | 5 ++--- .../src/context/propagation/carrier.ts | 19 ------------------- packages/opentelemetry-api/src/index.ts | 1 - .../src/shim.ts | 5 ++--- 5 files changed, 6 insertions(+), 29 deletions(-) delete mode 100644 packages/opentelemetry-api/src/context/propagation/carrier.ts diff --git a/packages/opentelemetry-api/src/context/propagation/HttpTextFormat.ts b/packages/opentelemetry-api/src/context/propagation/HttpTextFormat.ts index fef9293ecf..a8111400ae 100644 --- a/packages/opentelemetry-api/src/context/propagation/HttpTextFormat.ts +++ b/packages/opentelemetry-api/src/context/propagation/HttpTextFormat.ts @@ -15,7 +15,6 @@ */ import { Context } from '@opentelemetry/scope-base'; -import { Carrier } from './carrier'; /** * Injects {@link Context} into and extracts it from carriers that travel @@ -35,7 +34,7 @@ export interface HttpTextFormat { * @param context the Context from which to extract values to transmit over the wire. * @param carrier the carrier of propagation fields, such as http request headers. */ - inject(context: Context, carrier: Carrier): void; + inject(context: Context, carrier: unknown): void; /** * Given a {@link Context} and a carrier, extract context values from a carrier and @@ -44,5 +43,5 @@ export interface HttpTextFormat { * @param context the Context from which to extract values to transmit over the wire. * @param carrier the carrier of propagation fields, such as http request headers. */ - extract(context: Context, carrier: Carrier): Context; + extract(context: Context, carrier: unknown): Context; } diff --git a/packages/opentelemetry-api/src/context/propagation/NoopHttpTextFormat.ts b/packages/opentelemetry-api/src/context/propagation/NoopHttpTextFormat.ts index 1f38729eb5..7296ffdf9b 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): void {} /** Noop extract function does nothing and returns the input context */ - extract(context: Context, carrier: Carrier): Context { + extract(context: Context, carrier: unknown): Context { return context; } } diff --git a/packages/opentelemetry-api/src/context/propagation/carrier.ts b/packages/opentelemetry-api/src/context/propagation/carrier.ts deleted file mode 100644 index 7644ace1af..0000000000 --- a/packages/opentelemetry-api/src/context/propagation/carrier.ts +++ /dev/null @@ -1,19 +0,0 @@ -/*! - * 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 Carrier = { - [key: string]: unknown; -}; diff --git a/packages/opentelemetry-api/src/index.ts b/packages/opentelemetry-api/src/index.ts index e35ac4e746..ab10bb7ed4 100644 --- a/packages/opentelemetry-api/src/index.ts +++ b/packages/opentelemetry-api/src/index.ts @@ -16,7 +16,6 @@ export * from './common/Logger'; export * from './common/Time'; -export * from './context/propagation/carrier'; export * from './context/propagation/BinaryFormat'; export * from './context/propagation/HttpTextFormat'; export * from './distributed_context/DistributedContext'; diff --git a/packages/opentelemetry-shim-opentracing/src/shim.ts b/packages/opentelemetry-shim-opentracing/src/shim.ts index 7fdf9b1044..9fa2a0cc9e 100644 --- a/packages/opentelemetry-shim-opentracing/src/shim.ts +++ b/packages/opentelemetry-shim-opentracing/src/shim.ts @@ -15,7 +15,6 @@ */ import * as types from '@opentelemetry/api'; -import { Carrier } from '@opentelemetry/api'; import { Context, NoopLogger } from '@opentelemetry/core'; import * as opentracing from 'opentracing'; @@ -121,7 +120,7 @@ export class TracerShim extends opentracing.Tracer { _inject( spanContext: opentracing.SpanContext, format: string, - carrier: Carrier + carrier: unknown ): void { const opentelemSpanContext: types.SpanContext = (spanContext as SpanContextShim).getSpanContext(); if (!carrier || typeof carrier !== 'object') return; @@ -149,7 +148,7 @@ export class TracerShim extends opentracing.Tracer { } } - _extract(format: string, carrier: 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: From fb0d170c88f5447e56611f88bbd0891f3f938ab7 Mon Sep 17 00:00:00 2001 From: Daniel Dyla Date: Thu, 6 Feb 2020 15:18:24 -0500 Subject: [PATCH 12/23] chore: typo --- packages/opentelemetry-plugin-http/src/http.ts | 4 ++-- packages/opentelemetry-plugin-xml-http-request/src/xhr.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/opentelemetry-plugin-http/src/http.ts b/packages/opentelemetry-plugin-http/src/http.ts index e2abb4e369..b5c3937b2c 100644 --- a/packages/opentelemetry-plugin-http/src/http.ts +++ b/packages/opentelemetry-plugin-http/src/http.ts @@ -297,7 +297,7 @@ export class HttpPlugin extends BasePlugin { }), }; - // Using context direclty like this is temporary. In a future PR, context + // Using context directly like this is temporary. In a future PR, context // will be managed by the scope manager (which may be renamed to context manager?) const spanContext = Context.getExtractedSpanContext( propagation.extract(Context.ROOT_CONTEXT, headers) @@ -410,7 +410,7 @@ export class HttpPlugin extends BasePlugin { const span = plugin._startHttpSpan(operationName, spanOptions); plugin._tracer .getHttpTextFormat() - // Using context direclty like this is temporary. In a future PR, context + // Using context directly like this is temporary. In a future PR, context // will be managed by the scope manager (which may be renamed to context manager?) .inject( Context.setActiveSpan(Context.ROOT_CONTEXT, span), diff --git a/packages/opentelemetry-plugin-xml-http-request/src/xhr.ts b/packages/opentelemetry-plugin-xml-http-request/src/xhr.ts index 80c97c4ccf..70aa3bb7ef 100644 --- a/packages/opentelemetry-plugin-xml-http-request/src/xhr.ts +++ b/packages/opentelemetry-plugin-xml-http-request/src/xhr.ts @@ -90,7 +90,7 @@ export class XMLHttpRequestPlugin extends BasePlugin { const headers: { [key: string]: unknown } = {}; this._tracer .getHttpTextFormat() - // Using context direclty like this is temporary. In a future PR, context + // Using context directly like this is temporary. In a future PR, context // will be managed by the scope manager (which may be renamed to context manager?) .inject(Context.setActiveSpan(Context.ROOT_CONTEXT, span), headers); From d4335289d3a9b372e5ceb0bbdc55c848a7b39373 Mon Sep 17 00:00:00 2001 From: Daniel Dyla Date: Thu, 6 Feb 2020 17:05:06 -0500 Subject: [PATCH 13/23] chore: avoid extra allocation on context set value --- packages/opentelemetry-scope-base/src/context.ts | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/packages/opentelemetry-scope-base/src/context.ts b/packages/opentelemetry-scope-base/src/context.ts index 6b413e88b2..f4bfab021a 100644 --- a/packages/opentelemetry-scope-base/src/context.ts +++ b/packages/opentelemetry-scope-base/src/context.ts @@ -32,9 +32,8 @@ export class Context { * * @param context a context from which to inherit values */ - protected constructor(context: Context | Store = {}) { - const storage = context instanceof Context ? context._context : context; - this._context = Object.assign(Object.create(null), storage); + protected constructor(parentContext: Store = {}) { + this._context = Object.assign(Object.create(null), parentContext); } /** @@ -54,10 +53,9 @@ export class Context { * @param value value to set for the given key */ setValue(key: string, value: unknown): Context { - return new Context({ - ...this._context, - [key]: value, - }); + const context = new Context(this._context); + context._context[key] = value; + return context } /** @@ -67,7 +65,7 @@ export class Context { * @param key context key for which to clear a value */ deleteValue(key: string): Context { - const context = new Context(this); + const context = new Context(this._context); delete context._context[key]; return context; } From 38cda1d05214f1b2976c0b10ee1b1306077e70f3 Mon Sep 17 00:00:00 2001 From: Daniel Dyla Date: Thu, 6 Feb 2020 17:07:31 -0500 Subject: [PATCH 14/23] chore: lint --- packages/opentelemetry-scope-base/src/context.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/opentelemetry-scope-base/src/context.ts b/packages/opentelemetry-scope-base/src/context.ts index f4bfab021a..aeb809bb1c 100644 --- a/packages/opentelemetry-scope-base/src/context.ts +++ b/packages/opentelemetry-scope-base/src/context.ts @@ -55,7 +55,7 @@ export class Context { setValue(key: string, value: unknown): Context { const context = new Context(this._context); context._context[key] = value; - return context + return context; } /** From d4caaba6e90b18db558be2d552188b9f87d7eeb1 Mon Sep 17 00:00:00 2001 From: Daniel Dyla Date: Fri, 7 Feb 2020 10:27:37 -0500 Subject: [PATCH 15/23] chore: do not extend context --- packages/opentelemetry-api/src/index.ts | 2 + .../opentelemetry-core/src/context/context.ts | 105 ++++++++---------- .../src/context/propagation/B3Format.ts | 7 +- .../context/propagation/HttpTraceContext.ts | 7 +- .../test/context/B3Format.test.ts | 34 +++--- .../test/context/HttpTraceContext.test.ts | 24 ++-- .../opentelemetry-plugin-http/src/http.ts | 15 ++- .../test/utils/DummyPropagation.ts | 11 +- .../opentelemetry-plugin-https/package.json | 1 - .../test/utils/DummyPropagation.ts | 11 +- .../src/xhr.ts | 8 +- .../src/JaegerHttpTraceFormat.ts | 16 ++- .../test/JaegerHttpTraceFormat.test.ts | 23 ++-- .../opentelemetry-scope-base/src/context.ts | 17 +-- .../src/shim.ts | 14 ++- packages/opentelemetry-tracing/src/Tracer.ts | 21 ++-- .../test/BasicTracerRegistry.test.ts | 6 +- .../src/StackScopeManager.ts | 2 +- .../test/StackScopeManager.test.ts | 2 +- 19 files changed, 178 insertions(+), 148 deletions(-) diff --git a/packages/opentelemetry-api/src/index.ts b/packages/opentelemetry-api/src/index.ts index ab10bb7ed4..ffdf3667d6 100644 --- a/packages/opentelemetry-api/src/index.ts +++ b/packages/opentelemetry-api/src/index.ts @@ -45,6 +45,8 @@ export * from './trace/NoopTracerProvider'; export * from './metrics/NoopMeterProvider'; export * from './metrics/NoopMeter'; +export { Context } from '@opentelemetry/scope-base'; + import { TraceAPI } from './api/trace'; /** Entrypoint for trace API */ export const trace = TraceAPI.getInstance(); diff --git a/packages/opentelemetry-core/src/context/context.ts b/packages/opentelemetry-core/src/context/context.ts index b404664423..e1fec8b8b8 100644 --- a/packages/opentelemetry-core/src/context/context.ts +++ b/packages/opentelemetry-core/src/context/context.ts @@ -15,67 +15,60 @@ */ import { Span, SpanContext } from '@opentelemetry/api'; -import { Context as ContextBase } from '@opentelemetry/scope-base'; +import { Context } from '@opentelemetry/scope-base'; /** - * Context class with static helper functions for accessing values - * of cross-cutting concerns. + * Return the active span if one exists + * + * @param context context to get span from */ -export class Context extends ContextBase { - /** - * Return the active span if one exists - * - * @param context context to get span from - */ - static getActiveSpan(context: Context): Span | undefined { - return (context.getValue('ACTIVE_SPAN') as Span) || undefined; - } +export function getActiveSpan(context: Context): Span | undefined { + return (context.getValue('ACTIVE_SPAN') as Span) || undefined; +} - /** - * Set the active span on a context - * - * @param context context to use as parent - * @param span span to set active - */ - static setActiveSpan(context: Context, span: Span): Context { - return context.setValue('ACTIVE_SPAN', span); - } +/** + * Set the active span on a context + * + * @param context context to use as parent + * @param span span to set active + */ +export function setActiveSpan(context: Context, span: Span): Context { + return context.setValue('ACTIVE_SPAN', span); +} - /** - * Get the extracted span context from a context - * - * @param context context to get span context from - */ - static getExtractedSpanContext(context: Context): SpanContext | undefined { - return ( - (context.getValue('EXTRACTED_SPAN_CONTEXT') as SpanContext) || undefined - ); - } +/** + * Get the extracted span context from a context + * + * @param context context to get span context from + */ +export function getExtractedSpanContext( + context: Context +): SpanContext | undefined { + return ( + (context.getValue('EXTRACTED_SPAN_CONTEXT') as SpanContext) || undefined + ); +} - /** - * Set the extracted span context on a context - * - * @param context context to set span context on - * @param spanContext span context to set - */ - static setExtractedSpanContext( - context: Context, - spanContext: SpanContext - ): Context { - return context.setValue('EXTRACTED_SPAN_CONTEXT', spanContext); - } +/** + * Set the extracted span context on a context + * + * @param context context to set span context on + * @param spanContext span context to set + */ +export function setExtractedSpanContext( + context: Context, + spanContext: SpanContext +): Context { + return context.setValue('EXTRACTED_SPAN_CONTEXT', spanContext); +} - /** - * Get the span context of the parent span if it exists, - * or the extracted span context if there is no active - * span. - * - * @param context context to get values from - */ - static getParentSpanContext(context: Context) { - return ( - Context.getActiveSpan(context)?.context() || - Context.getExtractedSpanContext(context) - ); - } +/** + * Get the span context of the parent span if it exists, + * or the extracted span context if there is no active + * span. + * + * @param context context to get values from + */ +export function getParentSpanContext(context: Context) { + return getActiveSpan(context)?.context() || getExtractedSpanContext(context); } diff --git a/packages/opentelemetry-core/src/context/propagation/B3Format.ts b/packages/opentelemetry-core/src/context/propagation/B3Format.ts index 5b3db96594..dd9e7319de 100644 --- a/packages/opentelemetry-core/src/context/propagation/B3Format.ts +++ b/packages/opentelemetry-core/src/context/propagation/B3Format.ts @@ -15,7 +15,8 @@ */ import { HttpTextFormat, TraceFlags } from '@opentelemetry/api'; -import { Context } from '../context'; +import { Context } from '@opentelemetry/scope-base'; +import { getParentSpanContext, setExtractedSpanContext } from '../context'; export const X_B3_TRACE_ID = 'x-b3-traceid'; export const X_B3_SPAN_ID = 'x-b3-spanid'; @@ -38,7 +39,7 @@ function isValidSpanId(spanId: string): boolean { */ export class B3Format implements HttpTextFormat { inject(context: Context, carrier: { [key: string]: unknown }) { - const spanContext = Context.getParentSpanContext(context); + const spanContext = getParentSpanContext(context); if (!spanContext) return; if ( @@ -70,7 +71,7 @@ export class B3Format implements HttpTextFormat { : sampledHeader; if (isValidTraceId(traceId) && isValidSpanId(spanId)) { - return Context.setExtractedSpanContext(context, { + return setExtractedSpanContext(context, { traceId, spanId, isRemote: true, diff --git a/packages/opentelemetry-core/src/context/propagation/HttpTraceContext.ts b/packages/opentelemetry-core/src/context/propagation/HttpTraceContext.ts index fa70fbe1d2..f3f342b356 100644 --- a/packages/opentelemetry-core/src/context/propagation/HttpTraceContext.ts +++ b/packages/opentelemetry-core/src/context/propagation/HttpTraceContext.ts @@ -15,8 +15,9 @@ */ import { HttpTextFormat, SpanContext, TraceFlags } from '@opentelemetry/api'; +import { Context } from '@opentelemetry/scope-base'; import { TraceState } from '../../trace/TraceState'; -import { Context } from '../context'; +import { getParentSpanContext, setExtractedSpanContext } from '../context'; export const TRACE_PARENT_HEADER = 'traceparent'; export const TRACE_STATE_HEADER = 'tracestate'; @@ -58,7 +59,7 @@ export function parseTraceParent(traceParent: string): SpanContext | null { */ export class HttpTraceContext implements HttpTextFormat { inject(context: Context, carrier: { [key: string]: unknown }) { - const spanContext = Context.getParentSpanContext(context); + const spanContext = getParentSpanContext(context); if (!spanContext) return; const traceParent = `${VERSION}-${spanContext.traceId}-${ @@ -91,6 +92,6 @@ export class HttpTraceContext implements HttpTextFormat { : traceStateHeader; spanContext.traceState = new TraceState(state as string); } - return Context.setExtractedSpanContext(context, spanContext); + return setExtractedSpanContext(context, spanContext); } } diff --git a/packages/opentelemetry-core/test/context/B3Format.test.ts b/packages/opentelemetry-core/test/context/B3Format.test.ts index ea352f24cc..df7a1e6162 100644 --- a/packages/opentelemetry-core/test/context/B3Format.test.ts +++ b/packages/opentelemetry-core/test/context/B3Format.test.ts @@ -16,7 +16,11 @@ import { SpanContext, TraceFlags } from '@opentelemetry/api'; import * as assert from 'assert'; -import { Context } from '../../src/context/context'; +import { + setExtractedSpanContext, + getExtractedSpanContext, +} from '../../src/context/context'; +import { Context } from '@opentelemetry/scope-base'; import { B3Format, X_B3_SAMPLED, @@ -42,7 +46,7 @@ describe('B3Format', () => { }; b3Format.inject( - Context.setExtractedSpanContext(Context.ROOT_CONTEXT, spanContext), + setExtractedSpanContext(Context.ROOT_CONTEXT, spanContext), carrier ); assert.deepStrictEqual( @@ -63,7 +67,7 @@ describe('B3Format', () => { }; b3Format.inject( - Context.setExtractedSpanContext(Context.ROOT_CONTEXT, spanContext), + setExtractedSpanContext(Context.ROOT_CONTEXT, spanContext), carrier ); assert.deepStrictEqual( @@ -80,7 +84,7 @@ describe('B3Format', () => { spanId: '', }; b3Format.inject( - Context.setExtractedSpanContext(Context.ROOT_CONTEXT, emptySpanContext), + setExtractedSpanContext(Context.ROOT_CONTEXT, emptySpanContext), carrier ); assert.deepStrictEqual(carrier[X_B3_TRACE_ID], undefined); @@ -94,7 +98,7 @@ describe('B3Format', () => { }; b3Format.inject( - Context.setExtractedSpanContext(Context.ROOT_CONTEXT, spanContext), + setExtractedSpanContext(Context.ROOT_CONTEXT, spanContext), carrier ); assert.deepStrictEqual( @@ -110,7 +114,7 @@ describe('B3Format', () => { it('should extract context of a unsampled span from carrier', () => { carrier[X_B3_TRACE_ID] = '0af7651916cd43dd8448eb211c80319c'; carrier[X_B3_SPAN_ID] = 'b7ad6b7169203331'; - const extractedSpanContext = Context.getExtractedSpanContext( + const extractedSpanContext = getExtractedSpanContext( b3Format.extract(Context.ROOT_CONTEXT, carrier) ); @@ -126,7 +130,7 @@ describe('B3Format', () => { carrier[X_B3_TRACE_ID] = '0af7651916cd43dd8448eb211c80319c'; carrier[X_B3_SPAN_ID] = 'b7ad6b7169203331'; carrier[X_B3_SAMPLED] = '1'; - const extractedSpanContext = Context.getExtractedSpanContext( + const extractedSpanContext = getExtractedSpanContext( b3Format.extract(Context.ROOT_CONTEXT, carrier) ); @@ -142,7 +146,7 @@ describe('B3Format', () => { carrier[X_B3_TRACE_ID] = '0af7651916cd43dd8448eb211c80319c'; carrier[X_B3_SPAN_ID] = 'b7ad6b7169203331'; carrier[X_B3_SAMPLED] = true; - const extractedSpanContext = Context.getExtractedSpanContext( + const extractedSpanContext = getExtractedSpanContext( b3Format.extract(Context.ROOT_CONTEXT, carrier) ); @@ -158,7 +162,7 @@ describe('B3Format', () => { carrier[X_B3_TRACE_ID] = '0af7651916cd43dd8448eb211c80319c'; carrier[X_B3_SPAN_ID] = 'b7ad6b7169203331'; carrier[X_B3_SAMPLED] = false; - const extractedSpanContext = Context.getExtractedSpanContext( + const extractedSpanContext = getExtractedSpanContext( b3Format.extract(Context.ROOT_CONTEXT, carrier) ); @@ -174,7 +178,7 @@ describe('B3Format', () => { carrier[X_B3_TRACE_ID] = undefined; carrier[X_B3_SPAN_ID] = undefined; assert.deepStrictEqual( - Context.getExtractedSpanContext( + getExtractedSpanContext( b3Format.extract(Context.ROOT_CONTEXT, carrier) ), undefined @@ -185,7 +189,7 @@ describe('B3Format', () => { carrier[X_B3_TRACE_ID] = '0af7651916cd43dd8448eb211c80319c'; carrier[X_B3_SPAN_ID] = undefined; assert.deepStrictEqual( - Context.getExtractedSpanContext( + getExtractedSpanContext( b3Format.extract(Context.ROOT_CONTEXT, carrier) ), undefined @@ -194,7 +198,7 @@ describe('B3Format', () => { it('returns undefined if b3 header is missing', () => { assert.deepStrictEqual( - Context.getExtractedSpanContext( + getExtractedSpanContext( b3Format.extract(Context.ROOT_CONTEXT, carrier) ), undefined @@ -204,7 +208,7 @@ describe('B3Format', () => { it('returns undefined if b3 header is invalid', () => { carrier[X_B3_TRACE_ID] = 'invalid!'; assert.deepStrictEqual( - Context.getExtractedSpanContext( + getExtractedSpanContext( b3Format.extract(Context.ROOT_CONTEXT, carrier) ), undefined @@ -215,7 +219,7 @@ describe('B3Format', () => { carrier[X_B3_TRACE_ID] = ['0af7651916cd43dd8448eb211c80319c']; carrier[X_B3_SPAN_ID] = 'b7ad6b7169203331'; carrier[X_B3_SAMPLED] = '01'; - const extractedSpanContext = Context.getExtractedSpanContext( + const extractedSpanContext = getExtractedSpanContext( b3Format.extract(Context.ROOT_CONTEXT, carrier) ); assert.deepStrictEqual(extractedSpanContext, { @@ -266,7 +270,7 @@ describe('B3Format', () => { Object.getOwnPropertyNames(testCases).forEach(testCase => { carrier[X_B3_TRACE_ID] = testCases[testCase]; - const extractedSpanContext = Context.getExtractedSpanContext( + const extractedSpanContext = getExtractedSpanContext( b3Format.extract(Context.ROOT_CONTEXT, carrier) ); 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 83dccac40b..fb53cec343 100644 --- a/packages/opentelemetry-core/test/context/HttpTraceContext.test.ts +++ b/packages/opentelemetry-core/test/context/HttpTraceContext.test.ts @@ -15,8 +15,12 @@ */ import { SpanContext, TraceFlags } from '@opentelemetry/api'; +import { Context } from '@opentelemetry/scope-base'; import * as assert from 'assert'; -import { Context } from '../../src/context/context'; +import { + getExtractedSpanContext, + setExtractedSpanContext, +} from '../../src/context/context'; import { HttpTraceContext, TRACE_PARENT_HEADER, @@ -41,7 +45,7 @@ describe('HttpTraceContext', () => { }; httpTraceContext.inject( - Context.setExtractedSpanContext(Context.ROOT_CONTEXT, spanContext), + setExtractedSpanContext(Context.ROOT_CONTEXT, spanContext), carrier ); assert.deepStrictEqual( @@ -60,7 +64,7 @@ describe('HttpTraceContext', () => { }; httpTraceContext.inject( - Context.setExtractedSpanContext(Context.ROOT_CONTEXT, spanContext), + setExtractedSpanContext(Context.ROOT_CONTEXT, spanContext), carrier ); assert.deepStrictEqual( @@ -75,7 +79,7 @@ describe('HttpTraceContext', () => { it('should extract context of a sampled span from carrier', () => { carrier[TRACE_PARENT_HEADER] = '00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01'; - const extractedSpanContext = Context.getExtractedSpanContext( + const extractedSpanContext = getExtractedSpanContext( httpTraceContext.extract(Context.ROOT_CONTEXT, carrier) ); @@ -89,7 +93,7 @@ describe('HttpTraceContext', () => { it('returns null if traceparent header is missing', () => { assert.deepStrictEqual( - Context.getExtractedSpanContext( + getExtractedSpanContext( httpTraceContext.extract(Context.ROOT_CONTEXT, carrier) ), undefined @@ -99,7 +103,7 @@ describe('HttpTraceContext', () => { it('returns null if traceparent header is invalid', () => { carrier[TRACE_PARENT_HEADER] = 'invalid!'; assert.deepStrictEqual( - Context.getExtractedSpanContext( + getExtractedSpanContext( httpTraceContext.extract(Context.ROOT_CONTEXT, carrier) ), undefined @@ -110,7 +114,7 @@ describe('HttpTraceContext', () => { carrier[TRACE_PARENT_HEADER] = [ '00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01', ]; - const extractedSpanContext = Context.getExtractedSpanContext( + const extractedSpanContext = getExtractedSpanContext( httpTraceContext.extract(Context.ROOT_CONTEXT, carrier) ); assert.deepStrictEqual(extractedSpanContext, { @@ -125,7 +129,7 @@ describe('HttpTraceContext', () => { carrier[TRACE_PARENT_HEADER] = '00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01'; carrier[TRACE_STATE_HEADER] = 'foo=bar,baz=qux'; - const extractedSpanContext = Context.getExtractedSpanContext( + const extractedSpanContext = getExtractedSpanContext( httpTraceContext.extract(Context.ROOT_CONTEXT, carrier) ); @@ -143,7 +147,7 @@ describe('HttpTraceContext', () => { carrier[TRACE_PARENT_HEADER] = '00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01'; carrier[TRACE_STATE_HEADER] = ['foo=bar,baz=qux', 'quux=quuz']; - const extractedSpanContext = Context.getExtractedSpanContext( + const extractedSpanContext = getExtractedSpanContext( httpTraceContext.extract(Context.ROOT_CONTEXT, carrier) ); assert.deepStrictEqual(extractedSpanContext, { @@ -195,7 +199,7 @@ describe('HttpTraceContext', () => { Object.getOwnPropertyNames(testCases).forEach(testCase => { carrier[TRACE_PARENT_HEADER] = testCases[testCase]; - const extractedSpanContext = Context.getExtractedSpanContext( + const extractedSpanContext = getExtractedSpanContext( httpTraceContext.extract(Context.ROOT_CONTEXT, carrier) ); assert.deepStrictEqual(extractedSpanContext, undefined, testCase); diff --git a/packages/opentelemetry-plugin-http/src/http.ts b/packages/opentelemetry-plugin-http/src/http.ts index b5c3937b2c..19c9cb7a6f 100644 --- a/packages/opentelemetry-plugin-http/src/http.ts +++ b/packages/opentelemetry-plugin-http/src/http.ts @@ -16,12 +16,18 @@ import { CanonicalCode, + Context, Span, SpanKind, SpanOptions, Status, } from '@opentelemetry/api'; -import { BasePlugin, Context, isValid } from '@opentelemetry/core'; +import { + BasePlugin, + getExtractedSpanContext, + isValid, + setActiveSpan, +} from '@opentelemetry/core'; import { ClientRequest, IncomingMessage, @@ -299,7 +305,7 @@ export class HttpPlugin extends BasePlugin { // Using context directly like this is temporary. In a future PR, context // will be managed by the scope manager (which may be renamed to context manager?) - const spanContext = Context.getExtractedSpanContext( + const spanContext = getExtractedSpanContext( propagation.extract(Context.ROOT_CONTEXT, headers) ); if (spanContext && isValid(spanContext)) { @@ -412,10 +418,7 @@ export class HttpPlugin extends BasePlugin { .getHttpTextFormat() // Using context directly like this is temporary. In a future PR, context // will be managed by the scope manager (which may be renamed to context manager?) - .inject( - Context.setActiveSpan(Context.ROOT_CONTEXT, span), - options.headers! - ); + .inject(setActiveSpan(Context.ROOT_CONTEXT, span), options.headers!); const request: ClientRequest = plugin._safeExecute( span, diff --git a/packages/opentelemetry-plugin-http/test/utils/DummyPropagation.ts b/packages/opentelemetry-plugin-http/test/utils/DummyPropagation.ts index 9101ab7d3c..887b7127a0 100644 --- a/packages/opentelemetry-plugin-http/test/utils/DummyPropagation.ts +++ b/packages/opentelemetry-plugin-http/test/utils/DummyPropagation.ts @@ -13,21 +13,24 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { HttpTextFormat } from '@opentelemetry/api'; -import { Context } from '@opentelemetry/core'; +import { Context, HttpTextFormat } from '@opentelemetry/api'; +import { + setExtractedSpanContext, + getParentSpanContext, +} from '@opentelemetry/core'; import * as http from 'http'; export class DummyPropagation implements HttpTextFormat { static TRACE_CONTEXT_KEY = 'x-dummy-trace-id'; static SPAN_CONTEXT_KEY = 'x-dummy-span-id'; extract(context: Context, carrier: http.OutgoingHttpHeaders) { - return Context.setExtractedSpanContext(Context.ROOT_CONTEXT, { + return setExtractedSpanContext(Context.ROOT_CONTEXT, { traceId: carrier[DummyPropagation.TRACE_CONTEXT_KEY] as string, spanId: DummyPropagation.SPAN_CONTEXT_KEY, }); } inject(context: Context, headers: { [custom: string]: string }): void { - const spanContext = Context.getParentSpanContext(context); + const spanContext = getParentSpanContext(context); if (!spanContext) return; headers[DummyPropagation.TRACE_CONTEXT_KEY] = spanContext.traceId; headers[DummyPropagation.SPAN_CONTEXT_KEY] = spanContext.spanId; diff --git a/packages/opentelemetry-plugin-https/package.json b/packages/opentelemetry-plugin-https/package.json index ce24710c22..6e179098db 100644 --- a/packages/opentelemetry-plugin-https/package.json +++ b/packages/opentelemetry-plugin-https/package.json @@ -42,7 +42,6 @@ }, "devDependencies": { "@opentelemetry/node": "^0.4.0", - "@opentelemetry/scope-base": "^0.4.0", "@opentelemetry/tracing": "^0.4.0", "@types/got": "^9.6.7", "@types/mocha": "^5.2.7", diff --git a/packages/opentelemetry-plugin-https/test/utils/DummyPropagation.ts b/packages/opentelemetry-plugin-https/test/utils/DummyPropagation.ts index 7bf3510ea7..5c65bd16d7 100644 --- a/packages/opentelemetry-plugin-https/test/utils/DummyPropagation.ts +++ b/packages/opentelemetry-plugin-https/test/utils/DummyPropagation.ts @@ -13,21 +13,24 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { HttpTextFormat } from '@opentelemetry/api'; -import { Context } from '@opentelemetry/core'; +import { Context, HttpTextFormat } from '@opentelemetry/api'; +import { + setExtractedSpanContext, + getParentSpanContext, +} from '@opentelemetry/core'; import * as http from 'http'; export class DummyPropagation implements HttpTextFormat { static TRACE_CONTEXT_KEY = 'x-dummy-trace-id'; static SPAN_CONTEXT_KEY = 'x-dummy-span-id'; extract(context: Context, carrier: http.OutgoingHttpHeaders) { - return Context.setExtractedSpanContext(context, { + return setExtractedSpanContext(context, { traceId: carrier[DummyPropagation.TRACE_CONTEXT_KEY] as string, spanId: DummyPropagation.SPAN_CONTEXT_KEY, }); } inject(context: Context, headers: { [custom: string]: string }): void { - const spanContext = Context.getParentSpanContext(context); + const spanContext = getParentSpanContext(context); if (!spanContext) return; headers[DummyPropagation.TRACE_CONTEXT_KEY] = spanContext.traceId; headers[DummyPropagation.SPAN_CONTEXT_KEY] = spanContext.spanId; diff --git a/packages/opentelemetry-plugin-xml-http-request/src/xhr.ts b/packages/opentelemetry-plugin-xml-http-request/src/xhr.ts index 70aa3bb7ef..8aea846312 100644 --- a/packages/opentelemetry-plugin-xml-http-request/src/xhr.ts +++ b/packages/opentelemetry-plugin-xml-http-request/src/xhr.ts @@ -14,16 +14,16 @@ * limitations under the License. */ +import * as types from '@opentelemetry/api'; import { BasePlugin, - Context, hrTime, isUrlIgnored, isWrapped, otperformance, + setActiveSpan, urlMatches, } from '@opentelemetry/core'; -import * as types from '@opentelemetry/api'; import { addSpanNetworkEvent, getResource, @@ -90,9 +90,9 @@ export class XMLHttpRequestPlugin extends BasePlugin { const headers: { [key: string]: unknown } = {}; this._tracer .getHttpTextFormat() - // Using context directly like this is temporary. In a future PR, context + // Using context direclty like this is temporary. In a future PR, context // will be managed by the scope manager (which may be renamed to context manager?) - .inject(Context.setActiveSpan(Context.ROOT_CONTEXT, span), headers); + .inject(setActiveSpan(types.Context.ROOT_CONTEXT, span), headers); Object.keys(headers).forEach(key => { xhr.setRequestHeader(key, String(headers[key])); diff --git a/packages/opentelemetry-propagator-jaeger/src/JaegerHttpTraceFormat.ts b/packages/opentelemetry-propagator-jaeger/src/JaegerHttpTraceFormat.ts index 6fb7c78b0a..05e5980915 100644 --- a/packages/opentelemetry-propagator-jaeger/src/JaegerHttpTraceFormat.ts +++ b/packages/opentelemetry-propagator-jaeger/src/JaegerHttpTraceFormat.ts @@ -14,8 +14,16 @@ * limitations under the License. */ -import { HttpTextFormat, SpanContext, TraceFlags } from '@opentelemetry/api'; -import { Context } from '@opentelemetry/core'; +import { + Context, + HttpTextFormat, + SpanContext, + TraceFlags, +} from '@opentelemetry/api'; +import { + getParentSpanContext, + setExtractedSpanContext, +} from '@opentelemetry/core'; export const UBER_TRACE_ID_HEADER = 'uber-trace-id'; @@ -45,7 +53,7 @@ export class JaegerHttpTraceFormat implements HttpTextFormat { } inject(context: Context, carrier: { [key: string]: unknown }) { - const spanContext = Context.getParentSpanContext(context); + const spanContext = getParentSpanContext(context); if (!spanContext) return; const traceFlags = `0${( @@ -67,7 +75,7 @@ export class JaegerHttpTraceFormat implements HttpTextFormat { const spanContext = deserializeSpanContext(uberTraceId); if (!spanContext) return context; - return Context.setExtractedSpanContext(context, spanContext); + return setExtractedSpanContext(context, spanContext); } } diff --git a/packages/opentelemetry-propagator-jaeger/test/JaegerHttpTraceFormat.test.ts b/packages/opentelemetry-propagator-jaeger/test/JaegerHttpTraceFormat.test.ts index f641ebdf6d..c8e5e1b406 100644 --- a/packages/opentelemetry-propagator-jaeger/test/JaegerHttpTraceFormat.test.ts +++ b/packages/opentelemetry-propagator-jaeger/test/JaegerHttpTraceFormat.test.ts @@ -14,8 +14,11 @@ * limitations under the License. */ -import { SpanContext, TraceFlags } from '@opentelemetry/api'; -import { Context } from '@opentelemetry/core'; +import { Context, SpanContext, TraceFlags } from '@opentelemetry/api'; +import { + getExtractedSpanContext, + setExtractedSpanContext, +} from '@opentelemetry/core'; import * as assert from 'assert'; import { JaegerHttpTraceFormat, @@ -41,7 +44,7 @@ describe('JaegerHttpTraceFormat', () => { }; jaegerHttpTraceFormat.inject( - Context.setExtractedSpanContext(Context.ROOT_CONTEXT, spanContext), + setExtractedSpanContext(Context.ROOT_CONTEXT, spanContext), carrier ); assert.deepStrictEqual( @@ -58,7 +61,7 @@ describe('JaegerHttpTraceFormat', () => { }; customJaegerHttpTraceFormat.inject( - Context.setExtractedSpanContext(Context.ROOT_CONTEXT, spanContext), + setExtractedSpanContext(Context.ROOT_CONTEXT, spanContext), carrier ); assert.deepStrictEqual( @@ -72,7 +75,7 @@ describe('JaegerHttpTraceFormat', () => { it('should extract context of a sampled span from carrier', () => { carrier[UBER_TRACE_ID_HEADER] = 'd4cda95b652f4a1592b449d5929fda1b:6e0c63257de34c92:0:01'; - const extractedSpanContext = Context.getExtractedSpanContext( + const extractedSpanContext = getExtractedSpanContext( jaegerHttpTraceFormat.extract(Context.ROOT_CONTEXT, carrier) ); @@ -87,7 +90,7 @@ describe('JaegerHttpTraceFormat', () => { it('should extract context of a sampled span from carrier with 1 bit flag', () => { carrier[UBER_TRACE_ID_HEADER] = '9c41e35aeb6d1272:45fd2a9709dadcf1:a13699e3fb724f40:1'; - const extractedSpanContext = Context.getExtractedSpanContext( + const extractedSpanContext = getExtractedSpanContext( jaegerHttpTraceFormat.extract(Context.ROOT_CONTEXT, carrier) ); @@ -102,7 +105,7 @@ describe('JaegerHttpTraceFormat', () => { it('should extract context of a sampled span from UTF-8 encoded carrier', () => { carrier[UBER_TRACE_ID_HEADER] = 'ac1f3dc3c2c0b06e%3A5ac292c4a11a163e%3Ac086aaa825821068%3A1'; - const extractedSpanContext = Context.getExtractedSpanContext( + const extractedSpanContext = getExtractedSpanContext( jaegerHttpTraceFormat.extract(Context.ROOT_CONTEXT, carrier) ); @@ -117,7 +120,7 @@ describe('JaegerHttpTraceFormat', () => { it('should use custom header if provided', () => { carrier[customHeader] = 'd4cda95b652f4a1592b449d5929fda1b:6e0c63257de34c92:0:01'; - const extractedSpanContext = Context.getExtractedSpanContext( + const extractedSpanContext = getExtractedSpanContext( customJaegerHttpTraceFormat.extract(Context.ROOT_CONTEXT, carrier) ); @@ -131,7 +134,7 @@ describe('JaegerHttpTraceFormat', () => { it('returns undefined if UBER_TRACE_ID_HEADER header is missing', () => { assert.deepStrictEqual( - Context.getExtractedSpanContext( + getExtractedSpanContext( jaegerHttpTraceFormat.extract(Context.ROOT_CONTEXT, carrier) ), undefined @@ -141,7 +144,7 @@ describe('JaegerHttpTraceFormat', () => { it('returns undefined if UBER_TRACE_ID_HEADER header is invalid', () => { carrier[UBER_TRACE_ID_HEADER] = 'invalid!'; assert.deepStrictEqual( - Context.getExtractedSpanContext( + getExtractedSpanContext( jaegerHttpTraceFormat.extract(Context.ROOT_CONTEXT, carrier) ), undefined diff --git a/packages/opentelemetry-scope-base/src/context.ts b/packages/opentelemetry-scope-base/src/context.ts index aeb809bb1c..d1f944bd5e 100644 --- a/packages/opentelemetry-scope-base/src/context.ts +++ b/packages/opentelemetry-scope-base/src/context.ts @@ -23,8 +23,9 @@ type Store = { [identifer: string]: unknown }; * but create a new one with updated values. */ export class Context { - private _context: Store; + private _currentContext: Store; + /** The root context is used as the default parent context when there is no active context */ public static readonly ROOT_CONTEXT = new Context(); /** @@ -32,8 +33,8 @@ export class Context { * * @param context a context from which to inherit values */ - protected constructor(parentContext: Store = {}) { - this._context = Object.assign(Object.create(null), parentContext); + private constructor(parentContext: Store = {}) { + this._currentContext = Object.assign(Object.create(null), parentContext); } /** @@ -42,7 +43,7 @@ export class Context { * @param key key which identifies a context value */ getValue(key: string): unknown { - return this._context[key]; + return this._currentContext[key]; } /** @@ -53,8 +54,8 @@ export class Context { * @param value value to set for the given key */ setValue(key: string, value: unknown): Context { - const context = new Context(this._context); - context._context[key] = value; + const context = new Context(this._currentContext); + context._currentContext[key] = value; return context; } @@ -65,8 +66,8 @@ export class Context { * @param key context key for which to clear a value */ deleteValue(key: string): Context { - const context = new Context(this._context); - delete context._context[key]; + const context = new Context(this._currentContext); + delete context._currentContext[key]; return context; } } diff --git a/packages/opentelemetry-shim-opentracing/src/shim.ts b/packages/opentelemetry-shim-opentracing/src/shim.ts index 9fa2a0cc9e..c5d09eb8e7 100644 --- a/packages/opentelemetry-shim-opentracing/src/shim.ts +++ b/packages/opentelemetry-shim-opentracing/src/shim.ts @@ -15,7 +15,11 @@ */ import * as types from '@opentelemetry/api'; -import { Context, NoopLogger } from '@opentelemetry/core'; +import { + getExtractedSpanContext, + NoopLogger, + setExtractedSpanContext, +} from '@opentelemetry/core'; import * as opentracing from 'opentracing'; function translateReferences( @@ -131,8 +135,8 @@ export class TracerShim extends opentracing.Tracer { this._tracer .getHttpTextFormat() .inject( - Context.setExtractedSpanContext( - Context.ROOT_CONTEXT, + setExtractedSpanContext( + types.Context.ROOT_CONTEXT, opentelemSpanContext ), carrier @@ -153,10 +157,10 @@ export class TracerShim extends opentracing.Tracer { // tslint:disable-next-line:no-switch-case-fall-through case opentracing.FORMAT_HTTP_HEADERS: case opentracing.FORMAT_TEXT_MAP: - const context = Context.getExtractedSpanContext( + const context = getExtractedSpanContext( this._tracer .getHttpTextFormat() - .extract(Context.ROOT_CONTEXT, carrier) + .extract(types.Context.ROOT_CONTEXT, carrier) ); if (!context) { return null; diff --git a/packages/opentelemetry-tracing/src/Tracer.ts b/packages/opentelemetry-tracing/src/Tracer.ts index 3cccbfce04..6bff5f84d7 100644 --- a/packages/opentelemetry-tracing/src/Tracer.ts +++ b/packages/opentelemetry-tracing/src/Tracer.ts @@ -16,19 +16,20 @@ import * as types from '@opentelemetry/api'; import { - randomTraceId, + ConsoleLogger, + getActiveSpan, isValid, - randomSpanId, NoRecordingSpan, - ConsoleLogger, - Context, + randomSpanId, + randomTraceId, + setActiveSpan, } from '@opentelemetry/core'; -import { TracerConfig, TraceParams } from './types'; import { ScopeManager } from '@opentelemetry/scope-base'; +import { BasicTracerProvider } from './BasicTracerProvider'; +import { DEFAULT_CONFIG } from './config'; import { Span } from './Span'; +import { TraceParams, TracerConfig } from './types'; import { mergeConfig } from './utility'; -import { DEFAULT_CONFIG } from './config'; -import { BasicTracerProvider } from './BasicTracerProvider'; /** * This class represents a basic tracer. @@ -111,7 +112,7 @@ export class Tracer implements types.Tracer { */ getCurrentSpan(): types.Span | undefined { // Get the current Span from the context or null if none found. - return Context.getActiveSpan(this._scopeManager.active()); + return getActiveSpan(this._scopeManager.active()); } /** @@ -123,7 +124,7 @@ export class Tracer implements types.Tracer { ): ReturnType { // Set given span to context. return this._scopeManager.with( - Context.setActiveSpan(this._scopeManager.active(), span), + setActiveSpan(this._scopeManager.active(), span), fn ); } @@ -135,7 +136,7 @@ export class Tracer implements types.Tracer { return this._scopeManager.bind( target, span - ? Context.setActiveSpan(this._scopeManager.active(), span) + ? setActiveSpan(this._scopeManager.active(), span) : this._scopeManager.active() ); } diff --git a/packages/opentelemetry-tracing/test/BasicTracerRegistry.test.ts b/packages/opentelemetry-tracing/test/BasicTracerRegistry.test.ts index 7f8e8047ce..f829de1ff7 100644 --- a/packages/opentelemetry-tracing/test/BasicTracerRegistry.test.ts +++ b/packages/opentelemetry-tracing/test/BasicTracerRegistry.test.ts @@ -14,15 +14,15 @@ * limitations under the License. */ -import { TraceFlags } from '@opentelemetry/api'; +import { Context, TraceFlags } from '@opentelemetry/api'; import { ALWAYS_SAMPLER, BinaryTraceContext, - Context, HttpTraceContext, NEVER_SAMPLER, NoopLogger, NoRecordingSpan, + setActiveSpan, TraceState, } from '@opentelemetry/core'; import { NoopScopeManager, ScopeManager } from '@opentelemetry/scope-base'; @@ -317,7 +317,7 @@ describe('BasicTracerProvider', () => { const tracer = new BasicTracerProvider({ scopeManager: { active: () => - Context.setActiveSpan(Context.ROOT_CONTEXT, ('foo' as any) as Span), + setActiveSpan(Context.ROOT_CONTEXT, ('foo' as any) as Span), } as ScopeManager, }).getTracer('default'); assert.deepStrictEqual(tracer.getCurrentSpan(), 'foo'); diff --git a/packages/opentelemetry-web/src/StackScopeManager.ts b/packages/opentelemetry-web/src/StackScopeManager.ts index 5fe39459e8..72ee4712c6 100644 --- a/packages/opentelemetry-web/src/StackScopeManager.ts +++ b/packages/opentelemetry-web/src/StackScopeManager.ts @@ -14,8 +14,8 @@ * limitations under the License. */ +import { Context } from '@opentelemetry/api'; import { ScopeManager } from '@opentelemetry/scope-base'; -import { Context } from '@opentelemetry/core'; /** * Stack Scope Manager for managing the state in web diff --git a/packages/opentelemetry-web/test/StackScopeManager.test.ts b/packages/opentelemetry-web/test/StackScopeManager.test.ts index 680334a591..3d419aa0cb 100644 --- a/packages/opentelemetry-web/test/StackScopeManager.test.ts +++ b/packages/opentelemetry-web/test/StackScopeManager.test.ts @@ -16,7 +16,7 @@ import * as assert from 'assert'; import { StackScopeManager } from '../src'; -import { Context } from '@opentelemetry/core'; +import { Context } from '@opentelemetry/api'; describe('StackScopeManager', () => { let scopeManager: StackScopeManager; From d89fcc6d1749d095f11b19a2cc1ac9ce63f0533b Mon Sep 17 00:00:00 2001 From: Daniel Dyla Date: Fri, 7 Feb 2020 13:37:27 -0500 Subject: [PATCH 16/23] chore: remove extra allocation --- packages/opentelemetry-scope-base/src/context.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/opentelemetry-scope-base/src/context.ts b/packages/opentelemetry-scope-base/src/context.ts index d1f944bd5e..4a66b771e8 100644 --- a/packages/opentelemetry-scope-base/src/context.ts +++ b/packages/opentelemetry-scope-base/src/context.ts @@ -33,7 +33,7 @@ export class Context { * * @param context a context from which to inherit values */ - private constructor(parentContext: Store = {}) { + private constructor(parentContext?: Store) { this._currentContext = Object.assign(Object.create(null), parentContext); } From 68234f742c73367875ddcbed886c3852c3108ba5 Mon Sep 17 00:00:00 2001 From: Daniel Dyla Date: Fri, 7 Feb 2020 13:43:58 -0500 Subject: [PATCH 17/23] chore: review comments --- .../opentelemetry-core/test/context/HttpTraceContext.test.ts | 2 +- packages/opentelemetry-plugin-http/src/http.ts | 4 +++- packages/opentelemetry-scope-base/src/context.ts | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/opentelemetry-core/test/context/HttpTraceContext.test.ts b/packages/opentelemetry-core/test/context/HttpTraceContext.test.ts index fb53cec343..bc74b8d682 100644 --- a/packages/opentelemetry-core/test/context/HttpTraceContext.test.ts +++ b/packages/opentelemetry-core/test/context/HttpTraceContext.test.ts @@ -75,7 +75,7 @@ describe('HttpTraceContext', () => { }); }); - describe('.extract())', () => { + describe('.extract()', () => { it('should extract context of a sampled span from carrier', () => { carrier[TRACE_PARENT_HEADER] = '00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01'; diff --git a/packages/opentelemetry-plugin-http/src/http.ts b/packages/opentelemetry-plugin-http/src/http.ts index 19c9cb7a6f..e72e7a75b3 100644 --- a/packages/opentelemetry-plugin-http/src/http.ts +++ b/packages/opentelemetry-plugin-http/src/http.ts @@ -414,11 +414,13 @@ export class HttpPlugin extends BasePlugin { }; const span = plugin._startHttpSpan(operationName, spanOptions); + + if (!options.headers) options.headers = {}; plugin._tracer .getHttpTextFormat() // Using context directly like this is temporary. In a future PR, context // will be managed by the scope manager (which may be renamed to context manager?) - .inject(setActiveSpan(Context.ROOT_CONTEXT, span), options.headers!); + .inject(setActiveSpan(Context.ROOT_CONTEXT, span), options.headers); const request: ClientRequest = plugin._safeExecute( span, diff --git a/packages/opentelemetry-scope-base/src/context.ts b/packages/opentelemetry-scope-base/src/context.ts index 4a66b771e8..dffebb98e4 100644 --- a/packages/opentelemetry-scope-base/src/context.ts +++ b/packages/opentelemetry-scope-base/src/context.ts @@ -31,7 +31,7 @@ export class Context { /** * Construct a new context which inherits values from an optional parent context. * - * @param context a context from which to inherit values + * @param parentContext a context from which to inherit values */ private constructor(parentContext?: Store) { this._currentContext = Object.assign(Object.create(null), parentContext); From 2987a5962966abf90e91e3081ad42fc658f0067d Mon Sep 17 00:00:00 2001 From: Daniel Dyla Date: Wed, 12 Feb 2020 10:13:45 -0500 Subject: [PATCH 18/23] chore: use variables for context keys --- .../opentelemetry-core/src/context/context.ts | 15 ++++++++++----- .../test/ZoneScopeManager.test.ts | 4 ++-- .../test/StackScopeManager.test.ts | 4 ++-- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/packages/opentelemetry-core/src/context/context.ts b/packages/opentelemetry-core/src/context/context.ts index e1fec8b8b8..ee293556a2 100644 --- a/packages/opentelemetry-core/src/context/context.ts +++ b/packages/opentelemetry-core/src/context/context.ts @@ -17,13 +17,16 @@ import { Span, SpanContext } from '@opentelemetry/api'; import { Context } from '@opentelemetry/scope-base'; +const ACTIVE_SPAN_KEY = 'ACTIVE_SPAN'; +const EXTRACTED_SPAN_CONTEXT_KEY = 'EXTRACTED_SPAN_CONTEXT'; + /** * Return the active span if one exists * * @param context context to get span from */ export function getActiveSpan(context: Context): Span | undefined { - return (context.getValue('ACTIVE_SPAN') as Span) || undefined; + return (context.getValue(ACTIVE_SPAN_KEY) as Span) || undefined; } /** @@ -33,7 +36,7 @@ export function getActiveSpan(context: Context): Span | undefined { * @param span span to set active */ export function setActiveSpan(context: Context, span: Span): Context { - return context.setValue('ACTIVE_SPAN', span); + return context.setValue(ACTIVE_SPAN_KEY, span); } /** @@ -45,7 +48,7 @@ export function getExtractedSpanContext( context: Context ): SpanContext | undefined { return ( - (context.getValue('EXTRACTED_SPAN_CONTEXT') as SpanContext) || undefined + (context.getValue(EXTRACTED_SPAN_CONTEXT_KEY) as SpanContext) || undefined ); } @@ -59,7 +62,7 @@ export function setExtractedSpanContext( context: Context, spanContext: SpanContext ): Context { - return context.setValue('EXTRACTED_SPAN_CONTEXT', spanContext); + return context.setValue(EXTRACTED_SPAN_CONTEXT_KEY, spanContext); } /** @@ -69,6 +72,8 @@ export function setExtractedSpanContext( * * @param context context to get values from */ -export function getParentSpanContext(context: Context) { +export function getParentSpanContext( + context: Context +): SpanContext | undefined { return getActiveSpan(context)?.context() || getExtractedSpanContext(context); } diff --git a/packages/opentelemetry-scope-zone-peer-dep/test/ZoneScopeManager.test.ts b/packages/opentelemetry-scope-zone-peer-dep/test/ZoneScopeManager.test.ts index 8973c7e06e..57a4694ce2 100644 --- a/packages/opentelemetry-scope-zone-peer-dep/test/ZoneScopeManager.test.ts +++ b/packages/opentelemetry-scope-zone-peer-dep/test/ZoneScopeManager.test.ts @@ -42,7 +42,7 @@ describe('ZoneScopeManager', () => { assert.doesNotThrow(() => { assert(scopeManager.enable() === scopeManager, 'should return this'); scopeManager.with(ctx, () => { - assert(scopeManager.active() === ctx, 'should has root scope'); + assert(scopeManager.active() === ctx, 'should have root scope'); }); }); }); @@ -56,7 +56,7 @@ describe('ZoneScopeManager', () => { scopeManager.with(ctx, () => { assert( scopeManager.active() === Context.ROOT_CONTEXT, - 'should has root scope' + 'should have root scope' ); }); }); diff --git a/packages/opentelemetry-web/test/StackScopeManager.test.ts b/packages/opentelemetry-web/test/StackScopeManager.test.ts index 3d419aa0cb..4e12c9f123 100644 --- a/packages/opentelemetry-web/test/StackScopeManager.test.ts +++ b/packages/opentelemetry-web/test/StackScopeManager.test.ts @@ -36,7 +36,7 @@ describe('StackScopeManager', () => { assert(scopeManager.enable() === scopeManager, 'should return this'); assert( scopeManager.active() === Context.ROOT_CONTEXT, - 'should has root scope' + 'should have root scope' ); }); }); @@ -48,7 +48,7 @@ describe('StackScopeManager', () => { assert(scopeManager.disable() === scopeManager, 'should return this'); assert( scopeManager.active() === Context.ROOT_CONTEXT, - 'should has no scope' + 'should have no scope' ); }); }); From 5ad438311fe3df3ed8c8b992de9f4dbc2fdc12e7 Mon Sep 17 00:00:00 2001 From: Daniel Dyla Date: Wed, 12 Feb 2020 15:53:42 -0500 Subject: [PATCH 19/23] chore: use unique symbols to identify context values --- .../opentelemetry-core/src/context/context.ts | 4 +- .../test/AsyncHooksScopeManager.test.ts | 25 +++++----- .../opentelemetry-scope-base/src/context.ts | 25 +++++----- .../test/NoopScopeManager.test.ts | 3 +- .../test/ZoneScopeManager.test.ts | 48 ++++++++++--------- .../test/StackScopeManager.test.ts | 27 ++++++----- 6 files changed, 70 insertions(+), 62 deletions(-) diff --git a/packages/opentelemetry-core/src/context/context.ts b/packages/opentelemetry-core/src/context/context.ts index ee293556a2..4ffb649cef 100644 --- a/packages/opentelemetry-core/src/context/context.ts +++ b/packages/opentelemetry-core/src/context/context.ts @@ -17,8 +17,8 @@ import { Span, SpanContext } from '@opentelemetry/api'; import { Context } from '@opentelemetry/scope-base'; -const ACTIVE_SPAN_KEY = 'ACTIVE_SPAN'; -const EXTRACTED_SPAN_CONTEXT_KEY = 'EXTRACTED_SPAN_CONTEXT'; +const ACTIVE_SPAN_KEY = Context.getKey('OpenTelemetry Context Key ACTIVE_SPAN'); +const EXTRACTED_SPAN_CONTEXT_KEY = Context.getKey('OpenTelemetry Context Key EXTRACTED_SPAN_CONTEXT'); /** * Return the active span if one exists diff --git a/packages/opentelemetry-scope-async-hooks/test/AsyncHooksScopeManager.test.ts b/packages/opentelemetry-scope-async-hooks/test/AsyncHooksScopeManager.test.ts index 6927810b6e..dff24c7774 100644 --- a/packages/opentelemetry-scope-async-hooks/test/AsyncHooksScopeManager.test.ts +++ b/packages/opentelemetry-scope-async-hooks/test/AsyncHooksScopeManager.test.ts @@ -21,6 +21,7 @@ import { Context } from '@opentelemetry/scope-base'; describe('AsyncHooksScopeManager', () => { let scopeManager: AsyncHooksScopeManager; + const key1 = Context.getKey('test key 1'); beforeEach(() => { scopeManager = new AsyncHooksScopeManager(); @@ -55,7 +56,7 @@ describe('AsyncHooksScopeManager', () => { }); it('should run the callback (object as target)', done => { - const test = Context.ROOT_CONTEXT.setValue('a', 1); + const test = Context.ROOT_CONTEXT.setValue(key1, 1); scopeManager.with(test, () => { assert.strictEqual(scopeManager.active(), test, 'should have scope'); return done(); @@ -80,8 +81,8 @@ describe('AsyncHooksScopeManager', () => { }); it('should finally restore an old scope', done => { - const scope1 = Context.ROOT_CONTEXT.setValue('name', 'scope1'); - const scope2 = Context.ROOT_CONTEXT.setValue('name', 'scope2'); + const scope1 = Context.ROOT_CONTEXT.setValue(key1, 'scope1'); + const scope2 = Context.ROOT_CONTEXT.setValue(key1, 'scope2'); scopeManager.with(scope1, () => { assert.strictEqual(scopeManager.active(), scope1); scopeManager.with(scope2, () => { @@ -113,7 +114,7 @@ describe('AsyncHooksScopeManager', () => { }); it('should return current scope (when enabled)', done => { - const scope = Context.ROOT_CONTEXT.setValue('a', 1); + const scope = Context.ROOT_CONTEXT.setValue(key1, 1); const fn = scopeManager.bind(() => { assert.strictEqual(scopeManager.active(), scope, 'should have scope'); return done(); @@ -127,7 +128,7 @@ describe('AsyncHooksScopeManager', () => { */ it('should return current scope (when disabled)', done => { scopeManager.disable(); - const scope = Context.ROOT_CONTEXT.setValue('a', 1); + const scope = Context.ROOT_CONTEXT.setValue(key1, 1); const fn = scopeManager.bind(() => { assert.strictEqual(scopeManager.active(), scope, 'should have scope'); return done(); @@ -137,7 +138,7 @@ describe('AsyncHooksScopeManager', () => { it('should fail to return current scope (when disabled + async op)', done => { scopeManager.disable(); - const scope = Context.ROOT_CONTEXT.setValue('a', 1); + const scope = Context.ROOT_CONTEXT.setValue(key1, 1); const fn = scopeManager.bind(() => { setTimeout(() => { assert.strictEqual( @@ -153,7 +154,7 @@ describe('AsyncHooksScopeManager', () => { it('should return current scope (when re-enabled + async op)', done => { scopeManager.enable(); - const scope = Context.ROOT_CONTEXT.setValue('a', 1); + const scope = Context.ROOT_CONTEXT.setValue(key1, 1); const fn = scopeManager.bind(() => { setTimeout(() => { assert.strictEqual(scopeManager.active(), scope, 'should have scope'); @@ -179,7 +180,7 @@ describe('AsyncHooksScopeManager', () => { it('should return current scope and removeListener (when enabled)', done => { const ee = new EventEmitter(); - const scope = Context.ROOT_CONTEXT.setValue('a', 1); + const scope = Context.ROOT_CONTEXT.setValue(key1, 1); const patchedEe = scopeManager.bind(ee, scope); const handler = () => { assert.deepStrictEqual(scopeManager.active(), scope); @@ -194,7 +195,7 @@ describe('AsyncHooksScopeManager', () => { it('should return current scope and removeAllListener (when enabled)', done => { const ee = new EventEmitter(); - const scope = Context.ROOT_CONTEXT.setValue('a', 1); + const scope = Context.ROOT_CONTEXT.setValue(key1, 1); const patchedEe = scopeManager.bind(ee, scope); const handler = () => { assert.deepStrictEqual(scopeManager.active(), scope); @@ -214,7 +215,7 @@ describe('AsyncHooksScopeManager', () => { it('should return scope (when disabled)', done => { scopeManager.disable(); const ee = new EventEmitter(); - const scope = Context.ROOT_CONTEXT.setValue('a', 1); + const scope = Context.ROOT_CONTEXT.setValue(key1, 1); const patchedEe = scopeManager.bind(ee, scope); const handler = () => { assert.deepStrictEqual(scopeManager.active(), scope); @@ -231,7 +232,7 @@ describe('AsyncHooksScopeManager', () => { it('should not return current scope (when disabled + async op)', done => { scopeManager.disable(); const ee = new EventEmitter(); - const scope = Context.ROOT_CONTEXT.setValue('a', 1); + const scope = Context.ROOT_CONTEXT.setValue(key1, 1); const patchedEe = scopeManager.bind(ee, scope); const handler = () => { setImmediate(() => { @@ -249,7 +250,7 @@ describe('AsyncHooksScopeManager', () => { it('should return current scope (when enabled + async op)', done => { scopeManager.enable(); const ee = new EventEmitter(); - const scope = Context.ROOT_CONTEXT.setValue('a', 1); + const scope = Context.ROOT_CONTEXT.setValue(key1, 1); const patchedEe = scopeManager.bind(ee, scope); const handler = () => { setImmediate(() => { diff --git a/packages/opentelemetry-scope-base/src/context.ts b/packages/opentelemetry-scope-base/src/context.ts index dffebb98e4..67443d5bff 100644 --- a/packages/opentelemetry-scope-base/src/context.ts +++ b/packages/opentelemetry-scope-base/src/context.ts @@ -14,8 +14,6 @@ * limitations under the License. */ -/** Map of identifiers to an unknown value used internally to store context */ -type Store = { [identifer: string]: unknown }; /** * Class which stores and manages current context values. All methods which @@ -23,18 +21,23 @@ type Store = { [identifer: string]: unknown }; * but create a new one with updated values. */ export class Context { - private _currentContext: Store; + private _currentContext: Map; /** The root context is used as the default parent context when there is no active context */ public static readonly ROOT_CONTEXT = new Context(); + /** Get a key to uniquely identify a context value */ + public static getKey(description: string) { + return Symbol(description); + } + /** * Construct a new context which inherits values from an optional parent context. * * @param parentContext a context from which to inherit values */ - private constructor(parentContext?: Store) { - this._currentContext = Object.assign(Object.create(null), parentContext); + private constructor(parentContext?: Map) { + this._currentContext = parentContext ? new Map(parentContext) : new Map(); } /** @@ -42,8 +45,8 @@ export class Context { * * @param key key which identifies a context value */ - getValue(key: string): unknown { - return this._currentContext[key]; + getValue(key: symbol): unknown { + return this._currentContext.get(key); } /** @@ -53,9 +56,9 @@ export class Context { * @param key context key for which to set the value * @param value value to set for the given key */ - setValue(key: string, value: unknown): Context { + setValue(key: symbol, value: unknown): Context { const context = new Context(this._currentContext); - context._currentContext[key] = value; + context._currentContext.set(key, value); return context; } @@ -65,9 +68,9 @@ export class Context { * * @param key context key for which to clear a value */ - deleteValue(key: string): Context { + deleteValue(key: symbol): Context { const context = new Context(this._currentContext); - delete context._currentContext[key]; + context._currentContext.delete(key); return context; } } diff --git a/packages/opentelemetry-scope-base/test/NoopScopeManager.test.ts b/packages/opentelemetry-scope-base/test/NoopScopeManager.test.ts index 5a9245b666..0e68a4be79 100644 --- a/packages/opentelemetry-scope-base/test/NoopScopeManager.test.ts +++ b/packages/opentelemetry-scope-base/test/NoopScopeManager.test.ts @@ -44,7 +44,8 @@ describe('NoopScopeManager', () => { }); it('should run the callback (object as target)', done => { - const test = Context.ROOT_CONTEXT.setValue('a', 1); + const key = Context.getKey('test key 1'); + const test = Context.ROOT_CONTEXT.setValue(key, 1); scopeManager.with(test, () => { assert.strictEqual( scopeManager.active(), diff --git a/packages/opentelemetry-scope-zone-peer-dep/test/ZoneScopeManager.test.ts b/packages/opentelemetry-scope-zone-peer-dep/test/ZoneScopeManager.test.ts index 57a4694ce2..bed361e41a 100644 --- a/packages/opentelemetry-scope-zone-peer-dep/test/ZoneScopeManager.test.ts +++ b/packages/opentelemetry-scope-zone-peer-dep/test/ZoneScopeManager.test.ts @@ -24,6 +24,8 @@ let clock: any; describe('ZoneScopeManager', () => { let scopeManager: ZoneScopeManager; + const key1 = Context.getKey('test key 1'); + const key2 = Context.getKey('test key 2'); beforeEach(() => { clock = sinon.useFakeTimers(); @@ -38,7 +40,7 @@ describe('ZoneScopeManager', () => { describe('.enable()', () => { it('should work', () => { - const ctx = Context.ROOT_CONTEXT.setValue('a', 1); + const ctx = Context.ROOT_CONTEXT.setValue(key1, 1); assert.doesNotThrow(() => { assert(scopeManager.enable() === scopeManager, 'should return this'); scopeManager.with(ctx, () => { @@ -50,7 +52,7 @@ describe('ZoneScopeManager', () => { describe('.disable()', () => { it('should work', () => { - const ctx = Context.ROOT_CONTEXT.setValue('a', 1); + const ctx = Context.ROOT_CONTEXT.setValue(key1, 1); assert.doesNotThrow(() => { assert(scopeManager.disable() === scopeManager, 'should return this'); scopeManager.with(ctx, () => { @@ -69,7 +71,7 @@ describe('ZoneScopeManager', () => { }); it('should run the callback (object as target)', done => { - const test = Context.ROOT_CONTEXT.setValue('a', 1); + const test = Context.ROOT_CONTEXT.setValue(key1, 1); scopeManager.with(test, () => { assert.strictEqual(scopeManager.active(), test, 'should have scope'); return done(); @@ -94,9 +96,9 @@ describe('ZoneScopeManager', () => { }); it('should finally restore an old scope, including the async task', done => { - const scope1 = Context.ROOT_CONTEXT.setValue('scope', 'scope1'); - const scope2 = Context.ROOT_CONTEXT.setValue('scope', 'scope2'); - const scope3 = Context.ROOT_CONTEXT.setValue('scope', 'scope3'); + const scope1 = Context.ROOT_CONTEXT.setValue(key1, 'scope1'); + const scope2 = Context.ROOT_CONTEXT.setValue(key1, 'scope2'); + const scope3 = Context.ROOT_CONTEXT.setValue(key1, 'scope3'); scopeManager.with(scope1, () => { assert.strictEqual(scopeManager.active(), scope1); @@ -118,9 +120,9 @@ describe('ZoneScopeManager', () => { }); it('should finally restore an old scope when scope is an object, including the async task', done => { - const scope1 = Context.ROOT_CONTEXT.setValue('x', 1); - const scope2 = Context.ROOT_CONTEXT.setValue('x', 2); - const scope3 = Context.ROOT_CONTEXT.setValue('x', 3); + const scope1 = Context.ROOT_CONTEXT.setValue(key1, 1); + const scope2 = Context.ROOT_CONTEXT.setValue(key1, 2); + const scope3 = Context.ROOT_CONTEXT.setValue(key1, 3); scopeManager.with(scope1, () => { assert.strictEqual(scopeManager.active(), scope1); scopeManager.with(scope2, () => { @@ -140,29 +142,29 @@ describe('ZoneScopeManager', () => { assert.strictEqual(scopeManager.active(), window); }); it('should correctly return the scopes for 3 parallel actions', () => { - const rootSpan = Context.ROOT_CONTEXT.setValue('name', 'root'); + const rootSpan = Context.ROOT_CONTEXT.setValue(key1, 'root'); scopeManager.with(rootSpan, () => { assert.ok( - scopeManager.active().getValue('name') === 'root', + scopeManager.active().getValue(key1) === 'root', 'Current span is rootSpan' ); const concurrentSpan1 = Context.ROOT_CONTEXT.setValue( - 'span', + key2, 'concurrentSpan1' ); const concurrentSpan2 = Context.ROOT_CONTEXT.setValue( - 'span', + key2, 'concurrentSpan2' ); const concurrentSpan3 = Context.ROOT_CONTEXT.setValue( - 'span', + key2, 'concurrentSpan3' ); scopeManager.with(concurrentSpan1, () => { setTimeout(() => { assert.ok( - scopeManager.active().getValue('span') === concurrentSpan1, + scopeManager.active().getValue(key2) === concurrentSpan1, 'Current span is concurrentSpan1' ); }, 10); @@ -171,7 +173,7 @@ describe('ZoneScopeManager', () => { scopeManager.with(concurrentSpan2, () => { setTimeout(() => { assert.ok( - scopeManager.active().getValue('span') === concurrentSpan2, + scopeManager.active().getValue(key2) === concurrentSpan2, 'Current span is concurrentSpan2' ); }, 20); @@ -180,7 +182,7 @@ describe('ZoneScopeManager', () => { scopeManager.with(concurrentSpan3, () => { setTimeout(() => { assert.ok( - scopeManager.active().getValue('span') === concurrentSpan3, + scopeManager.active().getValue(key2) === concurrentSpan3, 'Current span is concurrentSpan3' ); }, 30); @@ -199,12 +201,12 @@ describe('ZoneScopeManager', () => { } getTitle() { - return (scopeManager.active().getValue('obj') as Obj).title; + return (scopeManager.active().getValue(key1) as Obj).title; } } const obj1 = new Obj('a1'); - const ctx = Context.ROOT_CONTEXT.setValue('obj', obj1); + const ctx = Context.ROOT_CONTEXT.setValue(key1, obj1); obj1.title = 'a2'; const obj2 = new Obj('b1'); const wrapper: any = scopeManager.bind(obj2.getTitle, ctx); @@ -230,7 +232,7 @@ describe('ZoneScopeManager', () => { }); it('should return current scope (when enabled)', done => { - const scope = Context.ROOT_CONTEXT.setValue('ctx', { a: 1 }); + const scope = Context.ROOT_CONTEXT.setValue(key1, { a: 1 }); const fn: any = scopeManager.bind(() => { assert.strictEqual(scopeManager.active(), scope, 'should have scope'); return done(); @@ -240,7 +242,7 @@ describe('ZoneScopeManager', () => { it('should return root scope (when disabled)', done => { scopeManager.disable(); - const scope = Context.ROOT_CONTEXT.setValue('ctx', { a: 1 }); + const scope = Context.ROOT_CONTEXT.setValue(key1, { a: 1 }); const fn: any = scopeManager.bind(() => { assert.strictEqual( scopeManager.active(), @@ -253,7 +255,7 @@ describe('ZoneScopeManager', () => { }); it('should bind the the certain scope to the target "addEventListener" function', done => { - const scope1 = Context.ROOT_CONTEXT.setValue('a', 1); + const scope1 = Context.ROOT_CONTEXT.setValue(key1, 1); const element = document.createElement('div'); scopeManager.bind(element, scope1); @@ -277,7 +279,7 @@ describe('ZoneScopeManager', () => { }); it('should preserve zone when creating new click event inside zone', done => { - const scope1 = Context.ROOT_CONTEXT.setValue('a', 1); + const scope1 = Context.ROOT_CONTEXT.setValue(key1, 1); const element = document.createElement('div'); scopeManager.bind(element, scope1); diff --git a/packages/opentelemetry-web/test/StackScopeManager.test.ts b/packages/opentelemetry-web/test/StackScopeManager.test.ts index 4e12c9f123..0f33e996ba 100644 --- a/packages/opentelemetry-web/test/StackScopeManager.test.ts +++ b/packages/opentelemetry-web/test/StackScopeManager.test.ts @@ -20,6 +20,7 @@ import { Context } from '@opentelemetry/api'; describe('StackScopeManager', () => { let scopeManager: StackScopeManager; + const key1 = Context.getKey('test key 1'); beforeEach(() => { scopeManager = new StackScopeManager(); @@ -60,7 +61,7 @@ describe('StackScopeManager', () => { }); it('should run the callback (object as target)', done => { - const test = Context.ROOT_CONTEXT.setValue('a', 1); + const test = Context.ROOT_CONTEXT.setValue(key1, 1); scopeManager.with(test, () => { assert.strictEqual(scopeManager.active(), test, 'should have scope'); return done(); @@ -85,9 +86,9 @@ describe('StackScopeManager', () => { }); it('should finally restore an old scope', done => { - const scope1 = Context.ROOT_CONTEXT.setValue('name', 'scope1'); - const scope2 = Context.ROOT_CONTEXT.setValue('name', 'scope2'); - const scope3 = Context.ROOT_CONTEXT.setValue('name', 'scope3'); + const scope1 = Context.ROOT_CONTEXT.setValue(key1, 'scope1'); + const scope2 = Context.ROOT_CONTEXT.setValue(key1, 'scope2'); + const scope3 = Context.ROOT_CONTEXT.setValue(key1, 'scope3'); scopeManager.with(scope1, () => { assert.strictEqual(scopeManager.active(), scope1); scopeManager.with(scope2, () => { @@ -104,9 +105,9 @@ describe('StackScopeManager', () => { }); it('should finally restore an old scope when scope is an object', done => { - const scope1 = Context.ROOT_CONTEXT.setValue('a', 1); - const scope2 = Context.ROOT_CONTEXT.setValue('a', 2); - const scope3 = Context.ROOT_CONTEXT.setValue('a', 3); + const scope1 = Context.ROOT_CONTEXT.setValue(key1, 1); + const scope2 = Context.ROOT_CONTEXT.setValue(key1, 2); + const scope3 = Context.ROOT_CONTEXT.setValue(key1, 3); scopeManager.with(scope1, () => { assert.strictEqual(scopeManager.active(), scope1); scopeManager.with(scope2, () => { @@ -133,12 +134,12 @@ describe('StackScopeManager', () => { } getTitle() { - return (scopeManager.active().getValue('obj') as Obj).title; + return (scopeManager.active().getValue(key1) as Obj).title; } } const obj1 = new Obj('a1'); - const ctx = Context.ROOT_CONTEXT.setValue('obj', obj1); + const ctx = Context.ROOT_CONTEXT.setValue(key1, obj1); obj1.title = 'a2'; const obj2 = new Obj('b1'); const wrapper: any = scopeManager.bind(obj2.getTitle, ctx); @@ -146,19 +147,19 @@ describe('StackScopeManager', () => { }); it('should return the same target (when enabled)', () => { - const test = Context.ROOT_CONTEXT.setValue('a', 1); + const test = Context.ROOT_CONTEXT.setValue(key1, 1); assert.deepStrictEqual(scopeManager.bind(test), test); }); it('should return the same target (when disabled)', () => { scopeManager.disable(); - const test = Context.ROOT_CONTEXT.setValue('a', 1); + const test = Context.ROOT_CONTEXT.setValue(key1, 1); assert.deepStrictEqual(scopeManager.bind(test), test); scopeManager.enable(); }); it('should return current scope (when enabled)', done => { - const scope = Context.ROOT_CONTEXT.setValue('a', 1); + const scope = Context.ROOT_CONTEXT.setValue(key1, 1); const fn: any = scopeManager.bind(() => { assert.strictEqual(scopeManager.active(), scope, 'should have scope'); return done(); @@ -168,7 +169,7 @@ describe('StackScopeManager', () => { it('should return current scope (when disabled)', done => { scopeManager.disable(); - const scope = Context.ROOT_CONTEXT.setValue('a', 1); + const scope = Context.ROOT_CONTEXT.setValue(key1, 1); const fn: any = scopeManager.bind(() => { assert.strictEqual(scopeManager.active(), scope, 'should have scope'); return done(); From d5ef1d8b896ad3e02033db60ad777a35c53a19ff Mon Sep 17 00:00:00 2001 From: Daniel Dyla Date: Wed, 12 Feb 2020 16:06:38 -0500 Subject: [PATCH 20/23] chore: use TODO context reference --- packages/opentelemetry-plugin-http/src/http.ts | 4 ++-- .../opentelemetry-plugin-xml-http-request/src/xhr.ts | 2 +- packages/opentelemetry-scope-base/src/context.ts | 9 +++++++++ 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/packages/opentelemetry-plugin-http/src/http.ts b/packages/opentelemetry-plugin-http/src/http.ts index e72e7a75b3..d11b4b0708 100644 --- a/packages/opentelemetry-plugin-http/src/http.ts +++ b/packages/opentelemetry-plugin-http/src/http.ts @@ -306,7 +306,7 @@ export class HttpPlugin extends BasePlugin { // Using context directly like this is temporary. In a future PR, context // will be managed by the scope manager (which may be renamed to context manager?) const spanContext = getExtractedSpanContext( - propagation.extract(Context.ROOT_CONTEXT, headers) + propagation.extract(Context.TODO, headers) ); if (spanContext && isValid(spanContext)) { spanOptions.parent = spanContext; @@ -420,7 +420,7 @@ export class HttpPlugin extends BasePlugin { .getHttpTextFormat() // Using context directly like this is temporary. In a future PR, context // will be managed by the scope manager (which may be renamed to context manager?) - .inject(setActiveSpan(Context.ROOT_CONTEXT, span), options.headers); + .inject(setActiveSpan(Context.TODO, span), options.headers); const request: ClientRequest = plugin._safeExecute( span, diff --git a/packages/opentelemetry-plugin-xml-http-request/src/xhr.ts b/packages/opentelemetry-plugin-xml-http-request/src/xhr.ts index 8aea846312..cef6eff144 100644 --- a/packages/opentelemetry-plugin-xml-http-request/src/xhr.ts +++ b/packages/opentelemetry-plugin-xml-http-request/src/xhr.ts @@ -92,7 +92,7 @@ export class XMLHttpRequestPlugin extends BasePlugin { .getHttpTextFormat() // Using context direclty like this is temporary. In a future PR, context // will be managed by the scope manager (which may be renamed to context manager?) - .inject(setActiveSpan(types.Context.ROOT_CONTEXT, span), headers); + .inject(setActiveSpan(types.Context.TODO, span), headers); Object.keys(headers).forEach(key => { xhr.setRequestHeader(key, String(headers[key])); diff --git a/packages/opentelemetry-scope-base/src/context.ts b/packages/opentelemetry-scope-base/src/context.ts index 67443d5bff..491f863264 100644 --- a/packages/opentelemetry-scope-base/src/context.ts +++ b/packages/opentelemetry-scope-base/src/context.ts @@ -26,6 +26,15 @@ export class Context { /** The root context is used as the default parent context when there is no active context */ public static readonly ROOT_CONTEXT = new Context(); + + /** + * This is another identifier to the root context which allows developers to easily search the + * codebase for direct uses of context which need to be removed in later PRs. + * + * It's existence is temporary and it should be removed when all references are fixed. + */ + public static readonly TODO = Context.ROOT_CONTEXT; + /** Get a key to uniquely identify a context value */ public static getKey(description: string) { return Symbol(description); From e750d4b67415a7f074492d4a772bb225f21fb16d Mon Sep 17 00:00:00 2001 From: Daniel Dyla Date: Wed, 12 Feb 2020 16:06:50 -0500 Subject: [PATCH 21/23] chore: remove root context from dummy propagator --- .../opentelemetry-plugin-http/test/utils/DummyPropagation.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/opentelemetry-plugin-http/test/utils/DummyPropagation.ts b/packages/opentelemetry-plugin-http/test/utils/DummyPropagation.ts index 887b7127a0..5c65bd16d7 100644 --- a/packages/opentelemetry-plugin-http/test/utils/DummyPropagation.ts +++ b/packages/opentelemetry-plugin-http/test/utils/DummyPropagation.ts @@ -24,7 +24,7 @@ export class DummyPropagation implements HttpTextFormat { static TRACE_CONTEXT_KEY = 'x-dummy-trace-id'; static SPAN_CONTEXT_KEY = 'x-dummy-span-id'; extract(context: Context, carrier: http.OutgoingHttpHeaders) { - return setExtractedSpanContext(Context.ROOT_CONTEXT, { + return setExtractedSpanContext(context, { traceId: carrier[DummyPropagation.TRACE_CONTEXT_KEY] as string, spanId: DummyPropagation.SPAN_CONTEXT_KEY, }); From adff03cb64f931f5a282cb52e115262196fc58f4 Mon Sep 17 00:00:00 2001 From: Daniel Dyla Date: Wed, 12 Feb 2020 16:09:50 -0500 Subject: [PATCH 22/23] chore: lint --- packages/opentelemetry-core/src/context/context.ts | 4 +++- packages/opentelemetry-scope-base/src/context.ts | 6 ++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/opentelemetry-core/src/context/context.ts b/packages/opentelemetry-core/src/context/context.ts index 4ffb649cef..16f430f13f 100644 --- a/packages/opentelemetry-core/src/context/context.ts +++ b/packages/opentelemetry-core/src/context/context.ts @@ -18,7 +18,9 @@ import { Span, SpanContext } from '@opentelemetry/api'; import { Context } from '@opentelemetry/scope-base'; const ACTIVE_SPAN_KEY = Context.getKey('OpenTelemetry Context Key ACTIVE_SPAN'); -const EXTRACTED_SPAN_CONTEXT_KEY = Context.getKey('OpenTelemetry Context Key EXTRACTED_SPAN_CONTEXT'); +const EXTRACTED_SPAN_CONTEXT_KEY = Context.getKey( + 'OpenTelemetry Context Key EXTRACTED_SPAN_CONTEXT' +); /** * Return the active span if one exists diff --git a/packages/opentelemetry-scope-base/src/context.ts b/packages/opentelemetry-scope-base/src/context.ts index 491f863264..23dcf84dd2 100644 --- a/packages/opentelemetry-scope-base/src/context.ts +++ b/packages/opentelemetry-scope-base/src/context.ts @@ -14,7 +14,6 @@ * limitations under the License. */ - /** * Class which stores and manages current context values. All methods which * update context such as get and delete do not modify an existing context, @@ -26,11 +25,10 @@ export class Context { /** The root context is used as the default parent context when there is no active context */ public static readonly ROOT_CONTEXT = new Context(); - - /** + /** * This is another identifier to the root context which allows developers to easily search the * codebase for direct uses of context which need to be removed in later PRs. - * + * * It's existence is temporary and it should be removed when all references are fixed. */ public static readonly TODO = Context.ROOT_CONTEXT; From 8e433f17a6d7a772a55fe57d72f143ca0b43f085 Mon Sep 17 00:00:00 2001 From: Daniel Dyla Date: Wed, 12 Feb 2020 16:36:10 -0500 Subject: [PATCH 23/23] chore: rename getKey to createKey --- packages/opentelemetry-core/src/context/context.ts | 6 ++++-- .../test/AsyncHooksScopeManager.test.ts | 2 +- packages/opentelemetry-scope-base/src/context.ts | 2 +- .../opentelemetry-scope-base/test/NoopScopeManager.test.ts | 2 +- .../test/ZoneScopeManager.test.ts | 4 ++-- packages/opentelemetry-web/test/StackScopeManager.test.ts | 2 +- 6 files changed, 10 insertions(+), 8 deletions(-) diff --git a/packages/opentelemetry-core/src/context/context.ts b/packages/opentelemetry-core/src/context/context.ts index 16f430f13f..0ae080b3ed 100644 --- a/packages/opentelemetry-core/src/context/context.ts +++ b/packages/opentelemetry-core/src/context/context.ts @@ -17,8 +17,10 @@ import { Span, SpanContext } from '@opentelemetry/api'; import { Context } from '@opentelemetry/scope-base'; -const ACTIVE_SPAN_KEY = Context.getKey('OpenTelemetry Context Key ACTIVE_SPAN'); -const EXTRACTED_SPAN_CONTEXT_KEY = Context.getKey( +const ACTIVE_SPAN_KEY = Context.createKey( + 'OpenTelemetry Context Key ACTIVE_SPAN' +); +const EXTRACTED_SPAN_CONTEXT_KEY = Context.createKey( 'OpenTelemetry Context Key EXTRACTED_SPAN_CONTEXT' ); diff --git a/packages/opentelemetry-scope-async-hooks/test/AsyncHooksScopeManager.test.ts b/packages/opentelemetry-scope-async-hooks/test/AsyncHooksScopeManager.test.ts index dff24c7774..ea85caf6a3 100644 --- a/packages/opentelemetry-scope-async-hooks/test/AsyncHooksScopeManager.test.ts +++ b/packages/opentelemetry-scope-async-hooks/test/AsyncHooksScopeManager.test.ts @@ -21,7 +21,7 @@ import { Context } from '@opentelemetry/scope-base'; describe('AsyncHooksScopeManager', () => { let scopeManager: AsyncHooksScopeManager; - const key1 = Context.getKey('test key 1'); + const key1 = Context.createKey('test key 1'); beforeEach(() => { scopeManager = new AsyncHooksScopeManager(); diff --git a/packages/opentelemetry-scope-base/src/context.ts b/packages/opentelemetry-scope-base/src/context.ts index 23dcf84dd2..8b42d7e9af 100644 --- a/packages/opentelemetry-scope-base/src/context.ts +++ b/packages/opentelemetry-scope-base/src/context.ts @@ -34,7 +34,7 @@ export class Context { public static readonly TODO = Context.ROOT_CONTEXT; /** Get a key to uniquely identify a context value */ - public static getKey(description: string) { + public static createKey(description: string) { return Symbol(description); } diff --git a/packages/opentelemetry-scope-base/test/NoopScopeManager.test.ts b/packages/opentelemetry-scope-base/test/NoopScopeManager.test.ts index 0e68a4be79..8daf15ded0 100644 --- a/packages/opentelemetry-scope-base/test/NoopScopeManager.test.ts +++ b/packages/opentelemetry-scope-base/test/NoopScopeManager.test.ts @@ -44,7 +44,7 @@ describe('NoopScopeManager', () => { }); it('should run the callback (object as target)', done => { - const key = Context.getKey('test key 1'); + const key = Context.createKey('test key 1'); const test = Context.ROOT_CONTEXT.setValue(key, 1); scopeManager.with(test, () => { assert.strictEqual( diff --git a/packages/opentelemetry-scope-zone-peer-dep/test/ZoneScopeManager.test.ts b/packages/opentelemetry-scope-zone-peer-dep/test/ZoneScopeManager.test.ts index bed361e41a..33e62c29e6 100644 --- a/packages/opentelemetry-scope-zone-peer-dep/test/ZoneScopeManager.test.ts +++ b/packages/opentelemetry-scope-zone-peer-dep/test/ZoneScopeManager.test.ts @@ -24,8 +24,8 @@ let clock: any; describe('ZoneScopeManager', () => { let scopeManager: ZoneScopeManager; - const key1 = Context.getKey('test key 1'); - const key2 = Context.getKey('test key 2'); + const key1 = Context.createKey('test key 1'); + const key2 = Context.createKey('test key 2'); beforeEach(() => { clock = sinon.useFakeTimers(); diff --git a/packages/opentelemetry-web/test/StackScopeManager.test.ts b/packages/opentelemetry-web/test/StackScopeManager.test.ts index 0f33e996ba..bb0e92970d 100644 --- a/packages/opentelemetry-web/test/StackScopeManager.test.ts +++ b/packages/opentelemetry-web/test/StackScopeManager.test.ts @@ -20,7 +20,7 @@ import { Context } from '@opentelemetry/api'; describe('StackScopeManager', () => { let scopeManager: StackScopeManager; - const key1 = Context.getKey('test key 1'); + const key1 = Context.createKey('test key 1'); beforeEach(() => { scopeManager = new StackScopeManager();