This commit is contained in:
Marc Pichler 2025-09-18 15:35:56 -04:00 committed by GitHub
commit 587f46f45f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 512 additions and 402 deletions

View File

@ -10,6 +10,10 @@ 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.
### :bug: Bug Fixes
* fix(instrumentation-http): respect requireParent flag when INVALID_SPAN_CONTEXT is used [#4788](https://github.com/open-telemetry/opentelemetry-js/pull/4788) @reberhardt7

View File

@ -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).
* <p> After successful setup using {@link NodeSDK#start()}, use `@opentelemetry/api` to obtain the registered components.
* <p> Use the shutdown handler {@link NodeSDK#shutdown()} to ensure your telemetry is exported before the process exits.
*
* @example <caption> Register SDK by using environment variables </caption>
* const nodeSdk = new NodeSDK(); // providing no options uses OTEL_* environment variables for SDK setup.
* nodeSdk.start(); // registers all configured SDK components
* @example <caption> Override environment variable config with your own components </caption>
* 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,
};
}
@ -371,23 +383,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) {
@ -429,6 +432,13 @@ export class NodeSDK {
instrumentation.setMeterProvider(metrics.getMeterProvider());
}
}
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())
);
}
public shutdown(): Promise<void> {

View File

@ -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[];

View File

@ -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<string, () => 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);
}

View File

@ -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);
assert.ok(listOfProcessors.length === 1);
assert.ok(listOfProcessors[0] instanceof BatchSpanProcessor);
assert.ok(
listOfProcessors[0]['_exporter'] instanceof OTLPProtoTraceExporter
);
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,
beforeEach(() => {
stubLoggerError = Sinon.stub(diag, 'warn');
});
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 span processor in sdk config', async () => {
const traceExporter = new ConsoleSpanExporter();
const spanProcessor = new SimpleSpanProcessor(traceExporter);
const sdk = new NodeSDK({
spanProcessor,
afterEach(() => {
stubLoggerError.restore();
});
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 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 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 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 OTLPTraceExporter);
delete env.OTEL_TRACES_EXPORTER;
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 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 ConsoleSpanExporter
);
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 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 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(listOfProcessors.length === 1);
assert.ok(listOfProcessors[0] instanceof SimpleSpanProcessor);
assert.ok(
listOfProcessors[0]['_exporter'] instanceof ConsoleSpanExporter
);
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 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('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 OTLPTraceExporter);
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 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 empty span processor when user sets env exporter to none', async () => {
env.OTEL_TRACES_EXPORTER = 'none';
const sdk = new NodeSDK();
sdk.start();
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();
});
// should warn
assert.strictEqual(
stubLoggerError.args[0][0],
'OTEL_TRACES_EXPORTER contains "none". SDK will not be initialized.'
);
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);
const listOfProcessors = getSdkSpanProcessors(sdk);
assert.strictEqual(listOfProcessors.length, 0);
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();
});
delete env.OTEL_TRACES_EXPORTER;
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 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 OTLPGrpcTraceExporter
);
delete env.OTEL_TRACES_EXPORTER;
delete env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL;
await sdk.shutdown();
});
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 OTLPProtoTraceExporter
);
env.OTEL_TRACES_EXPORTER = '';
await sdk.shutdown();
});
// should warn
assert.strictEqual(
stubLoggerError.args[0][0],
'OTEL_TRACES_EXPORTER contains "none". SDK will not be initialized.'
);
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.strictEqual(
(trace.getTracerProvider() as ProxyTracerProvider).getDelegate(),
delegate,
'tracer provider should not have changed'
);
// also it should warn
assert.strictEqual(
stubLoggerError.args[0][0],
'OTEL_TRACES_EXPORTER contains "none" along with other exporters. Using default otlp exporter.'
);
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
);
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
);
env.OTEL_TRACES_EXPORTER = '';
await sdk.shutdown();
});
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();
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.strictEqual(
stubLoggerError.args[0][0],
'Unrecognized OTEL_TRACES_EXPORTER value: invalid.'
);
// also it should warn
assert.strictEqual(
stubLoggerError.args[0][0],
'OTEL_TRACES_EXPORTER contains "none" along with other exporters. Using default otlp exporter.'
);
assert.strictEqual(
stubLoggerError.args[1][0],
'Unable to set up trace exporter(s) due to invalid exporter and/or protocol values.'
);
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 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 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.ok(listOfProcessors.length === 1);
assert.ok(listOfProcessors[0] instanceof BatchSpanProcessor);
assert.ok(listOfProcessors[0]['_exporter'] instanceof ZipkinExporter);
assert.strictEqual(
stubLoggerError.args[0][0],
'Unrecognized OTEL_TRACES_EXPORTER value: invalid.'
);
delete env.OTEL_TRACES_EXPORTER;
delete env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL;
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 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;
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 === 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 === 1);
assert.ok(listOfProcessors[0] instanceof BatchSpanProcessor);
assert.ok(listOfProcessors[0]['_exporter'] instanceof ZipkinExporter);
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 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 === 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 === 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 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 exporter', async () => {
env.OTEL_TRACES_EXPORTER = '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 === 1);
assert.ok(listOfProcessors[0] instanceof BatchSpanProcessor);
assert.ok(listOfProcessors[0]['_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 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 === 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 === 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 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 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 === 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 === 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 === 1);
assert.ok(listOfProcessors[0] instanceof SimpleSpanProcessor);
assert.ok(listOfProcessors[0]['_exporter'] instanceof ConsoleSpanExporter);
delete env.OTEL_TRACES_EXPORTER;
await sdk.shutdown();
});
it('should be able to use console and otlp exporters', async () => {
env.OTEL_TRACES_EXPORTER = 'console, otlp';
const sdk = new NodeSDK();
sdk.start();
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();
const listOfProcessors = getSdkSpanProcessors(sdk);
const listOfProcessors = getSdkSpanProcessors(sdk);
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();
});
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();
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();
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;
await sdk.shutdown();
});
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();
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();
});
});
});