diff --git a/experimental/CHANGELOG.md b/experimental/CHANGELOG.md index 93c3cbfcd23..50fc0cdd84b 100644 --- a/experimental/CHANGELOG.md +++ b/experimental/CHANGELOG.md @@ -10,6 +10,9 @@ For notes on migrating to 2.x / 0.200.x see [the upgrade guide](doc/upgrade-to-2 ### :rocket: Features +* feat(sdk-node): always set up propagtion and context manager [#5930](https://github.com/open-telemetry/opentelemetry-js/pull/5930) + * using `(new NodeSDK).start()` will now automatically set up a context management and propagation, even if no Trace SDK + is initialized. * feat(otlp-exporter-base, otlp-grpc-exporter-base): add an option to let an SDK distribution prepend their own user-agent string in HTTP & GRPC exporters [#5928](https://github.com/open-telemetry/opentelemetry-js/pull/5928) @david-luna ### :bug: Bug Fixes diff --git a/experimental/packages/opentelemetry-sdk-node/src/sdk.ts b/experimental/packages/opentelemetry-sdk-node/src/sdk.ts index 8ff8e2d6c20..ec7ff0c8464 100644 --- a/experimental/packages/opentelemetry-sdk-node/src/sdk.ts +++ b/experimental/packages/opentelemetry-sdk-node/src/sdk.ts @@ -14,13 +14,7 @@ * limitations under the License. */ -import { - ContextManager, - TextMapPropagator, - metrics, - diag, - DiagConsoleLogger, -} from '@opentelemetry/api'; +import { metrics, trace, diag, DiagConsoleLogger } from '@opentelemetry/api'; import { logs } from '@opentelemetry/api-logs'; import { Instrumentation, @@ -79,9 +73,14 @@ import { getResourceDetectorsFromEnv, getSpanProcessorsFromEnv, getPropagatorFromEnv, + setupPropagator, + setupContextManager, } from './utils'; -/** This class represents everything needed to register a fully configured OpenTelemetry Node.js SDK */ +type TracerProviderConfig = { + tracerConfig: NodeTracerConfig; + spanProcessors: SpanProcessor[]; +}; export type MeterProviderConfig = { /** @@ -129,6 +128,7 @@ function configureMetricProviderFromEnv(): IMetricReader[] { ); return metricReaders; } + enabledExporters.forEach(exporter => { if (exporter === 'otlp') { const protocol = @@ -201,13 +201,27 @@ function configureMetricProviderFromEnv(): IMetricReader[] { return metricReaders; } + +/** + * A setup helper for the OpenTelemetry SDKs (logs, metrics, traces). + *

After successful setup using {@link NodeSDK#start()}, use `@opentelemetry/api` to obtain the registered components. + *

Use the shutdown handler {@link NodeSDK#shutdown()} to ensure your telemetry is exported before the process exits. + * + * @example Register SDK by using environment variables + * const nodeSdk = new NodeSDK(); // providing no options uses OTEL_* environment variables for SDK setup. + * nodeSdk.start(); // registers all configured SDK components + * @example Override environment variable config with your own components + * const nodeSdk = new NodeSDK({ + * // override the list of metric reader with your own options and ignore environment variable config + * // explore the docs of other options to learn more! + * metricReaders: [ new PeriodicExportingMetricReader({ + * exporter: new OTLPMetricsExporter() + * })] + * }); + * nodeSdk.start(); // registers all configured SDK components + */ export class NodeSDK { - private _tracerProviderConfig?: { - tracerConfig: NodeTracerConfig; - spanProcessors: SpanProcessor[]; - contextManager?: ContextManager; - textMapPropagator?: TextMapPropagator; - }; + private _tracerProviderConfig?: TracerProviderConfig; private _loggerProviderConfig?: LoggerProviderConfig; private _meterProviderConfig?: MeterProviderConfig; private _instrumentations: Instrumentation[]; @@ -292,8 +306,6 @@ export class NodeSDK { this._tracerProviderConfig = { tracerConfig: tracerProviderConfig, spanProcessors, - contextManager: configuration.contextManager, - textMapPropagator: configuration.textMapPropagator, }; } @@ -350,6 +362,13 @@ export class NodeSDK { instrumentations: this._instrumentations, }); + setupContextManager(this._configuration?.contextManager); + setupPropagator( + this._configuration?.textMapPropagator === null + ? null // null means don't set, so we cannot fall back to env config. + : (this._configuration?.textMapPropagator ?? getPropagatorFromEnv()) + ); + if (this._autoDetectResources) { const internalConfig: ResourceDetectionConfig = { detectors: this._resourceDetectors, @@ -371,23 +390,14 @@ export class NodeSDK { ? this._tracerProviderConfig.spanProcessors : getSpanProcessorsFromEnv(); - this._tracerProvider = new NodeTracerProvider({ - ...this._configuration, - resource: this._resource, - spanProcessors, - }); - // Only register if there is a span processor if (spanProcessors.length > 0) { - this._tracerProvider.register({ - contextManager: - this._tracerProviderConfig?.contextManager ?? - // _tracerProviderConfig may be undefined if trace-specific settings are not provided - fall back to raw config - this._configuration?.contextManager, - propagator: - this._tracerProviderConfig?.textMapPropagator ?? - getPropagatorFromEnv(), + this._tracerProvider = new NodeTracerProvider({ + ...this._configuration, + resource: this._resource, + spanProcessors, }); + trace.setGlobalTracerProvider(this._tracerProvider); } if (this._loggerProviderConfig) { diff --git a/experimental/packages/opentelemetry-sdk-node/src/types.ts b/experimental/packages/opentelemetry-sdk-node/src/types.ts index 48b363a18f9..8a3f04ef885 100644 --- a/experimental/packages/opentelemetry-sdk-node/src/types.ts +++ b/experimental/packages/opentelemetry-sdk-node/src/types.ts @@ -31,7 +31,7 @@ import { export interface NodeSDKConfiguration { autoDetectResources: boolean; contextManager: ContextManager; - textMapPropagator: TextMapPropagator; + textMapPropagator: TextMapPropagator | null; /** @deprecated use logRecordProcessors instead*/ logRecordProcessor: LogRecordProcessor; logRecordProcessors?: LogRecordProcessor[]; diff --git a/experimental/packages/opentelemetry-sdk-node/src/utils.ts b/experimental/packages/opentelemetry-sdk-node/src/utils.ts index 7e977318032..5b4ac8443d2 100644 --- a/experimental/packages/opentelemetry-sdk-node/src/utils.ts +++ b/experimental/packages/opentelemetry-sdk-node/src/utils.ts @@ -14,7 +14,13 @@ * limitations under the License. */ -import { diag, TextMapPropagator } from '@opentelemetry/api'; +import { + context, + ContextManager, + diag, + propagation, + TextMapPropagator, +} from '@opentelemetry/api'; import { CompositePropagator, getStringFromEnv, @@ -43,6 +49,7 @@ import { } from '@opentelemetry/sdk-trace-base'; import { B3InjectEncoding, B3Propagator } from '@opentelemetry/propagator-b3'; import { JaegerPropagator } from '@opentelemetry/propagator-jaeger'; +import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks'; const RESOURCE_DETECTOR_ENVIRONMENT = 'env'; const RESOURCE_DETECTOR_HOST = 'host'; @@ -198,6 +205,10 @@ export function getPropagatorFromEnv(): TextMapPropagator | null | undefined { return undefined; } + if (propagatorsEnvVarValue.includes('none')) { + return null; + } + // Implementation note: this only contains specification required propagators that are actually hosted in this repo. // Any other propagators (like aws, aws-lambda, should go into `@opentelemetry/auto-configuration-propagators` instead). const propagatorsFactory = new Map TextMapPropagator>([ @@ -247,3 +258,47 @@ export function getPropagatorFromEnv(): TextMapPropagator | null | undefined { }); } } + +export function setupContextManager( + contextManager: ContextManager | null | undefined +) { + // null means 'do not register' + if (contextManager === null) { + return; + } + + // undefined means 'register default' + if (contextManager === undefined) { + const defaultContextManager = new AsyncLocalStorageContextManager(); + defaultContextManager.enable(); + context.setGlobalContextManager(defaultContextManager); + return; + } + + contextManager.enable(); + context.setGlobalContextManager(contextManager); +} + +export function setupPropagator( + propagator: TextMapPropagator | null | undefined +) { + // null means 'do not register' + if (propagator === null) { + return; + } + + // undefined means 'register default' + if (propagator === undefined) { + propagation.setGlobalPropagator( + new CompositePropagator({ + propagators: [ + new W3CTraceContextPropagator(), + new W3CBaggagePropagator(), + ], + }) + ); + return; + } + + propagation.setGlobalPropagator(propagator); +} diff --git a/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts b/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts index 55394ea426d..4ad72ce3236 100644 --- a/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts +++ b/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts @@ -28,7 +28,7 @@ import { AsyncHooksContextManager, AsyncLocalStorageContextManager, } from '@opentelemetry/context-async-hooks'; -import { CompositePropagator } from '@opentelemetry/core'; +import { W3CTraceContextPropagator } from '@opentelemetry/core'; import { JaegerExporter } from '@opentelemetry/exporter-jaeger'; import { AggregationTemporality, @@ -86,9 +86,22 @@ import { ZipkinExporter } from '@opentelemetry/exporter-zipkin'; import { ATTR_HOST_NAME, ATTR_PROCESS_PID } from './semconv'; +function assertDefaultContextManagerRegistered() { + assert.ok( + context['_getContextManager']().constructor.name === + AsyncLocalStorageContextManager.name + ); +} + +function assertDefaultPropagatorRegistered() { + assert.deepStrictEqual(propagation.fields(), [ + 'traceparent', + 'tracestate', + 'baggage', + ]); +} + describe('Node SDK', () => { - let ctxManager: any; - let propagator: any; let delegate: any; let logsDelegate: any; @@ -100,8 +113,6 @@ describe('Node SDK', () => { metrics.disable(); logs.disable(); - ctxManager = context['_getContextManager'](); - propagator = propagation['_getGlobalPropagator'](); delegate = (trace.getTracerProvider() as ProxyTracerProvider).getDelegate(); logsDelegate = ( logs.getLoggerProvider() as ProxyLoggerProvider @@ -113,9 +124,8 @@ describe('Node SDK', () => { }); describe('Basic Registration', () => { - it('should not register any unconfigured SDK components', async () => { - // need to set OTEL_TRACES_EXPORTER to none since default value is otlp - // which sets up an exporter and affects the context manager + it('should not register more than the minimal SDK components', async () => { + // need to set these to none, since the deafult value is 'otlp' env.OTEL_TRACES_EXPORTER = 'none'; env.OTEL_LOGS_EXPORTER = 'none'; env.OTEL_METRIC_EXPORTER = 'none'; @@ -125,16 +135,10 @@ describe('Node SDK', () => { sdk.start(); - assert.strictEqual( - context['_getContextManager'](), - ctxManager, - 'context manager should not change' - ); - assert.strictEqual( - propagation['_getGlobalPropagator'](), - propagator, - 'propagator should not change' - ); + // These are minimal OTel functionality and always registered. + assertDefaultContextManagerRegistered(); + assertDefaultPropagatorRegistered(); + assert.strictEqual( (trace.getTracerProvider() as ProxyTracerProvider).getDelegate(), delegate, @@ -195,13 +199,9 @@ describe('Node SDK', () => { assert.ok(!(metrics.getMeterProvider() instanceof MeterProvider)); - assert.ok( - context['_getContextManager']().constructor.name === - AsyncLocalStorageContextManager.name - ); - assert.ok( - propagation['_getGlobalPropagator']() instanceof CompositePropagator - ); + assertDefaultContextManagerRegistered(); + assertDefaultPropagatorRegistered(); + const apiTracerProvider = trace.getTracerProvider() as ProxyTracerProvider; assert.ok(apiTracerProvider.getDelegate() instanceof NodeTracerProvider); @@ -218,13 +218,9 @@ describe('Node SDK', () => { assert.ok(!(metrics.getMeterProvider() instanceof MeterProvider)); - assert.ok( - context['_getContextManager']().constructor.name === - AsyncLocalStorageContextManager.name - ); - assert.ok( - propagation['_getGlobalPropagator']() instanceof CompositePropagator - ); + assertDefaultContextManagerRegistered(); + assertDefaultPropagatorRegistered(); + const apiTracerProvider = trace.getTracerProvider() as ProxyTracerProvider; assert.ok(apiTracerProvider.getDelegate() instanceof NodeTracerProvider); @@ -248,13 +244,9 @@ describe('Node SDK', () => { assert.ok(!(metrics.getMeterProvider() instanceof MeterProvider)); - assert.ok( - context['_getContextManager']().constructor.name === - AsyncLocalStorageContextManager.name - ); - assert.ok( - propagation['_getGlobalPropagator']() instanceof CompositePropagator - ); + assertDefaultContextManagerRegistered(); + assertDefaultPropagatorRegistered(); + const apiTracerProvider = trace.getTracerProvider() as ProxyTracerProvider; const nodeTracerProvider = apiTracerProvider.getDelegate(); @@ -300,16 +292,9 @@ describe('Node SDK', () => { sdk.start(); - assert.strictEqual( - context['_getContextManager'](), - ctxManager, - 'context manager should not change' - ); - assert.strictEqual( - propagation['_getGlobalPropagator'](), - propagator, - 'propagator should not change' - ); + assertDefaultContextManagerRegistered(); + assertDefaultPropagatorRegistered(); + assert.strictEqual( (trace.getTracerProvider() as ProxyTracerProvider).getDelegate(), delegate, @@ -348,16 +333,9 @@ describe('Node SDK', () => { sdk.start(); - assert.strictEqual( - context['_getContextManager'](), - ctxManager, - 'context manager should not change' - ); - assert.strictEqual( - propagation['_getGlobalPropagator'](), - propagator, - 'propagator should not change' - ); + assertDefaultContextManagerRegistered(); + assertDefaultPropagatorRegistered(); + assert.strictEqual( (trace.getTracerProvider() as ProxyTracerProvider).getDelegate(), delegate, @@ -452,16 +430,9 @@ describe('Node SDK', () => { sdk.start(); - assert.strictEqual( - context['_getContextManager'](), - ctxManager, - 'context manager should not change' - ); - assert.strictEqual( - propagation['_getGlobalPropagator'](), - propagator, - 'propagator should not change' - ); + assertDefaultContextManagerRegistered(); + assertDefaultPropagatorRegistered(); + assert.strictEqual( (trace.getTracerProvider() as ProxyTracerProvider).getDelegate(), delegate, @@ -530,6 +501,22 @@ describe('Node SDK', () => { await sdk.shutdown(); }); + it('should register a propagator if only a propagator is provided', async () => { + // arrange + const expectedPropagator = new W3CTraceContextPropagator(); + const sdk = new NodeSDK({ + textMapPropagator: expectedPropagator, + }); + + // act + sdk.start(); + + // assert + const actualPropagator = propagation['_getGlobalPropagator'](); + assert.equal(actualPropagator, expectedPropagator); + await sdk.shutdown(); + }); + it('should register propagators as defined in OTEL_PROPAGATORS if trace SDK is configured', async () => { process.env.OTEL_PROPAGATORS = 'b3'; const sdk = new NodeSDK({ @@ -544,6 +531,52 @@ describe('Node SDK', () => { await sdk.shutdown(); delete process.env.OTEL_PROPAGATORS; }); + + it('should not register propagators OTEL_PROPAGATORS contains "none"', async () => { + process.env.OTEL_PROPAGATORS = 'none'; + const sdk = new NodeSDK({ + traceExporter: new ConsoleSpanExporter(), + autoDetectResources: false, + }); + + sdk.start(); + + assert.deepStrictEqual(propagation.fields(), []); + + await sdk.shutdown(); + delete process.env.OTEL_PROPAGATORS; + }); + + it('should not register propagators OTEL_PROPAGATORS contains "none" alongside valid propagator', async () => { + process.env.OTEL_PROPAGATORS = 'b3, none'; + const sdk = new NodeSDK({ + traceExporter: new ConsoleSpanExporter(), + autoDetectResources: false, + }); + + sdk.start(); + + assert.deepStrictEqual(propagation.fields(), []); + + await sdk.shutdown(); + delete process.env.OTEL_PROPAGATORS; + }); + + it('should not register propagators OTEL_PROPAGATORS contains valid propagator but option is set to null', async () => { + process.env.OTEL_PROPAGATORS = 'b3'; + const sdk = new NodeSDK({ + traceExporter: new ConsoleSpanExporter(), + autoDetectResources: false, + textMapPropagator: null, + }); + + sdk.start(); + + assert.deepStrictEqual(propagation.fields(), []); + + await sdk.shutdown(); + delete process.env.OTEL_PROPAGATORS; + }); }); async function waitForNumberOfMetrics( @@ -589,16 +622,9 @@ describe('Node SDK', () => { sdk.start(); - assert.strictEqual( - context['_getContextManager'](), - ctxManager, - 'context manager should not change' - ); - assert.strictEqual( - propagation['_getGlobalPropagator'](), - propagator, - 'propagator should not change' - ); + assertDefaultContextManagerRegistered(); + assertDefaultPropagatorRegistered(); + assert.strictEqual( (trace.getTracerProvider() as ProxyTracerProvider).getDelegate(), delegate, @@ -1433,361 +1459,376 @@ describe('Node SDK', () => { await sdk.shutdown(); }); }); -}); -describe('setup exporter from env', () => { - let stubLoggerError: Sinon.SinonStub; + describe('setup exporter from env', () => { + let stubLoggerError: Sinon.SinonStub; - const getSdkSpanProcessors = (sdk: NodeSDK) => { - const tracerProvider = sdk['_tracerProvider']; + const getSdkSpanProcessors = (sdk: NodeSDK) => { + const tracerProvider = sdk['_tracerProvider']; - assert.ok(tracerProvider instanceof NodeTracerProvider); + assert.ok(tracerProvider instanceof NodeTracerProvider); - const activeSpanProcessor = tracerProvider['_activeSpanProcessor']; + const activeSpanProcessor = tracerProvider['_activeSpanProcessor']; - assert.ok(activeSpanProcessor.constructor.name === 'MultiSpanProcessor'); + assert.ok(activeSpanProcessor.constructor.name === 'MultiSpanProcessor'); - return (activeSpanProcessor as any)['_spanProcessors'] as SpanProcessor[]; - }; + return (activeSpanProcessor as any)['_spanProcessors'] as SpanProcessor[]; + }; - beforeEach(() => { - stubLoggerError = Sinon.stub(diag, 'warn'); - }); - afterEach(() => { - stubLoggerError.restore(); - }); - - it('should use default exporter when nor env neither SDK config is given', async () => { - const sdk = new NodeSDK(); - sdk.start(); - const listOfProcessors = getSdkSpanProcessors(sdk); + beforeEach(() => { + stubLoggerError = Sinon.stub(diag, 'warn'); + }); + afterEach(() => { + stubLoggerError.restore(); + }); - assert.ok(listOfProcessors.length === 1); - assert.ok(listOfProcessors[0] instanceof BatchSpanProcessor); - assert.ok( - listOfProcessors[0]['_exporter'] instanceof OTLPProtoTraceExporter - ); - await sdk.shutdown(); - }); + it('should use default exporter when nor env neither SDK config is given', async () => { + const sdk = new NodeSDK(); + sdk.start(); + const listOfProcessors = getSdkSpanProcessors(sdk); - it('should ignore default env exporter when user provides exporter in sdk config', async () => { - const traceExporter = new ConsoleSpanExporter(); - const sdk = new NodeSDK({ - traceExporter, + assert.ok(listOfProcessors.length === 1); + assert.ok(listOfProcessors[0] instanceof BatchSpanProcessor); + assert.ok( + listOfProcessors[0]['_exporter'] instanceof OTLPProtoTraceExporter + ); + await sdk.shutdown(); }); - sdk.start(); - const listOfProcessors = getSdkSpanProcessors(sdk); - assert.ok(listOfProcessors.length === 1); - assert.ok(listOfProcessors[0] instanceof BatchSpanProcessor); - assert.ok(listOfProcessors[0]['_exporter'] instanceof ConsoleSpanExporter); - await sdk.shutdown(); - }); + it('should ignore default env exporter when user provides exporter in sdk config', async () => { + const traceExporter = new ConsoleSpanExporter(); + const sdk = new NodeSDK({ + traceExporter, + }); + sdk.start(); + const listOfProcessors = getSdkSpanProcessors(sdk); - it('should ignore default env exporter when user provides span processor in sdk config', async () => { - const traceExporter = new ConsoleSpanExporter(); - const spanProcessor = new SimpleSpanProcessor(traceExporter); - const sdk = new NodeSDK({ - spanProcessor, + assert.ok(listOfProcessors.length === 1); + assert.ok(listOfProcessors[0] instanceof BatchSpanProcessor); + assert.ok( + listOfProcessors[0]['_exporter'] instanceof ConsoleSpanExporter + ); + await sdk.shutdown(); }); - sdk.start(); - const listOfProcessors = getSdkSpanProcessors(sdk); - assert.ok(listOfProcessors.length === 1); - assert.ok(listOfProcessors[0] instanceof SimpleSpanProcessor); - assert.ok(listOfProcessors[0]['_exporter'] instanceof ConsoleSpanExporter); - await sdk.shutdown(); - }); + it('should ignore default env exporter when user provides span processor in sdk config', async () => { + const traceExporter = new ConsoleSpanExporter(); + const spanProcessor = new SimpleSpanProcessor(traceExporter); + const sdk = new NodeSDK({ + spanProcessor, + }); + sdk.start(); + const listOfProcessors = getSdkSpanProcessors(sdk); - it('should ignore exporter form env if another is provided in sdk config', async () => { - env.OTEL_TRACES_EXPORTER = 'console'; - const traceExporter = new OTLPTraceExporter(); - const sdk = new NodeSDK({ - traceExporter, + assert.ok(listOfProcessors.length === 1); + assert.ok(listOfProcessors[0] instanceof SimpleSpanProcessor); + assert.ok( + listOfProcessors[0]['_exporter'] instanceof ConsoleSpanExporter + ); + await sdk.shutdown(); }); - sdk.start(); - const listOfProcessors = getSdkSpanProcessors(sdk); - assert.ok(listOfProcessors.length === 1); - assert.ok(listOfProcessors[0] instanceof BatchSpanProcessor); - assert.ok(listOfProcessors[0]['_exporter'] instanceof OTLPTraceExporter); - delete env.OTEL_TRACES_EXPORTER; - await sdk.shutdown(); - }); + it('should ignore exporter form env if another is provided in sdk config', async () => { + env.OTEL_TRACES_EXPORTER = 'console'; + const traceExporter = new OTLPTraceExporter(); + const sdk = new NodeSDK({ + traceExporter, + }); + sdk.start(); + const listOfProcessors = getSdkSpanProcessors(sdk); - it('should only create one span processor when configured using env vars and config', async () => { - env.OTEL_TRACES_EXPORTER = 'console'; - const sdk = new NodeSDK({ - sampler: new AlwaysOffSampler(), + assert.ok(listOfProcessors.length === 1); + assert.ok(listOfProcessors[0] instanceof BatchSpanProcessor); + assert.ok(listOfProcessors[0]['_exporter'] instanceof OTLPTraceExporter); + delete env.OTEL_TRACES_EXPORTER; + await sdk.shutdown(); }); - sdk.start(); - const listOfProcessors = getSdkSpanProcessors(sdk); - assert.ok( - sdk['_tracerProvider']!['_config']?.sampler instanceof AlwaysOffSampler - ); - assert.strictEqual(listOfProcessors.length, 1); - assert.ok(listOfProcessors[0] instanceof SimpleSpanProcessor); - assert.ok(listOfProcessors[0]['_exporter'] instanceof ConsoleSpanExporter); - delete env.OTEL_TRACES_EXPORTER; - await sdk.shutdown(); - }); + it('should only create one span processor when configured using env vars and config', async () => { + env.OTEL_TRACES_EXPORTER = 'console'; + const sdk = new NodeSDK({ + sampler: new AlwaysOffSampler(), + }); + sdk.start(); + const listOfProcessors = getSdkSpanProcessors(sdk); - it('should use otlp exporter and defined exporter protocol env value', async () => { - env.OTEL_TRACES_EXPORTER = 'otlp'; - env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL = 'grpc'; - const sdk = new NodeSDK(); - sdk.start(); - const listOfProcessors = getSdkSpanProcessors(sdk); + assert.ok( + sdk['_tracerProvider']!['_config']?.sampler instanceof AlwaysOffSampler + ); + assert.strictEqual(listOfProcessors.length, 1); + assert.ok(listOfProcessors[0] instanceof SimpleSpanProcessor); + assert.ok( + listOfProcessors[0]['_exporter'] instanceof ConsoleSpanExporter + ); + delete env.OTEL_TRACES_EXPORTER; + await sdk.shutdown(); + }); - assert.ok(listOfProcessors.length === 1); - assert.ok(listOfProcessors[0] instanceof BatchSpanProcessor); - assert.ok( - listOfProcessors[0]['_exporter'] instanceof OTLPGrpcTraceExporter - ); - delete env.OTEL_TRACES_EXPORTER; - delete env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL; - await sdk.shutdown(); - }); + it('should use otlp exporter and defined exporter protocol env value', async () => { + env.OTEL_TRACES_EXPORTER = 'otlp'; + env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL = 'grpc'; + const sdk = new NodeSDK(); + sdk.start(); + const listOfProcessors = getSdkSpanProcessors(sdk); - it('sohuld use exporter and processor from env, signal specific env for protocol takes precedence', async () => { - env.OTEL_TRACES_EXPORTER = 'otlp'; - env.OTEL_EXPORTER_OTLP_PROTOCOL = 'http/protobuf'; - env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL = 'grpc'; - const sdk = new NodeSDK(); - sdk.start(); - const listOfProcessors = getSdkSpanProcessors(sdk); + assert.ok(listOfProcessors.length === 1); + assert.ok(listOfProcessors[0] instanceof BatchSpanProcessor); + assert.ok( + listOfProcessors[0]['_exporter'] instanceof OTLPGrpcTraceExporter + ); + delete env.OTEL_TRACES_EXPORTER; + delete env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL; + await sdk.shutdown(); + }); - assert.ok(listOfProcessors.length === 1); - assert.ok(listOfProcessors[0] instanceof BatchSpanProcessor); - assert.ok( - listOfProcessors[0]['_exporter'] instanceof OTLPGrpcTraceExporter - ); - delete env.OTEL_TRACES_EXPORTER; - delete env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL; - await sdk.shutdown(); - }); + it('sohuld use exporter and processor from env, signal specific env for protocol takes precedence', async () => { + env.OTEL_TRACES_EXPORTER = 'otlp'; + env.OTEL_EXPORTER_OTLP_PROTOCOL = 'http/protobuf'; + env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL = 'grpc'; + const sdk = new NodeSDK(); + sdk.start(); + const listOfProcessors = getSdkSpanProcessors(sdk); - it('should use empty span processor when user sets env exporter to none', async () => { - env.OTEL_TRACES_EXPORTER = 'none'; - const sdk = new NodeSDK(); - sdk.start(); + assert.ok(listOfProcessors.length === 1); + assert.ok(listOfProcessors[0] instanceof BatchSpanProcessor); + assert.ok( + listOfProcessors[0]['_exporter'] instanceof OTLPGrpcTraceExporter + ); + delete env.OTEL_TRACES_EXPORTER; + delete env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL; + await sdk.shutdown(); + }); - // should warn - assert.strictEqual( - stubLoggerError.args[0][0], - 'OTEL_TRACES_EXPORTER contains "none". SDK will not be initialized.' - ); + it('should use empty span processor when user sets env exporter to none', async () => { + env.OTEL_TRACES_EXPORTER = 'none'; + const sdk = new NodeSDK(); + sdk.start(); - const listOfProcessors = getSdkSpanProcessors(sdk); - assert.strictEqual(listOfProcessors.length, 0); + // should warn + assert.strictEqual( + stubLoggerError.args[0][0], + 'OTEL_TRACES_EXPORTER contains "none". SDK will not be initialized.' + ); - delete env.OTEL_TRACES_EXPORTER; - await sdk.shutdown(); - }); + assert.strictEqual( + (trace.getTracerProvider() as ProxyTracerProvider).getDelegate(), + delegate, + 'tracer provider should not have changed' + ); - it('should use default otlp exporter when empty value is provided for exporter via env', async () => { - env.OTEL_TRACES_EXPORTER = ''; - const sdk = new NodeSDK(); - sdk.start(); + delete env.OTEL_TRACES_EXPORTER; + await sdk.shutdown(); + }); - const listOfProcessors = getSdkSpanProcessors(sdk); + it('should use default otlp exporter when empty value is provided for exporter via env', async () => { + env.OTEL_TRACES_EXPORTER = ''; + const sdk = new NodeSDK(); + sdk.start(); - assert.ok(listOfProcessors.length === 1); - assert.ok(listOfProcessors[0] instanceof BatchSpanProcessor); - assert.ok( - listOfProcessors[0]['_exporter'] instanceof OTLPProtoTraceExporter - ); - env.OTEL_TRACES_EXPORTER = ''; - await sdk.shutdown(); - }); + const listOfProcessors = getSdkSpanProcessors(sdk); - it('should use only default exporter when none value is provided with other exporters', async () => { - env.OTEL_TRACES_EXPORTER = 'otlp,zipkin,none'; - const sdk = new NodeSDK(); - sdk.start(); + assert.ok(listOfProcessors.length === 1); + assert.ok(listOfProcessors[0] instanceof BatchSpanProcessor); + assert.ok( + listOfProcessors[0]['_exporter'] instanceof OTLPProtoTraceExporter + ); + env.OTEL_TRACES_EXPORTER = ''; + await sdk.shutdown(); + }); - // also it should warn - assert.strictEqual( - stubLoggerError.args[0][0], - 'OTEL_TRACES_EXPORTER contains "none" along with other exporters. Using default otlp exporter.' - ); + it('should use only default exporter when none value is provided with other exporters', async () => { + env.OTEL_TRACES_EXPORTER = 'otlp,zipkin,none'; + const sdk = new NodeSDK(); + sdk.start(); - const listOfProcessors = getSdkSpanProcessors(sdk); + // also it should warn + assert.strictEqual( + stubLoggerError.args[0][0], + 'OTEL_TRACES_EXPORTER contains "none" along with other exporters. Using default otlp exporter.' + ); - assert.ok(listOfProcessors.length === 1); - assert.ok(listOfProcessors[0] instanceof BatchSpanProcessor); - assert.ok( - listOfProcessors[0]['_exporter'] instanceof OTLPProtoTraceExporter - ); + const listOfProcessors = getSdkSpanProcessors(sdk); - delete env.OTEL_TRACES_EXPORTER; - await sdk.shutdown(); - }); + assert.ok(listOfProcessors.length === 1); + assert.ok(listOfProcessors[0] instanceof BatchSpanProcessor); + assert.ok( + listOfProcessors[0]['_exporter'] instanceof OTLPProtoTraceExporter + ); - it('should warn that provided exporter value is unrecognized and not able to be set up', async () => { - env.OTEL_TRACES_EXPORTER = 'invalid'; - const sdk = new NodeSDK(); - sdk.start(); + delete env.OTEL_TRACES_EXPORTER; + await sdk.shutdown(); + }); - assert.strictEqual( - stubLoggerError.args[0][0], - 'Unrecognized OTEL_TRACES_EXPORTER value: invalid.' - ); + it('should warn that provided exporter value is unrecognized and not able to be set up', async () => { + env.OTEL_TRACES_EXPORTER = 'invalid'; + const sdk = new NodeSDK(); + sdk.start(); - assert.strictEqual( - stubLoggerError.args[1][0], - 'Unable to set up trace exporter(s) due to invalid exporter and/or protocol values.' - ); + assert.strictEqual( + stubLoggerError.args[0][0], + 'Unrecognized OTEL_TRACES_EXPORTER value: invalid.' + ); - delete env.OTEL_TRACES_EXPORTER; - await sdk.shutdown(); - }); + assert.strictEqual( + stubLoggerError.args[1][0], + 'Unable to set up trace exporter(s) due to invalid exporter and/or protocol values.' + ); - it('should be able to setup zipkin exporter', async () => { - env.OTEL_TRACES_EXPORTER = 'zipkin'; - env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL = 'grpc'; - const sdk = new NodeSDK(); - sdk.start(); + delete env.OTEL_TRACES_EXPORTER; + await sdk.shutdown(); + }); - const listOfProcessors = getSdkSpanProcessors(sdk); + it('should be able to setup zipkin exporter', async () => { + env.OTEL_TRACES_EXPORTER = 'zipkin'; + env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL = 'grpc'; + const sdk = new NodeSDK(); + sdk.start(); - assert.ok(listOfProcessors.length === 1); - assert.ok(listOfProcessors[0] instanceof BatchSpanProcessor); - assert.ok(listOfProcessors[0]['_exporter'] instanceof ZipkinExporter); + const listOfProcessors = getSdkSpanProcessors(sdk); - delete env.OTEL_TRACES_EXPORTER; - delete env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL; - await sdk.shutdown(); - }); + assert.ok(listOfProcessors.length === 1); + assert.ok(listOfProcessors[0] instanceof BatchSpanProcessor); + assert.ok(listOfProcessors[0]['_exporter'] instanceof ZipkinExporter); - it('should be able to setup zipkin and otlp exporters', async () => { - env.OTEL_TRACES_EXPORTER = 'zipkin, otlp'; - env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL = 'grpc'; - const sdk = new NodeSDK(); - sdk.start(); + delete env.OTEL_TRACES_EXPORTER; + delete env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL; + await sdk.shutdown(); + }); - const listOfProcessors = getSdkSpanProcessors(sdk); + it('should be able to setup zipkin and otlp exporters', async () => { + env.OTEL_TRACES_EXPORTER = 'zipkin, otlp'; + env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL = 'grpc'; + const sdk = new NodeSDK(); + sdk.start(); - assert.ok(listOfProcessors.length === 2); - assert.ok(listOfProcessors[0] instanceof BatchSpanProcessor); - assert.ok(listOfProcessors[0]['_exporter'] instanceof ZipkinExporter); - assert.ok(listOfProcessors[1] instanceof BatchSpanProcessor); - assert.ok( - listOfProcessors[1]['_exporter'] instanceof OTLPGrpcTraceExporter - ); + const listOfProcessors = getSdkSpanProcessors(sdk); - delete env.OTEL_TRACES_EXPORTER; - delete env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL; - await sdk.shutdown(); - }); + assert.ok(listOfProcessors.length === 2); + assert.ok(listOfProcessors[0] instanceof BatchSpanProcessor); + assert.ok(listOfProcessors[0]['_exporter'] instanceof ZipkinExporter); + assert.ok(listOfProcessors[1] instanceof BatchSpanProcessor); + assert.ok( + listOfProcessors[1]['_exporter'] instanceof OTLPGrpcTraceExporter + ); - it('should be able to setup jaeger exporter', async () => { - env.OTEL_TRACES_EXPORTER = 'jaeger'; - env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL = 'grpc'; - const sdk = new NodeSDK(); - sdk.start(); + delete env.OTEL_TRACES_EXPORTER; + delete env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL; + await sdk.shutdown(); + }); - const listOfProcessors = getSdkSpanProcessors(sdk); + it('should be able to setup jaeger exporter', async () => { + env.OTEL_TRACES_EXPORTER = 'jaeger'; + env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL = 'grpc'; + const sdk = new NodeSDK(); + sdk.start(); - assert.ok(listOfProcessors.length === 1); - assert.ok(listOfProcessors[0] instanceof BatchSpanProcessor); - assert.ok(listOfProcessors[0]['_exporter'] instanceof JaegerExporter); + const listOfProcessors = getSdkSpanProcessors(sdk); - delete env.OTEL_TRACES_EXPORTER; - delete env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL; - await sdk.shutdown(); - }); + assert.ok(listOfProcessors.length === 1); + assert.ok(listOfProcessors[0] instanceof BatchSpanProcessor); + assert.ok(listOfProcessors[0]['_exporter'] instanceof JaegerExporter); - it('should be able to setup jaeger and otlp exporters', async () => { - env.OTEL_TRACES_EXPORTER = 'otlp, jaeger'; - env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL = 'grpc'; - const sdk = new NodeSDK(); - sdk.start(); + delete env.OTEL_TRACES_EXPORTER; + delete env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL; + await sdk.shutdown(); + }); - const listOfProcessors = getSdkSpanProcessors(sdk); + it('should be able to setup jaeger and otlp exporters', async () => { + env.OTEL_TRACES_EXPORTER = 'otlp, jaeger'; + env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL = 'grpc'; + const sdk = new NodeSDK(); + sdk.start(); - assert.ok(listOfProcessors.length === 2); - assert.ok(listOfProcessors[0] instanceof BatchSpanProcessor); - assert.ok( - listOfProcessors[0]['_exporter'] instanceof OTLPGrpcTraceExporter - ); - assert.ok(listOfProcessors[1] instanceof BatchSpanProcessor); - assert.ok(listOfProcessors[1]['_exporter'] instanceof JaegerExporter); + const listOfProcessors = getSdkSpanProcessors(sdk); - delete env.OTEL_TRACES_EXPORTER; - delete env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL; - await sdk.shutdown(); - }); + assert.ok(listOfProcessors.length === 2); + assert.ok(listOfProcessors[0] instanceof BatchSpanProcessor); + assert.ok( + listOfProcessors[0]['_exporter'] instanceof OTLPGrpcTraceExporter + ); + assert.ok(listOfProcessors[1] instanceof BatchSpanProcessor); + assert.ok(listOfProcessors[1]['_exporter'] instanceof JaegerExporter); - it('should be able to setup zipkin, jaeger and otlp exporters', async () => { - env.OTEL_TRACES_EXPORTER = 'zipkin, otlp, jaeger'; - env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL = 'grpc'; - const sdk = new NodeSDK(); - sdk.start(); + delete env.OTEL_TRACES_EXPORTER; + delete env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL; + await sdk.shutdown(); + }); - const listOfProcessors = getSdkSpanProcessors(sdk); + it('should be able to setup zipkin, jaeger and otlp exporters', async () => { + env.OTEL_TRACES_EXPORTER = 'zipkin, otlp, jaeger'; + env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL = 'grpc'; + const sdk = new NodeSDK(); + sdk.start(); - assert.ok(listOfProcessors.length === 3); - assert.ok(listOfProcessors[0] instanceof BatchSpanProcessor); - assert.ok(listOfProcessors[0]['_exporter'] instanceof ZipkinExporter); - assert.ok(listOfProcessors[1] instanceof BatchSpanProcessor); - assert.ok( - listOfProcessors[1]['_exporter'] instanceof OTLPGrpcTraceExporter - ); - assert.ok(listOfProcessors[2] instanceof BatchSpanProcessor); - assert.ok(listOfProcessors[2]['_exporter'] instanceof JaegerExporter); + const listOfProcessors = getSdkSpanProcessors(sdk); - delete env.OTEL_TRACES_EXPORTER; - delete env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL; - await sdk.shutdown(); - }); + assert.ok(listOfProcessors.length === 3); + assert.ok(listOfProcessors[0] instanceof BatchSpanProcessor); + assert.ok(listOfProcessors[0]['_exporter'] instanceof ZipkinExporter); + assert.ok(listOfProcessors[1] instanceof BatchSpanProcessor); + assert.ok( + listOfProcessors[1]['_exporter'] instanceof OTLPGrpcTraceExporter + ); + assert.ok(listOfProcessors[2] instanceof BatchSpanProcessor); + assert.ok(listOfProcessors[2]['_exporter'] instanceof JaegerExporter); - it('should be able to use console and otlp exporters', async () => { - env.OTEL_TRACES_EXPORTER = 'console, otlp'; - const sdk = new NodeSDK(); - sdk.start(); + delete env.OTEL_TRACES_EXPORTER; + delete env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL; + await sdk.shutdown(); + }); - const listOfProcessors = getSdkSpanProcessors(sdk); + it('should be able to use console and otlp exporters', async () => { + env.OTEL_TRACES_EXPORTER = 'console, otlp'; + const sdk = new NodeSDK(); + sdk.start(); - assert.ok(listOfProcessors.length === 2); - assert.ok(listOfProcessors[0] instanceof SimpleSpanProcessor); - assert.ok(listOfProcessors[0]['_exporter'] instanceof ConsoleSpanExporter); - assert.ok(listOfProcessors[1] instanceof BatchSpanProcessor); - assert.ok( - listOfProcessors[1]['_exporter'] instanceof OTLPProtoTraceExporter - ); - delete env.OTEL_TRACES_EXPORTER; - await sdk.shutdown(); - }); + const listOfProcessors = getSdkSpanProcessors(sdk); - it('should be able to use console exporter but not http/json exporter', async () => { - env.OTEL_TRACES_EXPORTER = 'console, http/json'; - const sdk = new NodeSDK(); - sdk.start(); + assert.ok(listOfProcessors.length === 2); + assert.ok(listOfProcessors[0] instanceof SimpleSpanProcessor); + assert.ok( + listOfProcessors[0]['_exporter'] instanceof ConsoleSpanExporter + ); + assert.ok(listOfProcessors[1] instanceof BatchSpanProcessor); + assert.ok( + listOfProcessors[1]['_exporter'] instanceof OTLPProtoTraceExporter + ); + delete env.OTEL_TRACES_EXPORTER; + await sdk.shutdown(); + }); - const listOfProcessors = getSdkSpanProcessors(sdk); + it('should be able to use console exporter but not http/json exporter', async () => { + env.OTEL_TRACES_EXPORTER = 'console, http/json'; + const sdk = new NodeSDK(); + sdk.start(); - assert.ok(listOfProcessors.length === 1); - assert.ok(listOfProcessors[0] instanceof SimpleSpanProcessor); - assert.ok(listOfProcessors[0]['_exporter'] instanceof ConsoleSpanExporter); - delete env.OTEL_TRACES_EXPORTER; - await sdk.shutdown(); - }); + const listOfProcessors = getSdkSpanProcessors(sdk); - it('should ignore the protocol from env when use the console exporter', async () => { - env.OTEL_TRACES_EXPORTER = 'console'; - env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL = 'grpc'; - const sdk = new NodeSDK(); - sdk.start(); + assert.ok(listOfProcessors.length === 1); + assert.ok(listOfProcessors[0] instanceof SimpleSpanProcessor); + assert.ok( + listOfProcessors[0]['_exporter'] instanceof ConsoleSpanExporter + ); + delete env.OTEL_TRACES_EXPORTER; + await sdk.shutdown(); + }); - const listOfProcessors = getSdkSpanProcessors(sdk); + it('should ignore the protocol from env when use the console exporter', async () => { + env.OTEL_TRACES_EXPORTER = 'console'; + env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL = 'grpc'; + const sdk = new NodeSDK(); + sdk.start(); - assert.ok(listOfProcessors.length === 1); - assert.ok(listOfProcessors[0] instanceof SimpleSpanProcessor); - assert.ok(listOfProcessors[0]['_exporter'] instanceof ConsoleSpanExporter); - delete env.OTEL_TRACES_EXPORTER; - delete env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL; - await sdk.shutdown(); + const listOfProcessors = getSdkSpanProcessors(sdk); + + assert.ok(listOfProcessors.length === 1); + assert.ok(listOfProcessors[0] instanceof SimpleSpanProcessor); + assert.ok( + listOfProcessors[0]['_exporter'] instanceof ConsoleSpanExporter + ); + delete env.OTEL_TRACES_EXPORTER; + delete env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL; + await sdk.shutdown(); + }); }); });