diff --git a/experimental/CHANGELOG.md b/experimental/CHANGELOG.md
index 1c77eafbada..04482240f8c 100644
--- a/experimental/CHANGELOG.md
+++ b/experimental/CHANGELOG.md
@@ -8,6 +8,8 @@ All notable changes to experimental packages in this project will be documented
### :rocket: (Enhancement)
+* feat(sdk-node): configure trace exporter with environment variables [#3143](https://github.com/open-telemetry/opentelemetry-js/pull/3143) @svetlanabrennan
+
### :bug: (Bug Fix)
### :books: (Refine Doc)
diff --git a/experimental/packages/opentelemetry-sdk-node/README.md b/experimental/packages/opentelemetry-sdk-node/README.md
index 97027ff7fb0..0d4f3c0f7a8 100644
--- a/experimental/packages/opentelemetry-sdk-node/README.md
+++ b/experimental/packages/opentelemetry-sdk-node/README.md
@@ -129,7 +129,7 @@ Configure a custom sampler. By default, all traces will be sampled.
### traceExporter
-Configure a trace exporter. If an exporter OR span processor is not configured, the tracing SDK will not be initialized and registered. If an exporter is configured, it will be used with a [BatchSpanProcessor](../../../packages/opentelemetry-sdk-trace-base/src/platform/node/export/BatchSpanProcessor.ts).
+Configure a trace exporter. If an exporter is configured, it will be used with a [BatchSpanProcessor](../../../packages/opentelemetry-sdk-trace-base/src/platform/node/export/BatchSpanProcessor.ts). If an exporter OR span processor is not configured programatically, this package will auto setup the default `otlp` exporter with `http/protobuf` protocol with a `BatchSpanProcessor`.
### spanLimits
@@ -139,6 +139,30 @@ Configure tracing parameters. These are the same trace parameters used to [confi
Configure the [service name](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/resource/semantic_conventions/README.md#service).
+## Configure Trace Exporter from Environment
+
+This is an alternative to programmatically configuring an exporter or span processor. This package will auto setup the default `otlp` exporter with `http/protobuf` protocol if `traceExporter` or `spanProcessor` hasn't been passed into the `NodeSDK` constructor.
+
+### Exporters
+
+| Environment variable | Description |
+|----------------------|-------------|
+| OTEL_TRACES_EXPORTER | List of exporters to be used for tracing, separated by commas. Options include `otlp`, `jaeger`, `zipkin`, and `none`. Default is `otlp`. `none` means no autoconfigured exporter.
+
+### OTLP Exporter
+
+| Environment variable | Description |
+|----------------------|-------------|
+| OTEL_EXPORTER_OTLP_PROTOCOL | The transport protocol to use on OTLP trace, metric, and log requests. Options include `grpc`, `http/protobuf`, and `http/json`. Default is `http/protobuf`. |
+| OTEL_EXPORTER_OTLP_TRACES_PROTOCOL | The transport protocol to use on OTLP trace requests. Options include `grpc`, `http/protobuf`, and `http/json`. Default is `http/protobuf`. |
+| OTEL_EXPORTER_OTLP_METRICS_PROTOCOL | The transport protocol to use on OTLP metric requests. Options include `grpc`, `http/protobuf`, and `http/json`. Default is `http/protobuf`. |
+
+Additionally, you can specify other applicable environment variables that apply to each exporter such as the following:
+
+- [OTLP exporter environment configuration](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/exporter.md#configuration-options)
+- [Zipkin exporter environment configuration](https://github.com/open-telemetry/opentelemetry-specification/blob/6ce62202e5407518e19c56c445c13682ef51a51d/specification/sdk-environment-variables.md#zipkin-exporter)
+- [Jaeger exporter environment configuration](https://github.com/open-telemetry/opentelemetry-specification/blob/6ce62202e5407518e19c56c445c13682ef51a51d/specification/sdk-environment-variables.md#jaeger-exporter)
+
## Useful links
- For more information on OpenTelemetry, visit:
diff --git a/experimental/packages/opentelemetry-sdk-node/package.json b/experimental/packages/opentelemetry-sdk-node/package.json
index 8bc140b6858..d4f0f48a381 100644
--- a/experimental/packages/opentelemetry-sdk-node/package.json
+++ b/experimental/packages/opentelemetry-sdk-node/package.json
@@ -44,6 +44,11 @@
"access": "public"
},
"dependencies": {
+ "@opentelemetry/exporter-jaeger": "1.7.0",
+ "@opentelemetry/exporter-trace-otlp-grpc": "0.33.0",
+ "@opentelemetry/exporter-trace-otlp-http": "0.33.0",
+ "@opentelemetry/exporter-trace-otlp-proto": "0.33.0",
+ "@opentelemetry/exporter-zipkin": "1.7.0",
"@opentelemetry/api-metrics": "0.33.0",
"@opentelemetry/core": "1.7.0",
"@opentelemetry/instrumentation": "0.33.0",
diff --git a/experimental/packages/opentelemetry-sdk-node/src/TracerProviderWithEnvExporter.ts b/experimental/packages/opentelemetry-sdk-node/src/TracerProviderWithEnvExporter.ts
new file mode 100644
index 00000000000..f4abb593abe
--- /dev/null
+++ b/experimental/packages/opentelemetry-sdk-node/src/TracerProviderWithEnvExporter.ts
@@ -0,0 +1,124 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { diag } from '@opentelemetry/api';
+import { getEnv, getEnvWithoutDefaults } from '@opentelemetry/core';
+import { ConsoleSpanExporter, SpanExporter, BatchSpanProcessor, SimpleSpanProcessor, SDKRegistrationConfig, SpanProcessor } from '@opentelemetry/sdk-trace-base';
+import { NodeTracerConfig, NodeTracerProvider } from '@opentelemetry/sdk-trace-node';
+import { OTLPTraceExporter as OTLPProtoTraceExporter } from '@opentelemetry/exporter-trace-otlp-proto';
+import { OTLPTraceExporter as OTLPHttpTraceExporter} from '@opentelemetry/exporter-trace-otlp-http';
+import { OTLPTraceExporter as OTLPGrpcTraceExporter} from '@opentelemetry/exporter-trace-otlp-grpc';
+import { ZipkinExporter } from '@opentelemetry/exporter-zipkin';
+import { JaegerExporter } from '@opentelemetry/exporter-jaeger';
+
+export class TracerProviderWithEnvExporters extends NodeTracerProvider {
+ private _configuredExporters: SpanExporter[] = [];
+ private _spanProcessors: SpanProcessor[] | undefined;
+ private _hasSpanProcessors: boolean = false;
+
+ static configureOtlp(): SpanExporter {
+ const protocol = this.getOtlpProtocol();
+
+ switch (protocol) {
+ case 'grpc':
+ return new OTLPGrpcTraceExporter;
+ case 'http/json':
+ return new OTLPHttpTraceExporter;
+ case 'http/protobuf':
+ return new OTLPProtoTraceExporter;
+ default:
+ diag.warn(`Unsupported OTLP traces protocol: ${protocol}. Using http/protobuf.`);
+ return new OTLPProtoTraceExporter;
+ }
+ }
+
+ static getOtlpProtocol(): string {
+ const parsedEnvValues = getEnvWithoutDefaults();
+
+ return parsedEnvValues.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL ??
+ parsedEnvValues.OTEL_EXPORTER_OTLP_PROTOCOL ??
+ getEnv().OTEL_EXPORTER_OTLP_TRACES_PROTOCOL ??
+ getEnv().OTEL_EXPORTER_OTLP_PROTOCOL;
+ }
+
+ protected static override _registeredExporters = new Map<
+ string,
+ () => SpanExporter
+ >([
+ ['otlp', () => this.configureOtlp()],
+ ['zipkin', () => new ZipkinExporter],
+ ['jaeger', () => new JaegerExporter],
+ ['console', () => new ConsoleSpanExporter]
+ ]);
+
+ public constructor(config: NodeTracerConfig = {}) {
+ super(config);
+ let traceExportersList = this.filterBlanksAndNulls(Array.from(new Set(getEnv().OTEL_TRACES_EXPORTER.split(','))));
+
+ if (traceExportersList.length === 0 || traceExportersList[0] === 'none') {
+ diag.warn('OTEL_TRACES_EXPORTER contains "none" or is empty. SDK will not be initialized.');
+ } else {
+ if (traceExportersList.length > 1 && traceExportersList.includes('none')) {
+ diag.warn('OTEL_TRACES_EXPORTER contains "none" along with other exporters. Using default otlp exporter.');
+ traceExportersList = ['otlp'];
+ }
+
+ traceExportersList.forEach(exporterName => {
+ const exporter = this._getSpanExporter(exporterName);
+ if (exporter) {
+ this._configuredExporters.push(exporter);
+ } else {
+ diag.warn(`Unrecognized OTEL_TRACES_EXPORTER value: ${exporterName}.`);
+ }
+ });
+
+ if (this._configuredExporters.length > 0) {
+ this._spanProcessors = this.configureSpanProcessors(this._configuredExporters);
+ this._spanProcessors.forEach(processor => {
+ this.addSpanProcessor(processor);
+ });
+ } else {
+ diag.warn('Unable to set up trace exporter(s) due to invalid exporter and/or protocol values.');
+ }
+ }
+ }
+
+ override addSpanProcessor(spanProcessor: SpanProcessor) {
+ super.addSpanProcessor(spanProcessor);
+ this._hasSpanProcessors = true;
+ }
+
+ override register(config?: SDKRegistrationConfig) {
+ if (this._hasSpanProcessors) {
+ super.register(config);
+ }
+ }
+
+ private configureSpanProcessors(exporters: SpanExporter[]): (BatchSpanProcessor | SimpleSpanProcessor)[] {
+ return exporters.map(exporter => {
+ if (exporter instanceof ConsoleSpanExporter) {
+ return new SimpleSpanProcessor(exporter);
+ } else {
+ return new BatchSpanProcessor(exporter);
+ }
+ });
+ }
+
+ private filterBlanksAndNulls(list: string[]): string[] {
+ return list.map(item => item.trim())
+ .filter(s => s !== 'null' && s !== '');
+ }
+}
diff --git a/experimental/packages/opentelemetry-sdk-node/src/sdk.ts b/experimental/packages/opentelemetry-sdk-node/src/sdk.ts
index a48028760bb..bb20ed94402 100644
--- a/experimental/packages/opentelemetry-sdk-node/src/sdk.ts
+++ b/experimental/packages/opentelemetry-sdk-node/src/sdk.ts
@@ -31,11 +31,12 @@ import {
import { MeterProvider, MetricReader, View } from '@opentelemetry/sdk-metrics';
import {
BatchSpanProcessor,
- SpanProcessor
+ SpanProcessor,
} from '@opentelemetry/sdk-trace-base';
import { NodeTracerConfig, NodeTracerProvider } from '@opentelemetry/sdk-trace-node';
import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions';
import { NodeSDKConfiguration } from './types';
+import { TracerProviderWithEnvExporters } from './TracerProviderWithEnvExporter';
/** This class represents everything needed to register a fully configured OpenTelemetry Node.js SDK */
@@ -64,7 +65,7 @@ export class NodeSDK {
private _autoDetectResources: boolean;
- private _tracerProvider?: NodeTracerProvider;
+ private _tracerProvider?: NodeTracerProvider | TracerProviderWithEnvExporters;
private _meterProvider?: MeterProvider;
private _serviceName?: string;
@@ -193,21 +194,25 @@ export class NodeSDK {
{ [SemanticResourceAttributes.SERVICE_NAME]: this._serviceName }
));
- if (this._tracerProviderConfig) {
- const tracerProvider = new NodeTracerProvider({
- ...this._tracerProviderConfig.tracerConfig,
- resource: this._resource,
- });
+ const Provider =
+ this._tracerProviderConfig ? NodeTracerProvider : TracerProviderWithEnvExporters;
+
+ const tracerProvider = new Provider ({
+ ...this._tracerProviderConfig?.tracerConfig,
+ resource: this._resource,
+ });
- this._tracerProvider = tracerProvider;
+ this._tracerProvider = tracerProvider;
+ if (this._tracerProviderConfig) {
tracerProvider.addSpanProcessor(this._tracerProviderConfig.spanProcessor);
- tracerProvider.register({
- contextManager: this._tracerProviderConfig.contextManager,
- propagator: this._tracerProviderConfig.textMapPropagator,
- });
}
+ tracerProvider.register({
+ contextManager: this._tracerProviderConfig?.contextManager,
+ propagator: this._tracerProviderConfig?.textMapPropagator,
+ });
+
if (this._meterProviderConfig) {
const meterProvider = new MeterProvider({
resource: this._resource,
diff --git a/experimental/packages/opentelemetry-sdk-node/test/TracerProviderWithEnvExporter.test.ts b/experimental/packages/opentelemetry-sdk-node/test/TracerProviderWithEnvExporter.test.ts
new file mode 100644
index 00000000000..2ba4d0394a7
--- /dev/null
+++ b/experimental/packages/opentelemetry-sdk-node/test/TracerProviderWithEnvExporter.test.ts
@@ -0,0 +1,283 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import {
+ diag,
+} from '@opentelemetry/api';
+import {
+ ConsoleSpanExporter,
+ SimpleSpanProcessor,
+ BatchSpanProcessor,
+} from '@opentelemetry/sdk-trace-base';
+import * as assert from 'assert';
+import * as Sinon from 'sinon';
+import { env } from 'process';
+import { OTLPTraceExporter as OTLPProtoTraceExporter, OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-proto';
+import { OTLPTraceExporter as OTLPHttpTraceExporter} from '@opentelemetry/exporter-trace-otlp-http';
+import { OTLPTraceExporter as OTLPGrpcTraceExporter} from '@opentelemetry/exporter-trace-otlp-grpc';
+import { ZipkinExporter } from '@opentelemetry/exporter-zipkin';
+import { JaegerExporter } from '@opentelemetry/exporter-jaeger';
+import { TracerProviderWithEnvExporters } from '../src/TracerProviderWithEnvExporter';
+
+describe('set up trace exporter with env exporters', () => {
+ let spyGetOtlpProtocol: Sinon.SinonSpy;
+ let stubLoggerError: Sinon.SinonStub;
+
+ beforeEach(() => {
+ spyGetOtlpProtocol = Sinon.spy(TracerProviderWithEnvExporters, 'getOtlpProtocol');
+ stubLoggerError = Sinon.stub(diag, 'warn');
+ });
+ afterEach(() => {
+ spyGetOtlpProtocol.restore();
+ stubLoggerError.restore();
+ });
+ describe('setup otlp exporter from env', () => {
+ it('set up default exporter when user does not define otel trace exporter', async () => {
+ const sdk = new TracerProviderWithEnvExporters();
+ const listOfProcessors = sdk['_spanProcessors']!;
+ const listOfExporters = sdk['_configuredExporters'];
+
+ assert(spyGetOtlpProtocol.returned('http/protobuf'));
+ assert(listOfExporters.length === 1);
+ assert(listOfExporters[0] instanceof OTLPProtoTraceExporter);
+ assert(listOfProcessors.length === 1);
+ assert(listOfProcessors[0] instanceof BatchSpanProcessor);
+ });
+ it('use otlp exporter and grpc exporter protocol env value', async () => {
+ env.OTEL_TRACES_EXPORTER = 'otlp';
+ env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL = 'grpc';
+
+ const sdk = new TracerProviderWithEnvExporters();
+ const listOfProcessors = sdk['_spanProcessors']!;
+ const listOfExporters = sdk['_configuredExporters'];
+
+ assert(spyGetOtlpProtocol.returned('grpc'));
+ assert(listOfExporters.length === 1);
+ assert(listOfExporters[0] instanceof OTLPGrpcTraceExporter);
+ assert(listOfProcessors.length === 1);
+ assert(listOfProcessors[0] instanceof BatchSpanProcessor);
+
+ delete env.OTEL_TRACES_EXPORTER;
+ delete env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL;
+ });
+ it('sdk will ignore protocol defined with no-signal env and use signal specific protocol instead', async () => {
+ env.OTEL_TRACES_EXPORTER = 'otlp';
+ env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL = 'http/protobuf';
+ env.OTEL_EXPORTER_OTLP_PROTOCOL = 'grpc';
+
+ const sdk = new TracerProviderWithEnvExporters();
+ const listOfProcessors = sdk['_spanProcessors']!;
+ const listOfExporters = sdk['_configuredExporters'];
+
+ assert(spyGetOtlpProtocol.returned('http/protobuf'));
+ assert(listOfExporters.length === 1);
+ assert(listOfExporters[0] instanceof OTLPTraceExporter);
+ assert(listOfProcessors.length === 1);
+ assert(listOfProcessors[0] instanceof BatchSpanProcessor);
+
+ delete env.OTEL_TRACES_EXPORTER;
+ delete env.OTEL_EXPORTER_OTLP_PROTOCOL;
+ delete env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL;
+ });
+ it('do not use any exporters when empty value is provided for exporter', async () => {
+ env.OTEL_TRACES_EXPORTER = '';
+ const sdk = new TracerProviderWithEnvExporters();
+ const listOfProcessors = sdk['_spanProcessors'];
+ const listOfExporters = sdk['_configuredExporters'];
+
+ assert(spyGetOtlpProtocol.notCalled);
+ assert(listOfExporters.length === 0);
+ assert(listOfProcessors === undefined);
+ env.OTEL_TRACES_EXPORTER = '';
+ });
+ it('do not use any exporters when none value is only provided', async () => {
+ env.OTEL_TRACES_EXPORTER = 'none';
+ const sdk = new TracerProviderWithEnvExporters();
+ const listOfProcessors = sdk['_spanProcessors'];
+ const listOfExporters = sdk['_configuredExporters'];
+
+ assert(spyGetOtlpProtocol.notCalled);
+ assert(listOfExporters.length === 0);
+ assert(listOfProcessors === undefined);
+ delete env.OTEL_TRACES_EXPORTER;
+ });
+ it('log warning that sdk will not be initalized when exporter is set to none', async () => {
+ env.OTEL_TRACES_EXPORTER = 'none';
+ new TracerProviderWithEnvExporters();
+
+ assert.strictEqual(stubLoggerError.args[0][0], 'OTEL_TRACES_EXPORTER contains "none" or is empty. SDK will not be initialized.');
+ delete env.OTEL_TRACES_EXPORTER;
+ });
+ it('use default exporter when none value is provided with other exports', async () => {
+ env.OTEL_TRACES_EXPORTER = 'otlp,zipkin,none';
+ const sdk = new TracerProviderWithEnvExporters();
+ const listOfProcessors = sdk['_spanProcessors']!;
+ const listOfExporters = sdk['_configuredExporters'];
+
+ assert(listOfExporters[0] instanceof OTLPProtoTraceExporter);
+ assert(listOfExporters.length === 1);
+ assert(listOfExporters[0] instanceof OTLPHttpTraceExporter === false);
+ assert(listOfExporters[0] instanceof ZipkinExporter === false);
+ assert(listOfProcessors.length === 1);
+ assert(listOfProcessors[0] instanceof BatchSpanProcessor);
+ delete env.OTEL_TRACES_EXPORTER;
+ });
+ it('log warning that default exporter will be used since exporter list contains none with other exports ', async () => {
+ env.OTEL_TRACES_EXPORTER = 'otlp,zipkin,none';
+ new TracerProviderWithEnvExporters();
+
+ assert.strictEqual(
+ stubLoggerError.args[0][0], 'OTEL_TRACES_EXPORTER contains "none" along with other exporters. Using default otlp exporter.'
+ );
+ delete env.OTEL_TRACES_EXPORTER;
+ });
+ it('should warn that exporter is unrecognized and not able to be set up', async () => {
+ env.OTEL_TRACES_EXPORTER = 'invalid';
+ new TracerProviderWithEnvExporters();
+
+ assert.strictEqual(
+ stubLoggerError.args[0][0], 'Unrecognized OTEL_TRACES_EXPORTER value: invalid.'
+ );
+
+ assert.strictEqual(
+ stubLoggerError.args[1][0], 'Unable to set up trace exporter(s) due to invalid exporter and/or protocol values.'
+ );
+
+ delete env.OTEL_TRACES_EXPORTER;
+ });
+ it('should log warning when provided protocol name is not valid', async () => {
+ env.OTEL_EXPORTER_OTLP_PROTOCOL = 'invalid';
+ new TracerProviderWithEnvExporters();
+
+ assert.strictEqual(
+ stubLoggerError.args[0][0], 'Unsupported OTLP traces protocol: invalid. Using http/protobuf.'
+ );
+ delete env.OTEL_EXPORTER_OTLP_PROTOCOL;
+ });
+ });
+ describe('setup zipkin exporter from env', () => {
+ it('use the zipkin exporter', async () => {
+ env.OTEL_TRACES_EXPORTER = 'zipkin';
+ const sdk = new TracerProviderWithEnvExporters();
+ const listOfProcessors = sdk['_spanProcessors']!;
+ const listOfExporters = sdk['_configuredExporters'];
+
+ assert(listOfExporters.length === 1);
+ assert(listOfExporters[0] instanceof ZipkinExporter);
+ assert(listOfProcessors.length === 1);
+ assert(listOfProcessors[0] instanceof BatchSpanProcessor);
+ delete env.OTEL_TRACES_EXPORTER;
+ });
+ it('setup zipkin exporter and otlp exporter', async () => {
+ env.OTEL_TRACES_EXPORTER = 'zipkin, otlp';
+ env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL = 'grpc';
+
+ const sdk = new TracerProviderWithEnvExporters();
+ const listOfProcessors = sdk['_spanProcessors']!;
+ const listOfExporters = sdk['_configuredExporters'];
+
+ assert(spyGetOtlpProtocol.returned('grpc'));
+ assert(listOfExporters.length === 2);
+ assert(listOfExporters[0] instanceof ZipkinExporter);
+ assert(listOfExporters[1] instanceof OTLPGrpcTraceExporter);
+ assert(listOfProcessors[0] instanceof BatchSpanProcessor);
+ assert(listOfProcessors[1] instanceof BatchSpanProcessor);
+
+ delete env.OTEL_TRACES_EXPORTER;
+ delete env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL;
+ });
+ });
+ describe('setup jaeger exporter from env', () => {
+ it('use the jaeger exporter', async () => {
+ env.OTEL_TRACES_EXPORTER = 'jaeger';
+ const sdk = new TracerProviderWithEnvExporters();
+ const listOfProcessors = sdk['_spanProcessors']!;
+ const listOfExporters = sdk['_configuredExporters'];
+
+ assert(listOfExporters.length === 1);
+ assert(listOfExporters[0] instanceof JaegerExporter);
+ assert(listOfProcessors.length === 1);
+ assert(listOfProcessors[0] instanceof BatchSpanProcessor);
+ delete env.OTEL_TRACES_EXPORTER;
+ });
+ it('setup jaeger exporter and otlp exporter', async () => {
+ env.OTEL_TRACES_EXPORTER = 'jaeger, otlp';
+ env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL = 'http/json';
+ const sdk = new TracerProviderWithEnvExporters();
+ const listOfProcessors = sdk['_spanProcessors']!;
+ const listOfExporters = sdk['_configuredExporters'];
+
+ assert(spyGetOtlpProtocol.returned('http/json'));
+ assert(listOfExporters.length === 2);
+ assert(listOfExporters[0] instanceof JaegerExporter);
+ assert(listOfExporters[1] instanceof OTLPHttpTraceExporter);
+ assert(listOfProcessors.length === 2);
+ assert(listOfProcessors[0] instanceof BatchSpanProcessor);
+ assert(listOfProcessors[1] instanceof BatchSpanProcessor);
+
+ delete env.OTEL_TRACES_EXPORTER;
+ delete env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL;
+ });
+ });
+ describe('setup console exporter from env', () => {
+ it('use the console exporter', async () => {
+ env.OTEL_TRACES_EXPORTER = 'console';
+ const sdk = new TracerProviderWithEnvExporters();
+ const listOfProcessors = sdk['_spanProcessors']!;
+ const listOfExporters = sdk['_configuredExporters'];
+
+ assert(listOfExporters.length === 1);
+ assert(listOfExporters[0] instanceof ConsoleSpanExporter);
+ assert(listOfProcessors.length === 1);
+ assert(listOfProcessors[0] instanceof SimpleSpanProcessor);
+ delete env.OTEL_TRACES_EXPORTER;
+ });
+ it('ignores the protocol', async () => {
+ env.OTEL_TRACES_EXPORTER = 'console';
+ env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL = 'grpc';
+ const sdk = new TracerProviderWithEnvExporters();
+ const listOfProcessors = sdk['_spanProcessors']!;
+ const listOfExporters = sdk['_configuredExporters'];
+
+ assert(spyGetOtlpProtocol.notCalled);
+ assert(listOfExporters.length === 1);
+ assert(listOfExporters[0] instanceof ConsoleSpanExporter);
+ assert(listOfProcessors.length === 1);
+ assert(listOfProcessors[0] instanceof SimpleSpanProcessor);
+
+ delete env.OTEL_TRACES_EXPORTER;
+ delete env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL;
+ });
+ it('setup console exporter and otlp exporter', async () => {
+ env.OTEL_TRACES_EXPORTER = 'console, otlp';
+ env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL = 'grpc';
+ const sdk = new TracerProviderWithEnvExporters();
+ const listOfProcessors = sdk['_spanProcessors']!;
+ const listOfExporters = sdk['_configuredExporters'];
+
+ assert(spyGetOtlpProtocol.returned('grpc'));
+ assert(listOfExporters.length === 2);
+ assert(listOfExporters[0] instanceof ConsoleSpanExporter);
+ assert(listOfExporters[1] instanceof OTLPGrpcTraceExporter);
+ assert(listOfProcessors.length === 2);
+ assert(listOfProcessors[0] instanceof SimpleSpanProcessor);
+ assert(listOfProcessors[1] instanceof BatchSpanProcessor);
+
+ delete env.OTEL_TRACES_EXPORTER;
+ delete env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL;
+ });
+ });
+});
diff --git a/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts b/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts
index c1b0b90d567..12a38337441 100644
--- a/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts
+++ b/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts
@@ -44,17 +44,21 @@ import {
import {
ConsoleSpanExporter,
SimpleSpanProcessor,
+ BatchSpanProcessor,
+ NoopSpanProcessor,
} from '@opentelemetry/sdk-trace-base';
import * as assert from 'assert';
import * as semver from 'semver';
import * as Sinon from 'sinon';
import { NodeSDK } from '../src';
+import { env } from 'process';
+import { TracerProviderWithEnvExporters } from '../src/TracerProviderWithEnvExporter';
import {
envDetector,
processDetector,
Resource
} from '@opentelemetry/resources';
-
+import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
const DefaultContextManager = semver.gte(process.version, '14.8.0')
? AsyncLocalStorageContextManager
@@ -78,6 +82,9 @@ 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
+ env.OTEL_TRACES_EXPORTER = 'none';
const sdk = new NodeSDK({
autoDetectResources: false,
});
@@ -87,8 +94,8 @@ describe('Node SDK', () => {
assert.strictEqual(context['_getContextManager'](), ctxManager, 'context manager should not change');
assert.strictEqual(propagation['_getGlobalPropagator'](), propagator, 'propagator should not change');
assert.strictEqual((trace.getTracerProvider() as ProxyTracerProvider).getDelegate(), delegate, 'tracer provider should not have changed');
-
assert.ok(!(metrics.getMeterProvider() instanceof MeterProvider));
+ delete env.OTEL_TRACES_EXPORTER;
});
it('should register a tracer provider if an exporter is provided', async () => {
@@ -135,6 +142,9 @@ describe('Node SDK', () => {
});
it('should register a meter provider if a reader is provided', async () => {
+ // need to set OTEL_TRACES_EXPORTER to none since default value is otlp
+ // which sets up an exporter and affects the context manager
+ env.OTEL_TRACES_EXPORTER = 'none';
const exporter = new ConsoleMetricExporter();
const metricReader = new PeriodicExportingMetricReader({
exporter: exporter,
@@ -156,6 +166,7 @@ describe('Node SDK', () => {
assert.ok(metrics.getMeterProvider() instanceof MeterProvider);
await sdk.shutdown();
+ delete env.OTEL_TRACES_EXPORTER;
});
});
@@ -173,6 +184,9 @@ describe('Node SDK', () => {
}
it('should register meter views when provided', async () => {
+ // need to set OTEL_TRACES_EXPORTER to none since default value is otlp
+ // which sets up an exporter and affects the context manager
+ env.OTEL_TRACES_EXPORTER = 'none';
const exporter = new InMemoryMetricExporter(AggregationTemporality.CUMULATIVE);
const metricReader = new PeriodicExportingMetricReader({
exporter: exporter,
@@ -219,6 +233,7 @@ describe('Node SDK', () => {
assert.ok(firstMetricRecord.descriptor.name === 'test-view', 'should have renamed counter metric');
await sdk.shutdown();
+ delete env.OTEL_TRACES_EXPORTER;
});
it('should throw error when calling configureMeterProvider when views are already configured', () => {
@@ -471,7 +486,6 @@ describe('Node SDK', () => {
delete process.env.OTEL_SERVICE_NAME;
});
-
it('should configure service name via OTEL_RESOURCE_ATTRIBUTES env var', async () => {
process.env.OTEL_RESOURCE_ATTRIBUTES = 'service.name=resource-env-set-name';
const sdk = new NodeSDK();
@@ -501,3 +515,178 @@ describe('Node SDK', () => {
});
});
});
+
+describe('setup exporter from env', () => {
+ let spyGetOtlpProtocol: Sinon.SinonSpy;
+ let stubLoggerError: Sinon.SinonStub;
+
+ beforeEach(() => {
+ spyGetOtlpProtocol = Sinon.spy(TracerProviderWithEnvExporters, 'getOtlpProtocol');
+ stubLoggerError = Sinon.stub(diag, 'warn');
+ });
+ afterEach(() => {
+ spyGetOtlpProtocol.restore();
+ stubLoggerError.restore();
+ });
+ it('use default exporter TracerProviderWithEnvExporters when user does not provide span processor or trace exporter to sdk config', async () => {
+ const sdk = new NodeSDK();
+ await sdk.start();
+ const listOfProcessors = sdk['_tracerProvider']!['_registeredSpanProcessors']!;
+
+ assert(sdk['_tracerProvider'] instanceof TracerProviderWithEnvExporters);
+ assert(listOfProcessors.length === 1);
+ assert(listOfProcessors[0] instanceof BatchSpanProcessor);
+ });
+ it('ignore env exporter when user provides exporter to sdk config', async () => {
+ const traceExporter = new ConsoleSpanExporter();
+ const sdk = new NodeSDK({
+ traceExporter
+ });
+ await sdk.start();
+ const listOfProcessors = sdk['_tracerProvider']!['_registeredSpanProcessors']!;
+
+ assert(sdk['_tracerProvider'] instanceof TracerProviderWithEnvExporters === false);
+ assert(listOfProcessors.length === 1);
+ assert(listOfProcessors[0] instanceof SimpleSpanProcessor === false);
+ assert(listOfProcessors[0] instanceof BatchSpanProcessor);
+ });
+ it('ignores default env exporter when user provides span processor to sdk config', async () => {
+ const traceExporter = new ConsoleSpanExporter();
+ const spanProcessor = new SimpleSpanProcessor(traceExporter);
+ const sdk = new NodeSDK({
+ spanProcessor
+ });
+ await sdk.start();
+ const listOfProcessors = sdk['_tracerProvider']!['_registeredSpanProcessors']!;
+
+ assert(sdk['_tracerProvider'] instanceof TracerProviderWithEnvExporters === false);
+ assert(listOfProcessors.length === 1);
+ assert(listOfProcessors[0] instanceof SimpleSpanProcessor);
+ assert(listOfProcessors[0] instanceof BatchSpanProcessor === false);
+ });
+ it('ignores env exporter when user provides tracer exporter to sdk config and sets exporter via env', async () => {
+ env.OTEL_TRACES_EXPORTER = 'console';
+ const traceExporter = new OTLPTraceExporter();
+ const sdk = new NodeSDK({
+ traceExporter
+ });
+ await sdk.start();
+ const listOfProcessors = sdk['_tracerProvider']!['_registeredSpanProcessors']!;
+
+ assert(sdk['_tracerProvider'] instanceof TracerProviderWithEnvExporters === false);
+ assert(listOfProcessors.length === 1);
+ assert(listOfProcessors[0] instanceof SimpleSpanProcessor === false);
+ assert(listOfProcessors[0] instanceof BatchSpanProcessor);
+ delete env.OTEL_TRACES_EXPORTER;
+ });
+ it('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();
+ await sdk.start();
+
+ const listOfProcessors = sdk['_tracerProvider']!['_registeredSpanProcessors']!;
+ assert(sdk['_tracerProvider'] instanceof TracerProviderWithEnvExporters);
+ assert(listOfProcessors.length === 1);
+ assert(listOfProcessors[0] instanceof BatchSpanProcessor);
+ delete env.OTEL_TRACES_EXPORTER;
+ delete env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL;
+ });
+ it('use noop span processor when user sets env exporter to none', async () => {
+ env.OTEL_TRACES_EXPORTER = 'none';
+ const sdk = new NodeSDK();
+ await sdk.start();
+
+ const listOfProcessors = sdk['_tracerProvider']!['_registeredSpanProcessors']!;
+ const activeProcessor = sdk['_tracerProvider']?.getActiveSpanProcessor();
+
+ assert(listOfProcessors.length === 0);
+ assert(activeProcessor instanceof NoopSpanProcessor);
+ delete env.OTEL_TRACES_EXPORTER;
+ });
+ it('log warning that sdk will not be initalized when exporter is set to none', async () => {
+ env.OTEL_TRACES_EXPORTER = 'none';
+ const sdk = new NodeSDK();
+ await sdk.start();
+
+ assert.strictEqual(stubLoggerError.args[0][0], 'OTEL_TRACES_EXPORTER contains "none" or is empty. SDK will not be initialized.');
+ delete env.OTEL_TRACES_EXPORTER;
+ });
+ it('do not use any exporters when empty value is provided for exporter', async () => {
+ env.OTEL_TRACES_EXPORTER = '';
+ const sdk = new NodeSDK();
+ await sdk.start();
+
+ const listOfProcessors = sdk['_tracerProvider']!['_registeredSpanProcessors']!;
+ const activeProcessor = sdk['_tracerProvider']?.getActiveSpanProcessor();
+
+ assert(listOfProcessors.length === 0);
+ assert(activeProcessor instanceof NoopSpanProcessor);
+ env.OTEL_TRACES_EXPORTER = '';
+ });
+
+ it('use only default exporter when none value is provided with other exporters', async () => {
+ env.OTEL_TRACES_EXPORTER = 'otlp,zipkin,none';
+ const sdk = new NodeSDK();
+ await sdk.start();
+
+ const listOfProcessors = sdk['_tracerProvider']!['_registeredSpanProcessors']!;
+ assert(sdk['_tracerProvider'] instanceof TracerProviderWithEnvExporters);
+ assert(listOfProcessors.length === 1);
+ assert(listOfProcessors[0] instanceof BatchSpanProcessor);
+
+ delete env.OTEL_TRACES_EXPORTER;
+ });
+ it('log warning that only default exporter will be used since exporter list contains none with other exports ', async () => {
+ env.OTEL_TRACES_EXPORTER = 'otlp,zipkin,none';
+ const sdk = new NodeSDK();
+ await sdk.start();
+
+ assert.strictEqual(
+ stubLoggerError.args[0][0], 'OTEL_TRACES_EXPORTER contains "none" along with other exporters. Using default otlp exporter.'
+ );
+ delete env.OTEL_TRACES_EXPORTER;
+ });
+ 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();
+ await sdk.start();
+
+ assert.strictEqual(
+ stubLoggerError.args[0][0], 'Unrecognized OTEL_TRACES_EXPORTER value: invalid.'
+ );
+
+ assert.strictEqual(
+ stubLoggerError.args[1][0], 'Unable to set up trace exporter(s) due to invalid exporter and/or protocol values.'
+ );
+
+ delete env.OTEL_TRACES_EXPORTER;
+ });
+ it('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();
+ await sdk.start();
+
+ const listOfProcessors = sdk['_tracerProvider']!['_registeredSpanProcessors']!;
+ assert(sdk['_tracerProvider'] instanceof TracerProviderWithEnvExporters);
+ assert(listOfProcessors.length === 3);
+ assert(listOfProcessors[0] instanceof BatchSpanProcessor);
+ assert(listOfProcessors[1] instanceof BatchSpanProcessor);
+ assert(listOfProcessors[2] instanceof BatchSpanProcessor);
+
+ delete env.OTEL_TRACES_EXPORTER;
+ delete env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL;
+ });
+ it('use the console exporter', async () => {
+ env.OTEL_TRACES_EXPORTER = 'console, otlp';
+ const sdk = new NodeSDK();
+ await sdk.start();
+
+ const listOfProcessors = sdk['_tracerProvider']!['_registeredSpanProcessors']!;
+ assert(listOfProcessors.length === 2);
+ assert(listOfProcessors[0] instanceof SimpleSpanProcessor);
+ assert(listOfProcessors[1] instanceof BatchSpanProcessor);
+ delete env.OTEL_TRACES_EXPORTER;
+ });
+});
diff --git a/experimental/packages/opentelemetry-sdk-node/tsconfig.json b/experimental/packages/opentelemetry-sdk-node/tsconfig.json
index 3d1db307391..55448ab7888 100644
--- a/experimental/packages/opentelemetry-sdk-node/tsconfig.json
+++ b/experimental/packages/opentelemetry-sdk-node/tsconfig.json
@@ -18,6 +18,12 @@
{
"path": "../../../packages/opentelemetry-core"
},
+ {
+ "path": "../../../packages/opentelemetry-exporter-jaeger"
+ },
+ {
+ "path": "../../../packages/opentelemetry-exporter-zipkin"
+ },
{
"path": "../../../packages/opentelemetry-resources"
},
@@ -30,6 +36,15 @@
{
"path": "../../../packages/opentelemetry-semantic-conventions"
},
+ {
+ "path": "../exporter-trace-otlp-grpc"
+ },
+ {
+ "path": "../exporter-trace-otlp-http"
+ },
+ {
+ "path": "../exporter-trace-otlp-proto"
+ },
{
"path": "../opentelemetry-api-metrics"
},
diff --git a/packages/opentelemetry-core/src/utils/environment.ts b/packages/opentelemetry-core/src/utils/environment.ts
index 05ba058f0fa..8d50e2cb9bc 100644
--- a/packages/opentelemetry-core/src/utils/environment.ts
+++ b/packages/opentelemetry-core/src/utils/environment.ts
@@ -102,7 +102,10 @@ export type ENVIRONMENT = {
OTEL_EXPORTER_OTLP_METRICS_CLIENT_KEY?: string,
OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE?: string,
OTEL_EXPORTER_OTLP_TRACES_CLIENT_CERTIFICATE?: string,
- OTEL_EXPORTER_OTLP_METRICS_CLIENT_CERTIFICATE?: string
+ OTEL_EXPORTER_OTLP_METRICS_CLIENT_CERTIFICATE?: string,
+ OTEL_EXPORTER_OTLP_PROTOCOL?: string,
+ OTEL_EXPORTER_OTLP_TRACES_PROTOCOL?: string,
+ OTEL_EXPORTER_OTLP_METRICS_PROTOCOL?: string,
} & ENVIRONMENT_NUMBERS &
ENVIRONMENT_LISTS;
@@ -154,7 +157,7 @@ export const DEFAULT_ENVIRONMENT: Required = {
OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT: DEFAULT_ATTRIBUTE_COUNT_LIMIT,
OTEL_SPAN_EVENT_COUNT_LIMIT: 128,
OTEL_SPAN_LINK_COUNT_LIMIT: 128,
- OTEL_TRACES_EXPORTER: 'none',
+ OTEL_TRACES_EXPORTER: 'otlp',
OTEL_TRACES_SAMPLER: TracesSamplerValues.ParentBasedAlwaysOn,
OTEL_TRACES_SAMPLER_ARG: '',
OTEL_EXPORTER_OTLP_INSECURE: '',
@@ -171,7 +174,10 @@ export const DEFAULT_ENVIRONMENT: Required = {
OTEL_EXPORTER_OTLP_METRICS_CLIENT_KEY: '',
OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE: '',
OTEL_EXPORTER_OTLP_TRACES_CLIENT_CERTIFICATE: '',
- OTEL_EXPORTER_OTLP_METRICS_CLIENT_CERTIFICATE: ''
+ OTEL_EXPORTER_OTLP_METRICS_CLIENT_CERTIFICATE: '',
+ OTEL_EXPORTER_OTLP_PROTOCOL: 'http/protobuf',
+ OTEL_EXPORTER_OTLP_TRACES_PROTOCOL: 'http/protobuf',
+ OTEL_EXPORTER_OTLP_METRICS_PROTOCOL: 'http/protobuf',
};
/**