From 5883e4e1780aa6e50ebafbf84de8032bbc2bc6a5 Mon Sep 17 00:00:00 2001 From: Daniel Dyla Date: Tue, 10 Mar 2020 08:39:05 -0400 Subject: [PATCH] Simplify SDK registration (#837) --- packages/opentelemetry-api/src/index.ts | 1 + packages/opentelemetry-node/package.json | 1 + .../src/NodeTracerProvider.ts | 21 ++++- .../test/registration.test.ts | 86 +++++++++++++++++++ .../src/BasicTracerProvider.ts | 32 +++++-- packages/opentelemetry-tracing/src/types.ts | 21 ++++- .../src/WebTracerProvider.ts | 23 ++++- .../test/registration.test.ts | 85 ++++++++++++++++++ 8 files changed, 261 insertions(+), 9 deletions(-) create mode 100644 packages/opentelemetry-node/test/registration.test.ts create mode 100644 packages/opentelemetry-web/test/registration.test.ts diff --git a/packages/opentelemetry-api/src/index.ts b/packages/opentelemetry-api/src/index.ts index e76488cf3..114bf875f 100644 --- a/packages/opentelemetry-api/src/index.ts +++ b/packages/opentelemetry-api/src/index.ts @@ -18,6 +18,7 @@ export * from './common/Logger'; export * from './common/Time'; export * from './context/propagation/getter'; export * from './context/propagation/HttpTextFormat'; +export * from './context/propagation/NoopHttpTextFormat'; export * from './context/propagation/setter'; export * from './correlation_context/CorrelationContext'; export * from './correlation_context/EntryValue'; diff --git a/packages/opentelemetry-node/package.json b/packages/opentelemetry-node/package.json index e310875fd..767c174e5 100644 --- a/packages/opentelemetry-node/package.json +++ b/packages/opentelemetry-node/package.json @@ -43,6 +43,7 @@ "devDependencies": { "@types/mocha": "^5.2.5", "@types/node": "^12.6.8", + "@opentelemetry/scope-base": "^0.4.0", "@types/semver": "^6.0.1", "@types/shimmer": "^1.0.1", "codecov": "^3.6.1", diff --git a/packages/opentelemetry-node/src/NodeTracerProvider.ts b/packages/opentelemetry-node/src/NodeTracerProvider.ts index acdf887dc..8cb200541 100644 --- a/packages/opentelemetry-node/src/NodeTracerProvider.ts +++ b/packages/opentelemetry-node/src/NodeTracerProvider.ts @@ -14,12 +14,20 @@ * limitations under the License. */ -import { BasicTracerProvider } from '@opentelemetry/tracing'; +import { AsyncHooksScopeManager } from '@opentelemetry/scope-async-hooks'; +import { + BasicTracerProvider, + SDKRegistrationConfig, +} from '@opentelemetry/tracing'; import { DEFAULT_INSTRUMENTATION_PLUGINS, NodeTracerConfig } from './config'; import { PluginLoader } from './instrumentation/PluginLoader'; /** - * This class represents a node tracer with `async_hooks` module. + * Register this TracerProvider for use with the OpenTelemetry API. + * Undefined values may be replaced with defaults, and + * null values will be skipped. + * + * @param config Configuration object for SDK registration */ export class NodeTracerProvider extends BasicTracerProvider { private readonly _pluginLoader: PluginLoader; @@ -37,4 +45,13 @@ export class NodeTracerProvider extends BasicTracerProvider { stop() { this._pluginLoader.unload(); } + + register(config: SDKRegistrationConfig = {}) { + if (config.contextManager === undefined) { + config.contextManager = new AsyncHooksScopeManager(); + config.contextManager.enable(); + } + + super.register(config); + } } diff --git a/packages/opentelemetry-node/test/registration.test.ts b/packages/opentelemetry-node/test/registration.test.ts new file mode 100644 index 000000000..3ce0fc8e2 --- /dev/null +++ b/packages/opentelemetry-node/test/registration.test.ts @@ -0,0 +1,86 @@ +/*! + * 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 { + context, + NoopHttpTextFormat, + NoopTracerProvider, + propagation, + trace, +} from '@opentelemetry/api'; +import { HttpTraceContext } from '@opentelemetry/core'; +import { AsyncHooksScopeManager } from '@opentelemetry/scope-async-hooks'; +import { NoopScopeManager } from '@opentelemetry/scope-base'; +import * as assert from 'assert'; +import { NodeTracerProvider } from '../src'; + +describe('API registration', () => { + beforeEach(() => { + context.initGlobalContextManager(new NoopScopeManager()); + propagation.initGlobalPropagator(new NoopHttpTextFormat()); + trace.initGlobalTracerProvider(new NoopTracerProvider()); + }); + + it('should register default implementations', () => { + const tracerProvider = new NodeTracerProvider(); + tracerProvider.register(); + + assert.ok(context['_scopeManager'] instanceof AsyncHooksScopeManager); + assert.ok(propagation['_propagator'] instanceof HttpTraceContext); + assert.ok(trace['_tracerProvider'] === tracerProvider); + }); + + it('should register configured implementations', () => { + const tracerProvider = new NodeTracerProvider(); + + const contextManager = new NoopScopeManager(); + const propagator = new NoopHttpTextFormat(); + + tracerProvider.register({ + contextManager, + propagator, + }); + + assert.ok(context['_scopeManager'] === contextManager); + assert.ok(propagation['_propagator'] === propagator); + + assert.ok(trace['_tracerProvider'] === tracerProvider); + }); + + it('should skip null context manager', () => { + const tracerProvider = new NodeTracerProvider(); + tracerProvider.register({ + contextManager: null, + }); + + assert.ok(context['_scopeManager'] instanceof NoopScopeManager); + + assert.ok(propagation['_propagator'] instanceof HttpTraceContext); + assert.ok(trace['_tracerProvider'] === tracerProvider); + }); + + it('should skip null propagator', () => { + const tracerProvider = new NodeTracerProvider(); + tracerProvider.register({ + propagator: null, + }); + + assert.ok(propagation['_propagator'] instanceof NoopHttpTextFormat); + + assert.ok(context['_scopeManager'] instanceof AsyncHooksScopeManager); + assert.ok(trace['_tracerProvider'] === tracerProvider); + }); +}); diff --git a/packages/opentelemetry-tracing/src/BasicTracerProvider.ts b/packages/opentelemetry-tracing/src/BasicTracerProvider.ts index 218ada5b4..8b9da493c 100644 --- a/packages/opentelemetry-tracing/src/BasicTracerProvider.ts +++ b/packages/opentelemetry-tracing/src/BasicTracerProvider.ts @@ -14,23 +14,23 @@ * limitations under the License. */ -import { ConsoleLogger } from '@opentelemetry/core'; -import * as types from '@opentelemetry/api'; +import * as api from '@opentelemetry/api'; +import { ConsoleLogger, HttpTraceContext } from '@opentelemetry/core'; import { SpanProcessor, Tracer } from '.'; import { DEFAULT_CONFIG } from './config'; import { MultiSpanProcessor } from './MultiSpanProcessor'; import { NoopSpanProcessor } from './NoopSpanProcessor'; -import { TracerConfig } from './types'; +import { SDKRegistrationConfig, TracerConfig } from './types'; /** * This class represents a basic tracer provider which platform libraries can extend */ -export class BasicTracerProvider implements types.TracerProvider { +export class BasicTracerProvider implements api.TracerProvider { private readonly _registeredSpanProcessors: SpanProcessor[] = []; private readonly _tracers: Map = new Map(); activeSpanProcessor = new NoopSpanProcessor(); - readonly logger: types.Logger; + readonly logger: api.Logger; constructor(private _config: TracerConfig = DEFAULT_CONFIG) { this.logger = _config.logger || new ConsoleLogger(_config.logLevel); @@ -59,4 +59,26 @@ export class BasicTracerProvider implements types.TracerProvider { getActiveSpanProcessor(): SpanProcessor { return this.activeSpanProcessor; } + + /** + * Register this TracerProvider for use with the OpenTelemetry API. + * Undefined values may be replaced with defaults, and + * null values will be skipped. + * + * @param config Configuration object for SDK registration + */ + register(config: SDKRegistrationConfig = {}) { + api.trace.initGlobalTracerProvider(this); + if (config.propagator === undefined) { + config.propagator = new HttpTraceContext(); + } + + if (config.contextManager) { + api.context.initGlobalContextManager(config.contextManager); + } + + if (config.propagator) { + api.propagation.initGlobalPropagator(config.propagator); + } + } } diff --git a/packages/opentelemetry-tracing/src/types.ts b/packages/opentelemetry-tracing/src/types.ts index 3fdd9240f..107f1f80a 100644 --- a/packages/opentelemetry-tracing/src/types.ts +++ b/packages/opentelemetry-tracing/src/types.ts @@ -14,8 +14,14 @@ * limitations under the License. */ -import { Attributes, Logger, Sampler } from '@opentelemetry/api'; +import { + Attributes, + HttpTextFormat, + Logger, + Sampler, +} from '@opentelemetry/api'; import { LogLevel } from '@opentelemetry/core'; +import { ScopeManager } from '@opentelemetry/scope-base'; /** * TracerConfig provides an interface for configuring a Basic Tracer. @@ -44,6 +50,19 @@ export interface TracerConfig { traceParams?: TraceParams; } +/** + * Configuration options for registering the API with the SDK. + * Undefined values may be substituted for defaults, and null + * values will not be registered. + */ +export interface SDKRegistrationConfig { + /** Propagator to register as the global propagator */ + propagator?: HttpTextFormat | null; + + /** Context manager to register as the global context manager */ + contextManager?: ScopeManager | null; +} + /** Global configuration of trace service */ export interface TraceParams { /** numberOfAttributesPerSpan is number of attributes per span */ diff --git a/packages/opentelemetry-web/src/WebTracerProvider.ts b/packages/opentelemetry-web/src/WebTracerProvider.ts index 05f066cd5..265e06ab4 100644 --- a/packages/opentelemetry-web/src/WebTracerProvider.ts +++ b/packages/opentelemetry-web/src/WebTracerProvider.ts @@ -15,7 +15,12 @@ */ import { BasePlugin } from '@opentelemetry/core'; -import { BasicTracerProvider, TracerConfig } from '@opentelemetry/tracing'; +import { + BasicTracerProvider, + SDKRegistrationConfig, + TracerConfig, +} from '@opentelemetry/tracing'; +import { StackScopeManager } from './StackScopeManager'; /** * WebTracerConfig provides an interface for configuring a Web Tracer. @@ -45,4 +50,20 @@ export class WebTracerProvider extends BasicTracerProvider { plugin.enable([], this, this.logger); } } + + /** + * Register this TracerProvider for use with the OpenTelemetry API. + * Undefined values may be replaced with defaults, and + * null values will be skipped. + * + * @param config Configuration object for SDK registration + */ + register(config: SDKRegistrationConfig = {}) { + if (config.contextManager === undefined) { + config.contextManager = new StackScopeManager(); + config.contextManager.enable(); + } + + super.register(config); + } } diff --git a/packages/opentelemetry-web/test/registration.test.ts b/packages/opentelemetry-web/test/registration.test.ts new file mode 100644 index 000000000..df7253481 --- /dev/null +++ b/packages/opentelemetry-web/test/registration.test.ts @@ -0,0 +1,85 @@ +/*! + * 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 { + context, + NoopHttpTextFormat, + NoopTracerProvider, + propagation, + trace, +} from '@opentelemetry/api'; +import { HttpTraceContext } from '@opentelemetry/core'; +import { NoopScopeManager } from '@opentelemetry/scope-base'; +import * as assert from 'assert'; +import { WebTracerProvider, StackScopeManager } from '../src'; + +describe('API registration', () => { + beforeEach(() => { + context.initGlobalContextManager(new NoopScopeManager()); + propagation.initGlobalPropagator(new NoopHttpTextFormat()); + trace.initGlobalTracerProvider(new NoopTracerProvider()); + }); + + it('should register default implementations', () => { + const tracerProvider = new WebTracerProvider(); + tracerProvider.register(); + + assert.ok(context['_scopeManager'] instanceof StackScopeManager); + assert.ok(propagation['_propagator'] instanceof HttpTraceContext); + assert.ok(trace['_tracerProvider'] === tracerProvider); + }); + + it('should register configured implementations', () => { + const tracerProvider = new WebTracerProvider(); + + const contextManager = new NoopScopeManager(); + const propagator = new NoopHttpTextFormat(); + + tracerProvider.register({ + contextManager, + propagator, + }); + + assert.ok(context['_scopeManager'] === contextManager); + assert.ok(propagation['_propagator'] === propagator); + + assert.ok(trace['_tracerProvider'] === tracerProvider); + }); + + it('should skip null context manager', () => { + const tracerProvider = new WebTracerProvider(); + tracerProvider.register({ + contextManager: null, + }); + + assert.ok(context['_scopeManager'] instanceof NoopScopeManager); + + assert.ok(propagation['_propagator'] instanceof HttpTraceContext); + assert.ok(trace['_tracerProvider'] === tracerProvider); + }); + + it('should skip null propagator', () => { + const tracerProvider = new WebTracerProvider(); + tracerProvider.register({ + propagator: null, + }); + + assert.ok(propagation['_propagator'] instanceof NoopHttpTextFormat); + + assert.ok(context['_scopeManager'] instanceof StackScopeManager); + assert.ok(trace['_tracerProvider'] === tracerProvider); + }); +});