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();
+ });
});
});