From cf64bff4856bf9cdf6e2d16c022f4dbc3ce4d378 Mon Sep 17 00:00:00 2001 From: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> Date: Wed, 27 Jul 2022 17:13:55 -0500 Subject: [PATCH 01/38] feat(sdk-node): wip add functionality to retrieve list of exporters Signed-off-by: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> --- .../opentelemetry-sdk-node/src/sdk.ts | 54 +++++++++++++++++-- 1 file changed, 49 insertions(+), 5 deletions(-) diff --git a/experimental/packages/opentelemetry-sdk-node/src/sdk.ts b/experimental/packages/opentelemetry-sdk-node/src/sdk.ts index f1f4815829e..55009b00a24 100644 --- a/experimental/packages/opentelemetry-sdk-node/src/sdk.ts +++ b/experimental/packages/opentelemetry-sdk-node/src/sdk.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { ContextManager, TextMapPropagator } from '@opentelemetry/api'; +import { ContextManager, TextMapPropagator, diag } from '@opentelemetry/api'; import { metrics } from '@opentelemetry/api-metrics'; import { InstrumentationOption, @@ -30,11 +30,14 @@ import { import { MeterProvider, MetricReader } from '@opentelemetry/sdk-metrics-base'; import { BatchSpanProcessor, - SpanProcessor + SpanProcessor, + SpanExporter, + SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base'; import { NodeTracerConfig, NodeTracerProvider } from '@opentelemetry/sdk-trace-node'; import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'; import { NodeSDKConfiguration } from './types'; +import { getEnv } from '@opentelemetry/core'; /** This class represents everything needed to register a fully configured OpenTelemetry Node.js SDK */ export class NodeSDK { @@ -46,14 +49,12 @@ export class NodeSDK { }; private _instrumentations: InstrumentationOption[]; private _metricReader?: MetricReader; - private _resource: Resource; - private _autoDetectResources: boolean; - private _tracerProvider?: NodeTracerProvider; private _meterProvider?: MeterProvider; private _serviceName?: string; + private _spanProcessors?: (BatchSpanProcessor | SimpleSpanProcessor)[]; /** * Create a new NodeJS SDK instance @@ -85,6 +86,9 @@ export class NodeSDK { configuration.contextManager, configuration.textMapPropagator ); + // create trace exporter(s) from env + } else { + this.createTraceExportersFromEnv(); } if (configuration.metricReader) { @@ -98,6 +102,46 @@ export class NodeSDK { this._instrumentations = instrumentations; } + public createTraceExportersFromEnv() { + let traceExportersList = this.retrieveListOfTraceExporters(); + + 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']; + } + + const configuredExporters: SpanExporter[] = + traceExportersList.map(exporterName => { + return this.configureExporter(exporterName); + }); + + this._spanProcessors = this.configureSpanProcessors(configuredExporters); + } + } + + // visible for testing + public retrieveListOfTraceExporters(): string[] { + const traceList = getEnv().OTEL_TRACES_EXPORTER.split(','); + const uniqueTraceExporters = Array.from(new Set(traceList)); + + return this.filterBlanksAndNulls(uniqueTraceExporters); + } + + private filterBlanksAndNulls(list: string[]): string[] { + return list.map(item => item.trim()) + .filter(s => s !== 'null' && s !== ''); + } + + public configureExporter(name: string): SpanExporter { + } + + // visible for testing + public configureSpanProcessors(exporters: SpanExporter[]): (BatchSpanProcessor | SimpleSpanProcessor)[] { + } + /** Set configurations required to register a NodeTracerProvider */ public configureTracerProvider( tracerConfig: NodeTracerConfig, From f6ff700abfa2e6c091c420a0851dce969d57ec42 Mon Sep 17 00:00:00 2001 From: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> Date: Thu, 28 Jul 2022 09:16:48 -0500 Subject: [PATCH 02/38] feat(sdk-node): wip add functionality to configure otlp Signed-off-by: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> --- .../opentelemetry-sdk-node/package.json | 3 ++ .../opentelemetry-sdk-node/src/sdk.ts | 28 +++++++++++++++++-- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/experimental/packages/opentelemetry-sdk-node/package.json b/experimental/packages/opentelemetry-sdk-node/package.json index 1949eb45bf7..45c825572fc 100644 --- a/experimental/packages/opentelemetry-sdk-node/package.json +++ b/experimental/packages/opentelemetry-sdk-node/package.json @@ -46,6 +46,9 @@ "dependencies": { "@opentelemetry/api-metrics": "0.31.0", "@opentelemetry/core": "1.5.0", + "@opentelemetry/exporter-trace-otlp-grpc": "^0.31.0", + "@opentelemetry/exporter-trace-otlp-http": "^0.31.0", + "@opentelemetry/exporter-trace-otlp-proto": "^0.31.0", "@opentelemetry/instrumentation": "0.31.0", "@opentelemetry/resources": "1.5.0", "@opentelemetry/sdk-metrics-base": "0.31.0", diff --git a/experimental/packages/opentelemetry-sdk-node/src/sdk.ts b/experimental/packages/opentelemetry-sdk-node/src/sdk.ts index 55009b00a24..c64251e6847 100644 --- a/experimental/packages/opentelemetry-sdk-node/src/sdk.ts +++ b/experimental/packages/opentelemetry-sdk-node/src/sdk.ts @@ -38,6 +38,9 @@ import { NodeTracerConfig, NodeTracerProvider } from '@opentelemetry/sdk-trace-n import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'; import { NodeSDKConfiguration } from './types'; import { getEnv } from '@opentelemetry/core'; +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'; /** This class represents everything needed to register a fully configured OpenTelemetry Node.js SDK */ export class NodeSDK { @@ -55,6 +58,7 @@ export class NodeSDK { private _meterProvider?: MeterProvider; private _serviceName?: string; private _spanProcessors?: (BatchSpanProcessor | SimpleSpanProcessor)[]; + private DATA_TYPE_TRACES = 'traces'; /** * Create a new NodeJS SDK instance @@ -136,10 +140,30 @@ export class NodeSDK { } public configureExporter(name: string): SpanExporter { + switch (name) { + default: + return this.configureOtlp(); + } } - // visible for testing - public configureSpanProcessors(exporters: SpanExporter[]): (BatchSpanProcessor | SimpleSpanProcessor)[] { + public configureOtlp(): SpanExporter { + const protocol = this.getOtlpProtocol(this.DATA_TYPE_TRACES); + + switch (protocol) { + case 'grpc': + return new OTLPGrpcTraceExporter(); + case 'http/json': + return new OTLPHttpTraceExporter(); + default: + return new OTLPProtoTraceExporter(); + } + } + + public getOtlpProtocol(dataType: string): string { + } + + // visible for testing + public configureSpanProcessors(exporters: SpanExporter[]): (BatchSpanProcessor | SimpleSpanProcessor)[] { } /** Set configurations required to register a NodeTracerProvider */ From 225976168623e60a22763f8d9fa1608b60e2490f Mon Sep 17 00:00:00 2001 From: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> Date: Thu, 28 Jul 2022 10:31:06 -0500 Subject: [PATCH 03/38] feat(sdk-node): wip add otlp protocol retrieval Signed-off-by: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> --- .../packages/opentelemetry-sdk-node/src/sdk.ts | 10 ++++++++++ packages/opentelemetry-core/src/utils/environment.ts | 10 ++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/experimental/packages/opentelemetry-sdk-node/src/sdk.ts b/experimental/packages/opentelemetry-sdk-node/src/sdk.ts index c64251e6847..2bd986e5f89 100644 --- a/experimental/packages/opentelemetry-sdk-node/src/sdk.ts +++ b/experimental/packages/opentelemetry-sdk-node/src/sdk.ts @@ -159,7 +159,17 @@ export class NodeSDK { } } + // todo: update retrieval of env using alternative to getEnv and add defaults back to environment.ts file public getOtlpProtocol(dataType: string): string { + const DEFAULT_OTLP_PROTOCOL = 'http/protobuf'; + switch (dataType) { + case 'traces': + return getEnv().OTEL_EXPORTER_OTLP_TRACES_PROTOCOL || getEnv().OTEL_EXPORTER_OTLP_PROTOCOL || DEFAULT_OTLP_PROTOCOL; + case 'metrics': + return getEnv().OTEL_EXPORTER_OTLP_METRICS_PROTOCOL || getEnv().OTEL_EXPORTER_OTLP_PROTOCOL || DEFAULT_OTLP_PROTOCOL; + default: + return getEnv().OTEL_EXPORTER_OTLP_PROTOCOL || DEFAULT_OTLP_PROTOCOL; + } } // visible for testing diff --git a/packages/opentelemetry-core/src/utils/environment.ts b/packages/opentelemetry-core/src/utils/environment.ts index def0fe48469..224c4871b72 100644 --- a/packages/opentelemetry-core/src/utils/environment.ts +++ b/packages/opentelemetry-core/src/utils/environment.ts @@ -101,7 +101,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; @@ -170,7 +173,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: '', + OTEL_EXPORTER_OTLP_TRACES_PROTOCOL: '', + OTEL_EXPORTER_OTLP_METRICS_PROTOCOL: '', }; /** From 651b5c7b1577d0b4336d774b7e11479b53fc504b Mon Sep 17 00:00:00 2001 From: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> Date: Thu, 28 Jul 2022 10:33:46 -0500 Subject: [PATCH 04/38] feat(sdk-node): wip configure span processor Signed-off-by: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> --- .../packages/opentelemetry-sdk-node/src/sdk.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/experimental/packages/opentelemetry-sdk-node/src/sdk.ts b/experimental/packages/opentelemetry-sdk-node/src/sdk.ts index 2bd986e5f89..53b57055baa 100644 --- a/experimental/packages/opentelemetry-sdk-node/src/sdk.ts +++ b/experimental/packages/opentelemetry-sdk-node/src/sdk.ts @@ -32,7 +32,8 @@ import { BatchSpanProcessor, SpanProcessor, SpanExporter, - SimpleSpanProcessor + SimpleSpanProcessor, + ConsoleSpanExporter } from '@opentelemetry/sdk-trace-base'; import { NodeTracerConfig, NodeTracerProvider } from '@opentelemetry/sdk-trace-node'; import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'; @@ -173,7 +174,14 @@ export class NodeSDK { } // visible for testing - public configureSpanProcessors(exporters: SpanExporter[]): (BatchSpanProcessor | SimpleSpanProcessor)[] { + public configureSpanProcessors(exporters: SpanExporter[]): (BatchSpanProcessor | SimpleSpanProcessor)[] { + return exporters.map(exporter => { + if (exporter instanceof ConsoleSpanExporter) { + return new SimpleSpanProcessor(exporter); + } else { + return new BatchSpanProcessor(exporter); + } + }); } /** Set configurations required to register a NodeTracerProvider */ From 053175c3925e3cce40fe10c628a30131c34a9cd2 Mon Sep 17 00:00:00 2001 From: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> Date: Thu, 28 Jul 2022 10:39:58 -0500 Subject: [PATCH 05/38] feat(sdk-node): wip create node tracer providers Signed-off-by: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> --- experimental/packages/opentelemetry-sdk-node/src/sdk.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/experimental/packages/opentelemetry-sdk-node/src/sdk.ts b/experimental/packages/opentelemetry-sdk-node/src/sdk.ts index 53b57055baa..60f7374926b 100644 --- a/experimental/packages/opentelemetry-sdk-node/src/sdk.ts +++ b/experimental/packages/opentelemetry-sdk-node/src/sdk.ts @@ -248,6 +248,15 @@ export class NodeSDK { contextManager: this._tracerProviderConfig.contextManager, propagator: this._tracerProviderConfig.textMapPropagator, }); + } else if (this._spanProcessors) { + const tracerProvider = new NodeTracerProvider(); + this._tracerProvider = tracerProvider; + + this._spanProcessors.forEach(processor => { + this._tracerProvider?.addSpanProcessor(processor); + }); + + tracerProvider.register(); } if (this._metricReader) { From a792d5de39aa475539ea8296aa87083b19d408ad Mon Sep 17 00:00:00 2001 From: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> Date: Thu, 28 Jul 2022 12:54:10 -0500 Subject: [PATCH 06/38] feat(sdk-node): add otlp exporter tests Signed-off-by: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> --- .../opentelemetry-sdk-node/src/sdk.ts | 1 + .../opentelemetry-sdk-node/test/sdk.test.ts | 112 +++++++++++++++++- .../opentelemetry-sdk-node/tsconfig.json | 9 ++ .../src/utils/environment.ts | 2 +- 4 files changed, 122 insertions(+), 2 deletions(-) diff --git a/experimental/packages/opentelemetry-sdk-node/src/sdk.ts b/experimental/packages/opentelemetry-sdk-node/src/sdk.ts index 60f7374926b..b9e337838c6 100644 --- a/experimental/packages/opentelemetry-sdk-node/src/sdk.ts +++ b/experimental/packages/opentelemetry-sdk-node/src/sdk.ts @@ -129,6 +129,7 @@ export class NodeSDK { // visible for testing public retrieveListOfTraceExporters(): string[] { + // todo: change default back to otlp and use alternative to getEnv const traceList = getEnv().OTEL_TRACES_EXPORTER.split(','); const uniqueTraceExporters = Array.from(new Set(traceList)); diff --git a/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts b/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts index 217800c9c05..be26f3ad7cb 100644 --- a/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts +++ b/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts @@ -36,13 +36,17 @@ import { import { ConsoleSpanExporter, SimpleSpanProcessor, + BatchSpanProcessor } 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 { envDetector, processDetector } from '@opentelemetry/resources'; - +import { env } from 'process'; +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'; const DefaultContextManager = semver.gte(process.version, '14.8.0') ? AsyncLocalStorageContextManager @@ -334,3 +338,109 @@ describe('Node SDK', () => { }); }); }); + +describe('setup exporter from env', () => { + let spyExporterList: Sinon.SinonSpy; + let spyConfigureExporter: Sinon.SinonSpy; + let spyConfigureSpanProcessors: Sinon.SinonSpy; + let spyGetOtlpProtocol: Sinon.SinonSpy; + let stubLoggerError: Sinon.SinonStub; + beforeEach(() => { + spyExporterList = Sinon.spy(NodeSDK.prototype, 'retrieveListOfTraceExporters'); + spyConfigureExporter = Sinon.spy(NodeSDK.prototype, 'configureExporter'); + spyConfigureSpanProcessors = Sinon.spy(NodeSDK.prototype, 'configureSpanProcessors'); + spyGetOtlpProtocol = Sinon.spy(NodeSDK.prototype, 'getOtlpProtocol'); + stubLoggerError = Sinon.stub(diag, 'warn'); + }); + afterEach(() => { + spyExporterList.restore(); + spyConfigureExporter.restore(); + spyConfigureSpanProcessors.restore(); + spyGetOtlpProtocol.restore(); + stubLoggerError.restore(); + }); + describe('set up otlp exporter from env', () => { + it('do not set up any exporter(s) when no exporters listed', () => { + new NodeSDK(); + assert(spyExporterList.returned([])); + assert(spyConfigureExporter.notCalled); + }); + it('use otlp exporter and defined exporter protocol env value', () => { + env.OTEL_TRACES_EXPORTER = 'otlp'; + env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL = 'grpc'; + new NodeSDK(); + const listOfProcessors = spyConfigureSpanProcessors.returnValues[0]; + const listOfExporters = spyConfigureSpanProcessors.args[0][0]; + + assert(spyExporterList.returned(['otlp'])); + assert(spyConfigureExporter.calledWith('otlp')); + 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', () => { + env.OTEL_TRACES_EXPORTER = 'otlp'; + env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL = 'http/protobuf'; + env.OTEL_EXPORTER_OTLP_PROTOCOL = 'grpc'; + new NodeSDK(); + + assert(spyExporterList.returned(['otlp'])); + assert(spyConfigureExporter.calledWith('otlp')); + assert(spyGetOtlpProtocol.returned('http/protobuf')); + 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', () => { + env.OTEL_TRACES_EXPORTER = ''; + new NodeSDK(); + + assert(spyExporterList.returned([])); + assert(spyConfigureExporter.notCalled); + env.OTEL_TRACES_EXPORTER = ''; + }); + it('do not use any exporters when none value is only provided', () => { + env.OTEL_TRACES_EXPORTER = 'none'; + new NodeSDK(); + + assert(spyExporterList.returned(['none'])); + assert(spyConfigureExporter.notCalled); + delete env.OTEL_TRACES_EXPORTER; + }); + it('log warning that sdk will not be initalized when exporter is set to none', () => { + env.OTEL_TRACES_EXPORTER = 'none'; + new NodeSDK(); + + 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', () => { + env.OTEL_TRACES_EXPORTER = 'otlp,zipkin,none'; + new NodeSDK(); + const listOfProcessors = spyConfigureSpanProcessors.returnValues[0]; + const listOfExporters = spyConfigureSpanProcessors.args[0][0]; + + assert(spyExporterList.returned(['otlp', 'zipkin', 'none'])); + assert(spyConfigureExporter.calledWith('otlp')); + assert(listOfExporters[0] instanceof OTLPProtoTraceExporter); + assert(listOfExporters.length === 1); + assert(listOfExporters[0] instanceof OTLPHttpTraceExporter === 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 ', () => { + env.OTEL_TRACES_EXPORTER = 'otlp,zipkin,none'; + new NodeSDK(); + + assert.strictEqual( + stubLoggerError.args[0][0], 'OTEL_TRACES_EXPORTER contains "none" along with other exporters. Using default otlp exporter.' + ); + delete env.OTEL_TRACES_EXPORTER; + }); + }); +}); \ No newline at end of file diff --git a/experimental/packages/opentelemetry-sdk-node/tsconfig.json b/experimental/packages/opentelemetry-sdk-node/tsconfig.json index c0fe67144f2..ead9ec56c1f 100644 --- a/experimental/packages/opentelemetry-sdk-node/tsconfig.json +++ b/experimental/packages/opentelemetry-sdk-node/tsconfig.json @@ -27,6 +27,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 224c4871b72..d720ebf88e0 100644 --- a/packages/opentelemetry-core/src/utils/environment.ts +++ b/packages/opentelemetry-core/src/utils/environment.ts @@ -156,7 +156,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: '', OTEL_TRACES_SAMPLER: TracesSamplerValues.ParentBasedAlwaysOn, OTEL_TRACES_SAMPLER_ARG: '', OTEL_EXPORTER_OTLP_INSECURE: '', From d852a9dab2feeb9d8cfdb810490fc1009361a2b0 Mon Sep 17 00:00:00 2001 From: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> Date: Thu, 28 Jul 2022 16:33:24 -0500 Subject: [PATCH 07/38] feat(sdk-node): refactor otlp protocol logic Signed-off-by: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> --- .../packages/opentelemetry-sdk-node/src/sdk.ts | 10 ++++------ .../opentelemetry-sdk-node/test/sdk.test.ts | 16 ++++++++++++---- .../opentelemetry-core/src/utils/environment.ts | 8 ++++---- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/experimental/packages/opentelemetry-sdk-node/src/sdk.ts b/experimental/packages/opentelemetry-sdk-node/src/sdk.ts index b9e337838c6..92008278891 100644 --- a/experimental/packages/opentelemetry-sdk-node/src/sdk.ts +++ b/experimental/packages/opentelemetry-sdk-node/src/sdk.ts @@ -129,7 +129,7 @@ export class NodeSDK { // visible for testing public retrieveListOfTraceExporters(): string[] { - // todo: change default back to otlp and use alternative to getEnv + // should we setup the default exporter when user doesn't provide specifc values const traceList = getEnv().OTEL_TRACES_EXPORTER.split(','); const uniqueTraceExporters = Array.from(new Set(traceList)); @@ -161,16 +161,14 @@ export class NodeSDK { } } - // todo: update retrieval of env using alternative to getEnv and add defaults back to environment.ts file public getOtlpProtocol(dataType: string): string { - const DEFAULT_OTLP_PROTOCOL = 'http/protobuf'; switch (dataType) { case 'traces': - return getEnv().OTEL_EXPORTER_OTLP_TRACES_PROTOCOL || getEnv().OTEL_EXPORTER_OTLP_PROTOCOL || DEFAULT_OTLP_PROTOCOL; + return getEnv().OTEL_EXPORTER_OTLP_TRACES_PROTOCOL || getEnv().OTEL_EXPORTER_OTLP_PROTOCOL; case 'metrics': - return getEnv().OTEL_EXPORTER_OTLP_METRICS_PROTOCOL || getEnv().OTEL_EXPORTER_OTLP_PROTOCOL || DEFAULT_OTLP_PROTOCOL; + return getEnv().OTEL_EXPORTER_OTLP_METRICS_PROTOCOL || getEnv().OTEL_EXPORTER_OTLP_PROTOCOL; default: - return getEnv().OTEL_EXPORTER_OTLP_PROTOCOL || DEFAULT_OTLP_PROTOCOL; + return getEnv().OTEL_EXPORTER_OTLP_PROTOCOL; } } diff --git a/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts b/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts index be26f3ad7cb..3f02e2ce9dd 100644 --- a/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts +++ b/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts @@ -339,7 +339,7 @@ describe('Node SDK', () => { }); }); -describe('setup exporter from env', () => { +describe.only('setup exporter from env', () => { let spyExporterList: Sinon.SinonSpy; let spyConfigureExporter: Sinon.SinonSpy; let spyConfigureSpanProcessors: Sinon.SinonSpy; @@ -360,10 +360,18 @@ describe('setup exporter from env', () => { stubLoggerError.restore(); }); describe('set up otlp exporter from env', () => { - it('do not set up any exporter(s) when no exporters listed', () => { + it('set up default exporter when user does not define otel trace exporter', () => { new NodeSDK(); - assert(spyExporterList.returned([])); - assert(spyConfigureExporter.notCalled); + const listOfProcessors = spyConfigureSpanProcessors.returnValues[0]; + const listOfExporters = spyConfigureSpanProcessors.args[0][0]; + + assert(spyExporterList.returned(['otlp'])); + assert(spyConfigureExporter.calledWith('otlp')); + 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 defined exporter protocol env value', () => { env.OTEL_TRACES_EXPORTER = 'otlp'; diff --git a/packages/opentelemetry-core/src/utils/environment.ts b/packages/opentelemetry-core/src/utils/environment.ts index 1f6bf4d47e5..8d50e2cb9bc 100644 --- a/packages/opentelemetry-core/src/utils/environment.ts +++ b/packages/opentelemetry-core/src/utils/environment.ts @@ -157,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: '', + OTEL_TRACES_EXPORTER: 'otlp', OTEL_TRACES_SAMPLER: TracesSamplerValues.ParentBasedAlwaysOn, OTEL_TRACES_SAMPLER_ARG: '', OTEL_EXPORTER_OTLP_INSECURE: '', @@ -175,9 +175,9 @@ export const DEFAULT_ENVIRONMENT: Required = { OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE: '', OTEL_EXPORTER_OTLP_TRACES_CLIENT_CERTIFICATE: '', OTEL_EXPORTER_OTLP_METRICS_CLIENT_CERTIFICATE: '', - OTEL_EXPORTER_OTLP_PROTOCOL: '', - OTEL_EXPORTER_OTLP_TRACES_PROTOCOL: '', - OTEL_EXPORTER_OTLP_METRICS_PROTOCOL: '', + OTEL_EXPORTER_OTLP_PROTOCOL: 'http/protobuf', + OTEL_EXPORTER_OTLP_TRACES_PROTOCOL: 'http/protobuf', + OTEL_EXPORTER_OTLP_METRICS_PROTOCOL: 'http/protobuf', }; /** From b2eee41c6380c473536794a7d3eca3423aa81d98 Mon Sep 17 00:00:00 2001 From: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> Date: Fri, 29 Jul 2022 10:11:26 -0500 Subject: [PATCH 08/38] feat(sdk-node): refactor configure exporter function Signed-off-by: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> --- .../opentelemetry-sdk-node/src/sdk.ts | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/experimental/packages/opentelemetry-sdk-node/src/sdk.ts b/experimental/packages/opentelemetry-sdk-node/src/sdk.ts index 92008278891..dc227222294 100644 --- a/experimental/packages/opentelemetry-sdk-node/src/sdk.ts +++ b/experimental/packages/opentelemetry-sdk-node/src/sdk.ts @@ -118,10 +118,14 @@ export class NodeSDK { traceExportersList = ['otlp']; } - const configuredExporters: SpanExporter[] = - traceExportersList.map(exporterName => { - return this.configureExporter(exporterName); - }); + let configuredExporters: SpanExporter[] = []; + + traceExportersList.forEach(exporterName => { + const exporter = this.configureExporter(exporterName); + if (exporter) { + configuredExporters.push(exporter); + } + }); this._spanProcessors = this.configureSpanProcessors(configuredExporters); } @@ -141,10 +145,13 @@ export class NodeSDK { .filter(s => s !== 'null' && s !== ''); } - public configureExporter(name: string): SpanExporter { + public configureExporter(name: string): SpanExporter | null { switch (name) { - default: + case 'otlp': return this.configureOtlp(); + default: + diag.warn(`Unrecognized OTEL_TRACES_EXPORTER value ${name}.`); + return null; } } From 0d3b81986e6266cfd10e5e51ec54f4e32565a1d4 Mon Sep 17 00:00:00 2001 From: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> Date: Fri, 29 Jul 2022 10:27:45 -0500 Subject: [PATCH 09/38] feat(sdk-node): add zipkin and jaeger exporters and tests Signed-off-by: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> --- .../opentelemetry-sdk-node/package.json | 2 + .../opentelemetry-sdk-node/src/sdk.ts | 15 +++- .../opentelemetry-sdk-node/test/sdk.test.ts | 72 +++++++++++++++++++ .../opentelemetry-sdk-node/tsconfig.json | 6 ++ 4 files changed, 92 insertions(+), 3 deletions(-) diff --git a/experimental/packages/opentelemetry-sdk-node/package.json b/experimental/packages/opentelemetry-sdk-node/package.json index 45c825572fc..e24425829ae 100644 --- a/experimental/packages/opentelemetry-sdk-node/package.json +++ b/experimental/packages/opentelemetry-sdk-node/package.json @@ -46,9 +46,11 @@ "dependencies": { "@opentelemetry/api-metrics": "0.31.0", "@opentelemetry/core": "1.5.0", + "@opentelemetry/exporter-jaeger": "^1.5.0", "@opentelemetry/exporter-trace-otlp-grpc": "^0.31.0", "@opentelemetry/exporter-trace-otlp-http": "^0.31.0", "@opentelemetry/exporter-trace-otlp-proto": "^0.31.0", + "@opentelemetry/exporter-zipkin": "^1.5.0", "@opentelemetry/instrumentation": "0.31.0", "@opentelemetry/resources": "1.5.0", "@opentelemetry/sdk-metrics-base": "0.31.0", diff --git a/experimental/packages/opentelemetry-sdk-node/src/sdk.ts b/experimental/packages/opentelemetry-sdk-node/src/sdk.ts index dc227222294..acb2322d920 100644 --- a/experimental/packages/opentelemetry-sdk-node/src/sdk.ts +++ b/experimental/packages/opentelemetry-sdk-node/src/sdk.ts @@ -42,6 +42,8 @@ import { getEnv } from '@opentelemetry/core'; 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'; /** This class represents everything needed to register a fully configured OpenTelemetry Node.js SDK */ export class NodeSDK { @@ -149,13 +151,17 @@ export class NodeSDK { switch (name) { case 'otlp': return this.configureOtlp(); + case 'zipkin': + return new ZipkinExporter(); + case 'jaeger': + return new JaegerExporter(); default: - diag.warn(`Unrecognized OTEL_TRACES_EXPORTER value ${name}.`); + diag.warn(`Unrecognized OTEL_TRACES_EXPORTER value: ${name}.`); return null; } } - public configureOtlp(): SpanExporter { + public configureOtlp(): SpanExporter | null { const protocol = this.getOtlpProtocol(this.DATA_TYPE_TRACES); switch (protocol) { @@ -163,8 +169,11 @@ export class NodeSDK { return new OTLPGrpcTraceExporter(); case 'http/json': return new OTLPHttpTraceExporter(); - default: + case 'http/protobuf': return new OTLPProtoTraceExporter(); + default: + diag.warn(`Unsupported OTLP traces protocol: ${protocol}.`); + return null; } } diff --git a/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts b/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts index 3f02e2ce9dd..5749ced08e8 100644 --- a/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts +++ b/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts @@ -47,6 +47,8 @@ import { env } from 'process'; 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'; const DefaultContextManager = semver.gte(process.version, '14.8.0') ? AsyncLocalStorageContextManager @@ -451,4 +453,74 @@ describe.only('setup exporter from env', () => { delete env.OTEL_TRACES_EXPORTER; }); }); + describe('setup zipkin exporter from env', () => { + it('use the zipkin exporter', () => { + env.OTEL_TRACES_EXPORTER = 'zipkin'; + new NodeSDK(); + const listOfProcessors = spyConfigureSpanProcessors.returnValues[0]; + const listOfExporters = spyConfigureSpanProcessors.args[0][0]; + + assert(spyExporterList.returned(['zipkin'])); + assert(spyConfigureExporter.calledWith('zipkin')); + 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', () => { + env.OTEL_TRACES_EXPORTER = 'zipkin, otlp'; + env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL = 'grpc'; + new NodeSDK(); + const listOfProcessors = spyConfigureSpanProcessors.returnValues[0]; + const listOfExporters = spyConfigureSpanProcessors.args[0][0]; + + assert(spyExporterList.returned(['zipkin', 'otlp'])); + assert(spyConfigureExporter.calledTwice); + assert(spyGetOtlpProtocol.returned('grpc')); + assert(listOfExporters.length === 2); + assert(listOfExporters[0] instanceof ZipkinExporter); + assert(listOfExporters[1] instanceof OTLPGrpcTraceExporter); + 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 jaeger exporter from env', () => { + it('use the jaeger exporter', () => { + env.OTEL_TRACES_EXPORTER = 'jaeger'; + new NodeSDK(); + const listOfProcessors = spyConfigureSpanProcessors.returnValues[0]; + const listOfExporters = spyConfigureSpanProcessors.args[0][0]; + + assert(spyExporterList.returned(['jaeger'])); + assert(spyConfigureExporter.calledWith('jaeger')); + 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', () => { + env.OTEL_TRACES_EXPORTER = 'jaeger, otlp'; + env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL = 'http/json'; + new NodeSDK().start(); + const listOfProcessors = spyConfigureSpanProcessors.returnValues[0]; + const listOfExporters = spyConfigureSpanProcessors.args[0][0]; + + assert(spyExporterList.returned(['jaeger', 'otlp'])); + assert(spyConfigureExporter.calledTwice); + 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; + }); + }); }); \ No newline at end of file diff --git a/experimental/packages/opentelemetry-sdk-node/tsconfig.json b/experimental/packages/opentelemetry-sdk-node/tsconfig.json index ead9ec56c1f..a97378edcdd 100644 --- a/experimental/packages/opentelemetry-sdk-node/tsconfig.json +++ b/experimental/packages/opentelemetry-sdk-node/tsconfig.json @@ -15,6 +15,12 @@ { "path": "../../../packages/opentelemetry-core" }, + { + "path": "../../../packages/opentelemetry-exporter-jaeger" + }, + { + "path": "../../../packages/opentelemetry-exporter-zipkin" + }, { "path": "../../../packages/opentelemetry-resources" }, From 0ea7a6ae6ef7d2145436b1a43a3b533e0a4cc453 Mon Sep 17 00:00:00 2001 From: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> Date: Fri, 29 Jul 2022 10:29:18 -0500 Subject: [PATCH 10/38] feat(sdk-node): add console exporter and tests Signed-off-by: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> --- .../opentelemetry-sdk-node/src/sdk.ts | 2 + .../opentelemetry-sdk-node/test/sdk.test.ts | 52 +++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/experimental/packages/opentelemetry-sdk-node/src/sdk.ts b/experimental/packages/opentelemetry-sdk-node/src/sdk.ts index acb2322d920..d8a19291881 100644 --- a/experimental/packages/opentelemetry-sdk-node/src/sdk.ts +++ b/experimental/packages/opentelemetry-sdk-node/src/sdk.ts @@ -155,6 +155,8 @@ export class NodeSDK { return new ZipkinExporter(); case 'jaeger': return new JaegerExporter(); + case 'console': + return new ConsoleSpanExporter(); default: diag.warn(`Unrecognized OTEL_TRACES_EXPORTER value: ${name}.`); return null; diff --git a/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts b/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts index 5749ced08e8..2c093d36ba1 100644 --- a/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts +++ b/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts @@ -523,4 +523,56 @@ describe.only('setup exporter from env', () => { delete env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL; }); }); + describe('setup console exporter from env', () => { + it('use the console exporter', () => { + env.OTEL_TRACES_EXPORTER = 'console'; + new NodeSDK(); + const listOfProcessors = spyConfigureSpanProcessors.returnValues[0]; + const listOfExporters = spyConfigureSpanProcessors.args[0][0]; + + assert(spyExporterList.returned(['console'])); + assert(spyConfigureExporter.calledWith('console')); + 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', () => { + env.OTEL_TRACES_EXPORTER = 'console'; + env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL = 'grpc'; + new NodeSDK(); + const listOfProcessors = spyConfigureSpanProcessors.returnValues[0]; + const listOfExporters = spyConfigureSpanProcessors.args[0][0]; + + assert(spyExporterList.returned(['console'])); + assert(spyConfigureExporter.calledWith('console')); + 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', () => { + env.OTEL_TRACES_EXPORTER = 'console, otlp'; + env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL = 'grpc'; + new NodeSDK(); + const listOfProcessors = spyConfigureSpanProcessors.returnValues[0]; + const listOfExporters = spyConfigureSpanProcessors.args[0][0]; + + assert(spyExporterList.returned(['console', 'otlp'])); + assert(spyConfigureExporter.calledTwice); + 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; + }); + }); }); \ No newline at end of file From 51c3203477546c924d8b6a6f1f102074f4ae24d1 Mon Sep 17 00:00:00 2001 From: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> Date: Fri, 29 Jul 2022 10:31:01 -0500 Subject: [PATCH 11/38] feat(sdk-node): add console exporter and tests Signed-off-by: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> --- experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts b/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts index 2c093d36ba1..574adb1ba2f 100644 --- a/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts +++ b/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts @@ -341,7 +341,7 @@ describe('Node SDK', () => { }); }); -describe.only('setup exporter from env', () => { +describe('setup exporter from env', () => { let spyExporterList: Sinon.SinonSpy; let spyConfigureExporter: Sinon.SinonSpy; let spyConfigureSpanProcessors: Sinon.SinonSpy; From 4c50c25d5a5d7d63d6dccd2ae3ba36ea7065e416 Mon Sep 17 00:00:00 2001 From: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> Date: Fri, 29 Jul 2022 11:45:21 -0500 Subject: [PATCH 12/38] feat(sdk-node): handle invalid exporters case Signed-off-by: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> --- .../opentelemetry-sdk-node/src/sdk.ts | 29 ++++++++++------- .../opentelemetry-sdk-node/test/sdk.test.ts | 32 ++++++++++++++++++- 2 files changed, 49 insertions(+), 12 deletions(-) diff --git a/experimental/packages/opentelemetry-sdk-node/src/sdk.ts b/experimental/packages/opentelemetry-sdk-node/src/sdk.ts index d8a19291881..04038abd819 100644 --- a/experimental/packages/opentelemetry-sdk-node/src/sdk.ts +++ b/experimental/packages/opentelemetry-sdk-node/src/sdk.ts @@ -38,11 +38,11 @@ import { import { NodeTracerConfig, NodeTracerProvider } from '@opentelemetry/sdk-trace-node'; import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'; import { NodeSDKConfiguration } from './types'; -import { getEnv } from '@opentelemetry/core'; +import { getEnv, getEnvWithoutDefaults } from '@opentelemetry/core'; 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 { ZipkinExporter } from '@opentelemetry/exporter-zipkin'; import { JaegerExporter } from '@opentelemetry/exporter-jaeger'; /** This class represents everything needed to register a fully configured OpenTelemetry Node.js SDK */ @@ -120,7 +120,7 @@ export class NodeSDK { traceExportersList = ['otlp']; } - let configuredExporters: SpanExporter[] = []; + const configuredExporters: SpanExporter[] = []; traceExportersList.forEach(exporterName => { const exporter = this.configureExporter(exporterName); @@ -129,13 +129,16 @@ export class NodeSDK { } }); - this._spanProcessors = this.configureSpanProcessors(configuredExporters); + if (configuredExporters.length > 0) { + this._spanProcessors = this.configureSpanProcessors(configuredExporters); + } else { + diag.warn('Unable to set up trace exporter(s) due to invalid exporter and/or protocol values.'); + } } } // visible for testing public retrieveListOfTraceExporters(): string[] { - // should we setup the default exporter when user doesn't provide specifc values const traceList = getEnv().OTEL_TRACES_EXPORTER.split(','); const uniqueTraceExporters = Array.from(new Set(traceList)); @@ -179,18 +182,22 @@ export class NodeSDK { } } - public getOtlpProtocol(dataType: string): string { + public getOtlpProtocol(dataType: string): string | null { + const parsedEnvValues = getEnvWithoutDefaults(); + switch (dataType) { case 'traces': - return getEnv().OTEL_EXPORTER_OTLP_TRACES_PROTOCOL || getEnv().OTEL_EXPORTER_OTLP_PROTOCOL; - case 'metrics': - return getEnv().OTEL_EXPORTER_OTLP_METRICS_PROTOCOL || getEnv().OTEL_EXPORTER_OTLP_PROTOCOL; + return parsedEnvValues.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL ?? + parsedEnvValues.OTEL_EXPORTER_OTLP_PROTOCOL ?? + getEnv().OTEL_EXPORTER_OTLP_TRACES_PROTOCOL ?? + getEnv().OTEL_EXPORTER_OTLP_PROTOCOL; default: - return getEnv().OTEL_EXPORTER_OTLP_PROTOCOL; + diag.warn(`Data type not recognized: ${dataType}`); + return null; } } - // visible for testing + // visible for testing public configureSpanProcessors(exporters: SpanExporter[]): (BatchSpanProcessor | SimpleSpanProcessor)[] { return exporters.map(exporter => { if (exporter instanceof ConsoleSpanExporter) { diff --git a/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts b/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts index 574adb1ba2f..d30cf55501e 100644 --- a/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts +++ b/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts @@ -452,6 +452,36 @@ describe('setup exporter from env', () => { ); delete env.OTEL_TRACES_EXPORTER; }); + it('do not set up span processor when there are no valid exporters', () => { + env.OTEL_TRACES_EXPORTER = 'otlp'; + env.OTEL_EXPORTER_OTLP_PROTOCOL = 'invalid'; + new NodeSDK(); + + assert(spyConfigureSpanProcessors.notCalled); + 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; + delete env.OTEL_EXPORTER_OTLP_PROTOCOL; + }); + it('should ignore invalid exporters when remaining exporters are valid.', () => { + env.OTEL_TRACES_EXPORTER = 'otlp, zipkin'; + env.OTEL_EXPORTER_OTLP_PROTOCOL = 'invalid'; + new NodeSDK(); + const listOfProcessors = spyConfigureSpanProcessors.returnValues[0]; + const listOfExporters = spyConfigureSpanProcessors.args[0][0]; + + assert.strictEqual( + stubLoggerError.args[0][0], 'Unsupported OTLP traces protocol: invalid.' + ); + assert(spyConfigureSpanProcessors.called); + assert(listOfExporters.length === 1); + assert(listOfExporters[0] instanceof ZipkinExporter); + assert(listOfProcessors.length === 1); + assert(listOfProcessors[0] instanceof BatchSpanProcessor); + delete env.OTEL_TRACES_EXPORTER; + delete env.OTEL_EXPORTER_OTLP_PROTOCOL; + }); }); describe('setup zipkin exporter from env', () => { it('use the zipkin exporter', () => { @@ -575,4 +605,4 @@ describe('setup exporter from env', () => { delete env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL; }); }); -}); \ No newline at end of file +}); From 663483c55bfd20f3748722cdde505dce885c275e Mon Sep 17 00:00:00 2001 From: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> Date: Tue, 2 Aug 2022 18:06:34 -0500 Subject: [PATCH 13/38] feat(sdk-node): fix tests failing due to newly added feature Signed-off-by: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> --- .../packages/opentelemetry-sdk-node/test/sdk.test.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts b/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts index d30cf55501e..24bde8f556f 100644 --- a/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts +++ b/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts @@ -72,6 +72,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, }); @@ -83,6 +86,7 @@ describe('Node SDK', () => { assert.strictEqual((trace.getTracerProvider() as ProxyTracerProvider).getDelegate(), delegate, 'tracer provider should not have changed'); assert.ok(metrics.getMeterProvider() instanceof NoopMeterProvider); + delete env.OTEL_TRACES_EXPORTER; }); it('should register a tracer provider if an exporter is provided', async () => { @@ -129,6 +133,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, @@ -150,6 +157,7 @@ describe('Node SDK', () => { assert.ok(metrics.getMeterProvider() instanceof MeterProvider); await sdk.shutdown(); + delete env.OTEL_TRACES_EXPORTER; }); }); From de026d698c68e55a0247c5beef27f38fa36c2159 Mon Sep 17 00:00:00 2001 From: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> Date: Wed, 3 Aug 2022 14:10:34 -0500 Subject: [PATCH 14/38] feat(sdk-node): add more unit tests Signed-off-by: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> --- .../opentelemetry-sdk-node/src/sdk.ts | 1 - .../opentelemetry-sdk-node/test/sdk.test.ts | 22 +++++++++++++++---- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/experimental/packages/opentelemetry-sdk-node/src/sdk.ts b/experimental/packages/opentelemetry-sdk-node/src/sdk.ts index 04038abd819..c7f5089a117 100644 --- a/experimental/packages/opentelemetry-sdk-node/src/sdk.ts +++ b/experimental/packages/opentelemetry-sdk-node/src/sdk.ts @@ -93,7 +93,6 @@ export class NodeSDK { configuration.contextManager, configuration.textMapPropagator ); - // create trace exporter(s) from env } else { this.createTraceExportersFromEnv(); } diff --git a/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts b/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts index 24bde8f556f..e309304685a 100644 --- a/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts +++ b/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts @@ -318,7 +318,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(); @@ -479,9 +478,6 @@ describe('setup exporter from env', () => { const listOfProcessors = spyConfigureSpanProcessors.returnValues[0]; const listOfExporters = spyConfigureSpanProcessors.args[0][0]; - assert.strictEqual( - stubLoggerError.args[0][0], 'Unsupported OTLP traces protocol: invalid.' - ); assert(spyConfigureSpanProcessors.called); assert(listOfExporters.length === 1); assert(listOfExporters[0] instanceof ZipkinExporter); @@ -490,6 +486,24 @@ describe('setup exporter from env', () => { delete env.OTEL_TRACES_EXPORTER; delete env.OTEL_EXPORTER_OTLP_PROTOCOL; }); + it('should log warning when provided protocol name is not valid', () => { + env.OTEL_EXPORTER_OTLP_PROTOCOL = 'invalid'; + new NodeSDK(); + + assert.strictEqual( + stubLoggerError.args[0][0], 'Unsupported OTLP traces protocol: invalid.' + ); + delete env.OTEL_EXPORTER_OTLP_PROTOCOL; + }); + it('should log warning when provided exporter name is not valid', () => { + env.OTEL_TRACES_EXPORTER = 'someExporter'; + new NodeSDK(); + + assert.strictEqual( + stubLoggerError.args[0][0], 'Unrecognized OTEL_TRACES_EXPORTER value: someExporter.' + ); + delete env.OTEL_TRACES_EXPORTER; + }); }); describe('setup zipkin exporter from env', () => { it('use the zipkin exporter', () => { From d579063f1e86b9d8883a3b245f8ef1fd632a8dc6 Mon Sep 17 00:00:00 2001 From: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> Date: Wed, 3 Aug 2022 14:12:22 -0500 Subject: [PATCH 15/38] feat(sdk-node): add changelog Signed-off-by: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> --- experimental/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/experimental/CHANGELOG.md b/experimental/CHANGELOG.md index 95be6b4e3d5..e413c07375b 100644 --- a/experimental/CHANGELOG.md +++ b/experimental/CHANGELOG.md @@ -10,6 +10,8 @@ All notable changes to experimental packages in this project will be documented * feature(prometheus-serialiser): export the unit block when unit is set in metric descriptor [#3066](https://github.com/open-telemetry/opentelemetry-js/pull/3041) @weyert +* feat(sdk-node): configure trace exporter with environment variables [#3143](https://github.com/open-telemetry/opentelemetry-js/pull/3143) @svetlanabrennan + ### :bug: (Bug Fix) * fix(instrumentation-http): add `http.host` attribute before sending the request #3054 @cuichenli From 26ebf54a88f4570051a678cf3f70fc01be60cc72 Mon Sep 17 00:00:00 2001 From: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> Date: Wed, 3 Aug 2022 14:43:51 -0500 Subject: [PATCH 16/38] feat(sdk-node): add documentation to readme Signed-off-by: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> --- .../packages/opentelemetry-sdk-node/README.md | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/experimental/packages/opentelemetry-sdk-node/README.md b/experimental/packages/opentelemetry-sdk-node/README.md index a21de3c7411..1c2e86044a4 100644 --- a/experimental/packages/opentelemetry-sdk-node/README.md +++ b/experimental/packages/opentelemetry-sdk-node/README.md @@ -122,7 +122,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 @@ -132,6 +132,27 @@ 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: From 3c35019e1a4ff3fe200af6dbf74635cda4997fba Mon Sep 17 00:00:00 2001 From: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> Date: Wed, 3 Aug 2022 14:51:52 -0500 Subject: [PATCH 17/38] feat(sdk-node): add comments to public methods Signed-off-by: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> --- experimental/packages/opentelemetry-sdk-node/src/sdk.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/experimental/packages/opentelemetry-sdk-node/src/sdk.ts b/experimental/packages/opentelemetry-sdk-node/src/sdk.ts index c7f5089a117..9b4d3718ed8 100644 --- a/experimental/packages/opentelemetry-sdk-node/src/sdk.ts +++ b/experimental/packages/opentelemetry-sdk-node/src/sdk.ts @@ -108,7 +108,7 @@ export class NodeSDK { this._instrumentations = instrumentations; } - public createTraceExportersFromEnv() { + private createTraceExportersFromEnv() { let traceExportersList = this.retrieveListOfTraceExporters(); if (traceExportersList.length === 0 || traceExportersList[0] === 'none') { @@ -149,6 +149,7 @@ export class NodeSDK { .filter(s => s !== 'null' && s !== ''); } + // visible for testing public configureExporter(name: string): SpanExporter | null { switch (name) { case 'otlp': @@ -165,6 +166,7 @@ export class NodeSDK { } } + // visible for testing public configureOtlp(): SpanExporter | null { const protocol = this.getOtlpProtocol(this.DATA_TYPE_TRACES); @@ -181,6 +183,7 @@ export class NodeSDK { } } + // visible for testing public getOtlpProtocol(dataType: string): string | null { const parsedEnvValues = getEnvWithoutDefaults(); From 0b2a104b36c8675b032dd45231c1ee322de79f0c Mon Sep 17 00:00:00 2001 From: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> Date: Wed, 3 Aug 2022 16:16:06 -0500 Subject: [PATCH 18/38] feat(sdk-node): add integration tests Signed-off-by: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> --- .../opentelemetry-sdk-node/src/sdk.ts | 14 +++- .../opentelemetry-sdk-node/test/sdk.test.ts | 66 ++++++++++++++- .../test/traceHelper.ts | 80 +++++++++++++++++++ 3 files changed, 154 insertions(+), 6 deletions(-) create mode 100644 experimental/packages/opentelemetry-sdk-node/test/traceHelper.ts diff --git a/experimental/packages/opentelemetry-sdk-node/src/sdk.ts b/experimental/packages/opentelemetry-sdk-node/src/sdk.ts index 9b4d3718ed8..c38741936e8 100644 --- a/experimental/packages/opentelemetry-sdk-node/src/sdk.ts +++ b/experimental/packages/opentelemetry-sdk-node/src/sdk.ts @@ -61,6 +61,7 @@ export class NodeSDK { private _meterProvider?: MeterProvider; private _serviceName?: string; private _spanProcessors?: (BatchSpanProcessor | SimpleSpanProcessor)[]; + private _configuredExporters: SpanExporter[] = []; private DATA_TYPE_TRACES = 'traces'; /** @@ -119,17 +120,17 @@ export class NodeSDK { traceExportersList = ['otlp']; } - const configuredExporters: SpanExporter[] = []; + // const configuredExporters: SpanExporter[] = []; traceExportersList.forEach(exporterName => { const exporter = this.configureExporter(exporterName); if (exporter) { - configuredExporters.push(exporter); + this._configuredExporters.push(exporter); } }); - if (configuredExporters.length > 0) { - this._spanProcessors = this.configureSpanProcessors(configuredExporters); + if (this._configuredExporters.length > 0) { + this._spanProcessors = this.configureSpanProcessors(this._configuredExporters); } else { diag.warn('Unable to set up trace exporter(s) due to invalid exporter and/or protocol values.'); } @@ -318,4 +319,9 @@ export class NodeSDK { }) ); } + + // visible for testing + public _getSpanExporter(): SpanExporter[] { + return this._configuredExporters; + } } diff --git a/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts b/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts index e309304685a..0c7acf17ee5 100644 --- a/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts +++ b/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts @@ -27,7 +27,7 @@ import { AsyncHooksContextManager, AsyncLocalStorageContextManager, } from '@opentelemetry/context-async-hooks'; -import { CompositePropagator } from '@opentelemetry/core'; +import { CompositePropagator, ExportResultCode } from '@opentelemetry/core'; import { ConsoleMetricExporter, MeterProvider, PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics-base'; import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node'; import { @@ -36,7 +36,8 @@ import { import { ConsoleSpanExporter, SimpleSpanProcessor, - BatchSpanProcessor + BatchSpanProcessor, + ReadableSpan } from '@opentelemetry/sdk-trace-base'; import * as assert from 'assert'; import * as semver from 'semver'; @@ -49,6 +50,8 @@ import { OTLPTraceExporter as OTLPHttpTraceExporter} from '@opentelemetry/export import { OTLPTraceExporter as OTLPGrpcTraceExporter} from '@opentelemetry/exporter-trace-otlp-grpc'; import { ZipkinExporter } from '@opentelemetry/exporter-zipkin'; import { JaegerExporter } from '@opentelemetry/exporter-jaeger'; +import * as http from 'http'; +import { mockedReadableSpan } from './traceHelper'; const DefaultContextManager = semver.gte(process.version, '14.8.0') ? AsyncLocalStorageContextManager @@ -627,4 +630,63 @@ describe('setup exporter from env', () => { delete env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL; }); }); + describe('export spans using exporter set up from environment', () => { + let spans: ReadableSpan[]; + + const server = http.createServer((_, res) => { + res.statusCode = 200; + res.end(); + }); + before(done => { + server.listen(8080, done); + }); + after(done => { + server.close(done); + }); + it('should export spans successfully using default otlp exporter', done => { + env.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://localhost:8080'; + const sdk = new NodeSDK(); + sdk.start(); + spans = []; + spans.push(Object.assign({}, mockedReadableSpan)); + + const collectorExporter = sdk._getSpanExporter()[0]; + + collectorExporter.export(spans, result => { + assert.strictEqual(result.code, ExportResultCode.SUCCESS); + done(); + }); + + delete env.OTEL_EXPORTER_OTLP_ENDPOINT; + }); + it('should export spans successfully using otlp and console exporter', done => { + env.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://localhost:8080'; + env.OTEL_TRACES_EXPORTER = 'otlp, console'; + env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL = 'http/json'; + + const sdk = new NodeSDK(); + sdk.start(); + spans = []; + spans.push(Object.assign({}, mockedReadableSpan)); + + const OTLPExporter = sdk._getSpanExporter()[0]; + const consoleExporter = sdk._getSpanExporter()[1]; + const spyConsole = Sinon.spy(console, 'dir'); + + consoleExporter.export(spans, result => { + assert.strictEqual(result.code, ExportResultCode.SUCCESS); + }); + + assert(spyConsole.args.length > 0); + + OTLPExporter.export(spans, result => { + assert.strictEqual(result.code, ExportResultCode.SUCCESS); + done(); + }); + + delete env.OTEL_EXPORTER_OTLP_ENDPOINT; + delete env.OTEL_TRACES_EXPORTER; + delete env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL; + }); + }); }); diff --git a/experimental/packages/opentelemetry-sdk-node/test/traceHelper.ts b/experimental/packages/opentelemetry-sdk-node/test/traceHelper.ts new file mode 100644 index 00000000000..42ea7d7c500 --- /dev/null +++ b/experimental/packages/opentelemetry-sdk-node/test/traceHelper.ts @@ -0,0 +1,80 @@ +/* + * 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 { ReadableSpan } from '@opentelemetry/sdk-trace-base'; +import { SpanStatusCode, TraceFlags } from '@opentelemetry/api'; +import { Resource } from '@opentelemetry/resources'; + +const traceIdHex = '1f1008dc8e270e85c40a0d7c3939b278'; +const spanIdHex = '5e107261f64fa53e'; +const parentIdHex = '78a8915098864388'; + +export const mockedReadableSpan: ReadableSpan = { + name: 'documentFetch', + kind: 0, + spanContext: () => { + return { + traceId: traceIdHex, + spanId: spanIdHex, + traceFlags: TraceFlags.SAMPLED, + }; + }, + parentSpanId: parentIdHex, + startTime: [1574120165, 429803070], + endTime: [1574120165, 438688070], + ended: true, + status: { code: SpanStatusCode.OK }, + attributes: { component: 'document-load' }, + links: [ + { + context: { + traceId: traceIdHex, + spanId: parentIdHex, + traceFlags: TraceFlags.SAMPLED, + }, + attributes: { component: 'document-load' }, + }, + ], + events: [ + { name: 'fetchStart', time: [1574120165, 429803070] }, + { + name: 'domainLookupStart', + time: [1574120165, 429803070], + }, + { name: 'domainLookupEnd', time: [1574120165, 429803070] }, + { + name: 'connectStart', + time: [1574120165, 429803070], + }, + { name: 'connectEnd', time: [1574120165, 429803070] }, + { + name: 'requestStart', + time: [1574120165, 435513070], + }, + { name: 'responseStart', time: [1574120165, 436923070] }, + { + name: 'responseEnd', + time: [1574120165, 438688070], + }, + ], + duration: [0, 8885000], + resource: new Resource({ + service: 'ui', + version: 1, + cost: 112.12, + }), + instrumentationLibrary: { name: 'default', version: '0.0.1' }, +}; From a9497c5d566f5511a879668eed0ff119c00f6eac Mon Sep 17 00:00:00 2001 From: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> Date: Wed, 3 Aug 2022 16:17:56 -0500 Subject: [PATCH 19/38] feat(sdk-node): fix lint Signed-off-by: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> --- experimental/packages/opentelemetry-sdk-node/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/experimental/packages/opentelemetry-sdk-node/README.md b/experimental/packages/opentelemetry-sdk-node/README.md index 1c2e86044a4..5d215448f46 100644 --- a/experimental/packages/opentelemetry-sdk-node/README.md +++ b/experimental/packages/opentelemetry-sdk-node/README.md @@ -134,7 +134,7 @@ Configure the [service name](https://github.com/open-telemetry/opentelemetry-spe ## 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. +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 | From b718812c58979dfcd7a0b064aa35e9a436a3c006 Mon Sep 17 00:00:00 2001 From: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> Date: Wed, 3 Aug 2022 16:29:21 -0500 Subject: [PATCH 20/38] feat(sdk-node): fix lint in readme Signed-off-by: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> --- experimental/packages/opentelemetry-sdk-node/README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/experimental/packages/opentelemetry-sdk-node/README.md b/experimental/packages/opentelemetry-sdk-node/README.md index 5d215448f46..7620cc2b280 100644 --- a/experimental/packages/opentelemetry-sdk-node/README.md +++ b/experimental/packages/opentelemetry-sdk-node/README.md @@ -137,11 +137,13 @@ Configure the [service name](https://github.com/open-telemetry/opentelemetry-spe 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`. | @@ -149,9 +151,9 @@ This is an alternative to programmatically configuring an exporter or span proce | 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) +- [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 From 9f94d564b077880ab26186582fde999d21444a32 Mon Sep 17 00:00:00 2001 From: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> Date: Wed, 3 Aug 2022 16:31:38 -0500 Subject: [PATCH 21/38] feat(sdk-node): fix lint in readme Signed-off-by: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> --- experimental/packages/opentelemetry-sdk-node/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/experimental/packages/opentelemetry-sdk-node/README.md b/experimental/packages/opentelemetry-sdk-node/README.md index 7620cc2b280..5c4a6748f08 100644 --- a/experimental/packages/opentelemetry-sdk-node/README.md +++ b/experimental/packages/opentelemetry-sdk-node/README.md @@ -151,6 +151,7 @@ This is an alternative to programmatically configuring an exporter or span proce | 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) From 75c1404906c21cbbedc4ee44c16a2784114a78aa Mon Sep 17 00:00:00 2001 From: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> Date: Wed, 17 Aug 2022 15:53:10 -0500 Subject: [PATCH 22/38] feat(sdk-node): refactor to use class for tracer with env exporters Signed-off-by: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> --- .../opentelemetry-sdk-node/src/sdk.ts | 144 +++-------------- .../src/tracerProviderWithEnvExporter.ts | 124 +++++++++++++++ .../opentelemetry-sdk-node/test/sdk.test.ts | 149 ++++++++++-------- 3 files changed, 229 insertions(+), 188 deletions(-) create mode 100644 experimental/packages/opentelemetry-sdk-node/src/tracerProviderWithEnvExporter.ts diff --git a/experimental/packages/opentelemetry-sdk-node/src/sdk.ts b/experimental/packages/opentelemetry-sdk-node/src/sdk.ts index c38741936e8..cd0321b0dd3 100644 --- a/experimental/packages/opentelemetry-sdk-node/src/sdk.ts +++ b/experimental/packages/opentelemetry-sdk-node/src/sdk.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { ContextManager, TextMapPropagator, diag } from '@opentelemetry/api'; +import { ContextManager, TextMapPropagator } from '@opentelemetry/api'; import { metrics } from '@opentelemetry/api-metrics'; import { InstrumentationOption, @@ -32,18 +32,11 @@ import { BatchSpanProcessor, SpanProcessor, SpanExporter, - SimpleSpanProcessor, - ConsoleSpanExporter } from '@opentelemetry/sdk-trace-base'; import { NodeTracerConfig, NodeTracerProvider } from '@opentelemetry/sdk-trace-node'; import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'; import { NodeSDKConfiguration } from './types'; -import { getEnv, getEnvWithoutDefaults } from '@opentelemetry/core'; -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'; +import { tracerProviderWithEnvExporters } from './tracerProviderWithEnvExporter'; /** This class represents everything needed to register a fully configured OpenTelemetry Node.js SDK */ export class NodeSDK { @@ -60,9 +53,8 @@ export class NodeSDK { private _tracerProvider?: NodeTracerProvider; private _meterProvider?: MeterProvider; private _serviceName?: string; - private _spanProcessors?: (BatchSpanProcessor | SimpleSpanProcessor)[]; - private _configuredExporters: SpanExporter[] = []; - private DATA_TYPE_TRACES = 'traces'; + private _tracerProviderWithEnvExporters?: tracerProviderWithEnvExporters; + private _useEnvExporters = false; /** * Create a new NodeJS SDK instance @@ -95,7 +87,7 @@ export class NodeSDK { configuration.textMapPropagator ); } else { - this.createTraceExportersFromEnv(); + this._useEnvExporters = true; } if (configuration.metricReader) { @@ -109,108 +101,6 @@ export class NodeSDK { this._instrumentations = instrumentations; } - private createTraceExportersFromEnv() { - let traceExportersList = this.retrieveListOfTraceExporters(); - - 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']; - } - - // const configuredExporters: SpanExporter[] = []; - - traceExportersList.forEach(exporterName => { - const exporter = this.configureExporter(exporterName); - if (exporter) { - this._configuredExporters.push(exporter); - } - }); - - if (this._configuredExporters.length > 0) { - this._spanProcessors = this.configureSpanProcessors(this._configuredExporters); - } else { - diag.warn('Unable to set up trace exporter(s) due to invalid exporter and/or protocol values.'); - } - } - } - - // visible for testing - public retrieveListOfTraceExporters(): string[] { - const traceList = getEnv().OTEL_TRACES_EXPORTER.split(','); - const uniqueTraceExporters = Array.from(new Set(traceList)); - - return this.filterBlanksAndNulls(uniqueTraceExporters); - } - - private filterBlanksAndNulls(list: string[]): string[] { - return list.map(item => item.trim()) - .filter(s => s !== 'null' && s !== ''); - } - - // visible for testing - public configureExporter(name: string): SpanExporter | null { - switch (name) { - case 'otlp': - return this.configureOtlp(); - case 'zipkin': - return new ZipkinExporter(); - case 'jaeger': - return new JaegerExporter(); - case 'console': - return new ConsoleSpanExporter(); - default: - diag.warn(`Unrecognized OTEL_TRACES_EXPORTER value: ${name}.`); - return null; - } - } - - // visible for testing - public configureOtlp(): SpanExporter | null { - const protocol = this.getOtlpProtocol(this.DATA_TYPE_TRACES); - - 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}.`); - return null; - } - } - - // visible for testing - public getOtlpProtocol(dataType: string): string | null { - const parsedEnvValues = getEnvWithoutDefaults(); - - switch (dataType) { - case 'traces': - return parsedEnvValues.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL ?? - parsedEnvValues.OTEL_EXPORTER_OTLP_PROTOCOL ?? - getEnv().OTEL_EXPORTER_OTLP_TRACES_PROTOCOL ?? - getEnv().OTEL_EXPORTER_OTLP_PROTOCOL; - default: - diag.warn(`Data type not recognized: ${dataType}`); - return null; - } - } - - // visible for testing - public configureSpanProcessors(exporters: SpanExporter[]): (BatchSpanProcessor | SimpleSpanProcessor)[] { - return exporters.map(exporter => { - if (exporter instanceof ConsoleSpanExporter) { - return new SimpleSpanProcessor(exporter); - } else { - return new BatchSpanProcessor(exporter); - } - }); - } - /** Set configurations required to register a NodeTracerProvider */ public configureTracerProvider( tracerConfig: NodeTracerConfig, @@ -275,15 +165,19 @@ export class NodeSDK { contextManager: this._tracerProviderConfig.contextManager, propagator: this._tracerProviderConfig.textMapPropagator, }); - } else if (this._spanProcessors) { - const tracerProvider = new NodeTracerProvider(); - this._tracerProvider = tracerProvider; - - this._spanProcessors.forEach(processor => { - this._tracerProvider?.addSpanProcessor(processor); + } else if (this._useEnvExporters) { + this._tracerProviderWithEnvExporters = new tracerProviderWithEnvExporters({ + resource: this._resource }); + this._tracerProvider = this._tracerProviderWithEnvExporters; - tracerProvider.register(); + if (this._tracerProviderWithEnvExporters._spanProcessors) { + this._tracerProviderWithEnvExporters._spanProcessors.forEach(processor => { + this._tracerProviderWithEnvExporters?.addSpanProcessor(processor); + }); + + this._tracerProviderWithEnvExporters?.register(); + } } if (this._metricReader) { @@ -320,8 +214,8 @@ export class NodeSDK { ); } - // visible for testing - public _getSpanExporter(): SpanExporter[] { - return this._configuredExporters; + // added for testing + public _getSpanExporter(): SpanExporter[] | undefined { + return this._tracerProviderWithEnvExporters?._configuredExporters } } 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..414bcc1cc8a --- /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 } 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 { + static DATA_TYPE_TRACES = 'traces'; + public _configuredExporters: SpanExporter[] = []; + public _spanProcessors: (BatchSpanProcessor | SimpleSpanProcessor)[] | undefined; + + static configureOtlp(): SpanExporter { + const protocol = this.getOtlpProtocol(this.DATA_TYPE_TRACES); + + switch (protocol) { + case 'grpc': + return new OTLPGrpcTraceExporter; + case 'http/json': + return new OTLPHttpTraceExporter; + case 'http/protobuf': + return new OTLPProtoTraceExporter; + default: + // is this what we want the default to be? + // this is causing test on line 477 and 490 to fail because an exporter is still set up + diag.warn(`Unsupported OTLP traces protocol: ${protocol}. Using http/protobuf.`); + return new OTLPProtoTraceExporter; + } + } + + static getOtlpProtocol(dataType: string): string | null { + const parsedEnvValues = getEnvWithoutDefaults(); + + switch (dataType) { + case 'traces': + return parsedEnvValues.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL ?? + parsedEnvValues.OTEL_EXPORTER_OTLP_PROTOCOL ?? + getEnv().OTEL_EXPORTER_OTLP_TRACES_PROTOCOL ?? + getEnv().OTEL_EXPORTER_OTLP_PROTOCOL; + default: + diag.warn(`Data type not recognized: ${dataType}`); + return null; + } + } + + 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.retrieveListOfTraceExporters(); + + 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); + } else { + diag.warn('Unable to set up trace exporter(s) due to invalid exporter and/or protocol values.'); + } + } + } + + public configureSpanProcessors(exporters: SpanExporter[]): (BatchSpanProcessor | SimpleSpanProcessor)[] { + return exporters.map(exporter => { + if (exporter instanceof ConsoleSpanExporter) { + return new SimpleSpanProcessor(exporter); + } else { + return new BatchSpanProcessor(exporter); + } + }); + } + + public retrieveListOfTraceExporters(): string[] { + const traceList = getEnv().OTEL_TRACES_EXPORTER.split(','); + const uniqueTraceExporters = Array.from(new Set(traceList)); + + return this.filterBlanksAndNulls(uniqueTraceExporters); + } + + private filterBlanksAndNulls(list: string[]): string[] { + return list.map(item => item.trim()) + .filter(s => s !== 'null' && s !== ''); + } +} diff --git a/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts b/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts index 0c7acf17ee5..428d9304280 100644 --- a/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts +++ b/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts @@ -52,6 +52,7 @@ import { ZipkinExporter } from '@opentelemetry/exporter-zipkin'; import { JaegerExporter } from '@opentelemetry/exporter-jaeger'; import * as http from 'http'; import { mockedReadableSpan } from './traceHelper'; +import { tracerProviderWithEnvExporters } from '../src/tracerProviderWithEnvExporter'; const DefaultContextManager = semver.gte(process.version, '14.8.0') ? AsyncLocalStorageContextManager @@ -353,47 +354,47 @@ describe('Node SDK', () => { describe('setup exporter from env', () => { let spyExporterList: Sinon.SinonSpy; - let spyConfigureExporter: Sinon.SinonSpy; let spyConfigureSpanProcessors: Sinon.SinonSpy; let spyGetOtlpProtocol: Sinon.SinonSpy; let stubLoggerError: Sinon.SinonStub; + beforeEach(() => { - spyExporterList = Sinon.spy(NodeSDK.prototype, 'retrieveListOfTraceExporters'); - spyConfigureExporter = Sinon.spy(NodeSDK.prototype, 'configureExporter'); - spyConfigureSpanProcessors = Sinon.spy(NodeSDK.prototype, 'configureSpanProcessors'); - spyGetOtlpProtocol = Sinon.spy(NodeSDK.prototype, 'getOtlpProtocol'); + spyExporterList = Sinon.spy(tracerProviderWithEnvExporters.prototype, 'retrieveListOfTraceExporters'); + spyConfigureSpanProcessors = Sinon.spy(tracerProviderWithEnvExporters.prototype, 'configureSpanProcessors'); + spyGetOtlpProtocol = Sinon.spy(tracerProviderWithEnvExporters, 'getOtlpProtocol'); stubLoggerError = Sinon.stub(diag, 'warn'); }); afterEach(() => { spyExporterList.restore(); - spyConfigureExporter.restore(); spyConfigureSpanProcessors.restore(); spyGetOtlpProtocol.restore(); stubLoggerError.restore(); }); describe('set up otlp exporter from env', () => { - it('set up default exporter when user does not define otel trace exporter', () => { - new NodeSDK(); + it('set up default exporter when user does not define otel trace exporter', async () => { + const sdk = new NodeSDK(); + await sdk.start(); + + assert(spyConfigureSpanProcessors.calledOnce); const listOfProcessors = spyConfigureSpanProcessors.returnValues[0]; const listOfExporters = spyConfigureSpanProcessors.args[0][0]; assert(spyExporterList.returned(['otlp'])); - assert(spyConfigureExporter.calledWith('otlp')); 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 defined exporter protocol env value', () => { + it('use otlp exporter and defined exporter protocol env value', async () => { env.OTEL_TRACES_EXPORTER = 'otlp'; env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL = 'grpc'; - new NodeSDK(); + const sdk = new NodeSDK(); + await sdk.start(); const listOfProcessors = spyConfigureSpanProcessors.returnValues[0]; const listOfExporters = spyConfigureSpanProcessors.args[0][0]; assert(spyExporterList.returned(['otlp'])); - assert(spyConfigureExporter.calledWith('otlp')); assert(spyGetOtlpProtocol.returned('grpc')); assert(listOfExporters.length === 1); assert(listOfExporters[0] instanceof OTLPGrpcTraceExporter); @@ -402,50 +403,51 @@ describe('setup exporter from env', () => { 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', () => { + 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'; - new NodeSDK(); + const sdk = new NodeSDK(); + await sdk.start(); assert(spyExporterList.returned(['otlp'])); - assert(spyConfigureExporter.calledWith('otlp')); assert(spyGetOtlpProtocol.returned('http/protobuf')); 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', () => { + it('do not use any exporters when empty value is provided for exporter', async () => { env.OTEL_TRACES_EXPORTER = ''; - new NodeSDK(); + const sdk = new NodeSDK(); + await sdk.start(); assert(spyExporterList.returned([])); - assert(spyConfigureExporter.notCalled); env.OTEL_TRACES_EXPORTER = ''; }); - it('do not use any exporters when none value is only provided', () => { + it('do not use any exporters when none value is only provided', async () => { env.OTEL_TRACES_EXPORTER = 'none'; - new NodeSDK(); + const sdk = new NodeSDK(); + await sdk.start(); assert(spyExporterList.returned(['none'])); - assert(spyConfigureExporter.notCalled); delete env.OTEL_TRACES_EXPORTER; }); - it('log warning that sdk will not be initalized when exporter is set to none', () => { + it('log warning that sdk will not be initalized when exporter is set to none', async () => { env.OTEL_TRACES_EXPORTER = 'none'; - new NodeSDK(); + 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('use default exporter when none value is provided with other exports', () => { + it('use default exporter when none value is provided with other exports', async () => { env.OTEL_TRACES_EXPORTER = 'otlp,zipkin,none'; - new NodeSDK(); + const sdk = new NodeSDK(); + await sdk.start(); const listOfProcessors = spyConfigureSpanProcessors.returnValues[0]; const listOfExporters = spyConfigureSpanProcessors.args[0][0]; assert(spyExporterList.returned(['otlp', 'zipkin', 'none'])); - assert(spyConfigureExporter.calledWith('otlp')); assert(listOfExporters[0] instanceof OTLPProtoTraceExporter); assert(listOfExporters.length === 1); assert(listOfExporters[0] instanceof OTLPHttpTraceExporter === false); @@ -453,31 +455,33 @@ describe('setup exporter from env', () => { 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 ', () => { + 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 NodeSDK(); + 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('do not set up span processor when there are no valid exporters', () => { + it.skip('do not set up span processor when there are no valid exporters', async () => { env.OTEL_TRACES_EXPORTER = 'otlp'; env.OTEL_EXPORTER_OTLP_PROTOCOL = 'invalid'; - new NodeSDK(); + const sdk = new NodeSDK(); + await sdk.start(); - assert(spyConfigureSpanProcessors.notCalled); 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; delete env.OTEL_EXPORTER_OTLP_PROTOCOL; }); - it('should ignore invalid exporters when remaining exporters are valid.', () => { + it.skip('should ignore invalid exporters when remaining exporters are valid.', async () => { env.OTEL_TRACES_EXPORTER = 'otlp, zipkin'; env.OTEL_EXPORTER_OTLP_PROTOCOL = 'invalid'; - new NodeSDK(); + const sdk = new NodeSDK(); + await sdk.start(); const listOfProcessors = spyConfigureSpanProcessors.returnValues[0]; const listOfExporters = spyConfigureSpanProcessors.args[0][0]; @@ -489,18 +493,35 @@ describe('setup exporter from env', () => { delete env.OTEL_TRACES_EXPORTER; delete env.OTEL_EXPORTER_OTLP_PROTOCOL; }); - it('should log warning when provided protocol name is not valid', () => { + it('should warn that exporter 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('should log warning when provided protocol name is not valid', async () => { env.OTEL_EXPORTER_OTLP_PROTOCOL = 'invalid'; - new NodeSDK(); + const sdk = new NodeSDK(); + await sdk.start(); assert.strictEqual( - stubLoggerError.args[0][0], 'Unsupported OTLP traces protocol: invalid.' + stubLoggerError.args[0][0], 'Unsupported OTLP traces protocol: invalid. Using http/protobuf.' ); delete env.OTEL_EXPORTER_OTLP_PROTOCOL; }); - it('should log warning when provided exporter name is not valid', () => { + it('should log warning when provided exporter name is not valid', async () => { env.OTEL_TRACES_EXPORTER = 'someExporter'; - new NodeSDK(); + const sdk = new NodeSDK(); + await sdk.start(); assert.strictEqual( stubLoggerError.args[0][0], 'Unrecognized OTEL_TRACES_EXPORTER value: someExporter.' @@ -509,29 +530,29 @@ describe('setup exporter from env', () => { }); }); describe('setup zipkin exporter from env', () => { - it('use the zipkin exporter', () => { + it('use the zipkin exporter', async () => { env.OTEL_TRACES_EXPORTER = 'zipkin'; - new NodeSDK(); + const sdk = new NodeSDK(); + await sdk.start(); const listOfProcessors = spyConfigureSpanProcessors.returnValues[0]; const listOfExporters = spyConfigureSpanProcessors.args[0][0]; assert(spyExporterList.returned(['zipkin'])); - assert(spyConfigureExporter.calledWith('zipkin')); 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', () => { + it('setup zipkin exporter and otlp exporter', async () => { env.OTEL_TRACES_EXPORTER = 'zipkin, otlp'; env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL = 'grpc'; - new NodeSDK(); + const sdk = new NodeSDK(); + await sdk.start(); const listOfProcessors = spyConfigureSpanProcessors.returnValues[0]; const listOfExporters = spyConfigureSpanProcessors.args[0][0]; assert(spyExporterList.returned(['zipkin', 'otlp'])); - assert(spyConfigureExporter.calledTwice); assert(spyGetOtlpProtocol.returned('grpc')); assert(listOfExporters.length === 2); assert(listOfExporters[0] instanceof ZipkinExporter); @@ -544,29 +565,31 @@ describe('setup exporter from env', () => { }); }); describe('setup jaeger exporter from env', () => { - it('use the jaeger exporter', () => { + it('use the jaeger exporter', async () => { env.OTEL_TRACES_EXPORTER = 'jaeger'; - new NodeSDK(); + const sdk = new NodeSDK(); + await sdk.start(); const listOfProcessors = spyConfigureSpanProcessors.returnValues[0]; const listOfExporters = spyConfigureSpanProcessors.args[0][0]; assert(spyExporterList.returned(['jaeger'])); - assert(spyConfigureExporter.calledWith('jaeger')); + // assert(spyConfigureExporter.calledWith('jaeger')); 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', () => { + it('setup jaeger exporter and otlp exporter', async () => { env.OTEL_TRACES_EXPORTER = 'jaeger, otlp'; env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL = 'http/json'; - new NodeSDK().start(); + let sdk = new NodeSDK(); + await sdk.start(); const listOfProcessors = spyConfigureSpanProcessors.returnValues[0]; const listOfExporters = spyConfigureSpanProcessors.args[0][0]; assert(spyExporterList.returned(['jaeger', 'otlp'])); - assert(spyConfigureExporter.calledTwice); + // assert(spyConfigureExporter.calledTwice); assert(spyGetOtlpProtocol.returned('http/json')); assert(listOfExporters.length === 2); assert(listOfExporters[0] instanceof JaegerExporter); @@ -579,29 +602,29 @@ describe('setup exporter from env', () => { }); }); describe('setup console exporter from env', () => { - it('use the console exporter', () => { + it('use the console exporter', async () => { env.OTEL_TRACES_EXPORTER = 'console'; - new NodeSDK(); + const sdk = new NodeSDK(); + await sdk.start(); const listOfProcessors = spyConfigureSpanProcessors.returnValues[0]; const listOfExporters = spyConfigureSpanProcessors.args[0][0]; assert(spyExporterList.returned(['console'])); - assert(spyConfigureExporter.calledWith('console')); 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', () => { + it('ignores the protocol', async () => { env.OTEL_TRACES_EXPORTER = 'console'; env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL = 'grpc'; - new NodeSDK(); + const sdk = new NodeSDK(); + await sdk.start(); const listOfProcessors = spyConfigureSpanProcessors.returnValues[0]; const listOfExporters = spyConfigureSpanProcessors.args[0][0]; assert(spyExporterList.returned(['console'])); - assert(spyConfigureExporter.calledWith('console')); assert(spyGetOtlpProtocol.notCalled); assert(listOfExporters.length === 1); assert(listOfExporters[0] instanceof ConsoleSpanExporter); @@ -610,15 +633,15 @@ describe('setup exporter from env', () => { delete env.OTEL_TRACES_EXPORTER; delete env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL; }); - it('setup console exporter and otlp exporter', () => { + it('setup console exporter and otlp exporter', async () => { env.OTEL_TRACES_EXPORTER = 'console, otlp'; env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL = 'grpc'; - new NodeSDK(); + const sdk = new NodeSDK(); + await sdk.start(); const listOfProcessors = spyConfigureSpanProcessors.returnValues[0]; const listOfExporters = spyConfigureSpanProcessors.args[0][0]; assert(spyExporterList.returned(['console', 'otlp'])); - assert(spyConfigureExporter.calledTwice); assert(spyGetOtlpProtocol.returned('grpc')); assert(listOfExporters.length === 2); assert(listOfExporters[0] instanceof ConsoleSpanExporter); @@ -630,7 +653,7 @@ describe('setup exporter from env', () => { delete env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL; }); }); - describe('export spans using exporter set up from environment', () => { + describe.skip('export spans using exporter set up from environment', () => { let spans: ReadableSpan[]; const server = http.createServer((_, res) => { @@ -650,7 +673,7 @@ describe('setup exporter from env', () => { spans = []; spans.push(Object.assign({}, mockedReadableSpan)); - const collectorExporter = sdk._getSpanExporter()[0]; + const collectorExporter = sdk._getSpanExporter()![0]; collectorExporter.export(spans, result => { assert.strictEqual(result.code, ExportResultCode.SUCCESS); @@ -669,8 +692,8 @@ describe('setup exporter from env', () => { spans = []; spans.push(Object.assign({}, mockedReadableSpan)); - const OTLPExporter = sdk._getSpanExporter()[0]; - const consoleExporter = sdk._getSpanExporter()[1]; + const OTLPExporter = sdk._getSpanExporter()![0]; + const consoleExporter = sdk._getSpanExporter()![1]; const spyConsole = Sinon.spy(console, 'dir'); consoleExporter.export(spans, result => { From e7543b475377ec73594c52a613d2242f250f8311 Mon Sep 17 00:00:00 2001 From: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> Date: Mon, 22 Aug 2022 10:46:32 -0500 Subject: [PATCH 23/38] feat(sdk-node): fix lint Signed-off-by: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> --- .../packages/opentelemetry-sdk-node/src/sdk.ts | 2 +- .../src/tracerProviderWithEnvExporter.ts | 13 +++++++------ .../opentelemetry-sdk-node/test/sdk.test.ts | 2 +- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/experimental/packages/opentelemetry-sdk-node/src/sdk.ts b/experimental/packages/opentelemetry-sdk-node/src/sdk.ts index cd0321b0dd3..5c549e6c5b7 100644 --- a/experimental/packages/opentelemetry-sdk-node/src/sdk.ts +++ b/experimental/packages/opentelemetry-sdk-node/src/sdk.ts @@ -216,6 +216,6 @@ export class NodeSDK { // added for testing public _getSpanExporter(): SpanExporter[] | undefined { - return this._tracerProviderWithEnvExporters?._configuredExporters + return this._tracerProviderWithEnvExporters?._configuredExporters; } } diff --git a/experimental/packages/opentelemetry-sdk-node/src/tracerProviderWithEnvExporter.ts b/experimental/packages/opentelemetry-sdk-node/src/tracerProviderWithEnvExporter.ts index 414bcc1cc8a..0d366888b92 100644 --- a/experimental/packages/opentelemetry-sdk-node/src/tracerProviderWithEnvExporter.ts +++ b/experimental/packages/opentelemetry-sdk-node/src/tracerProviderWithEnvExporter.ts @@ -64,12 +64,13 @@ export class tracerProviderWithEnvExporters extends NodeTracerProvider { protected static override _registeredExporters = new Map< string, - () => SpanExporter>([ - ['otlp', () => this.configureOtlp()], - ['zipkin', () => new ZipkinExporter], - ['jaeger', () => new JaegerExporter], - ['console', () => new ConsoleSpanExporter] - ]); + () => SpanExporter + >([ + ['otlp', () => this.configureOtlp()], + ['zipkin', () => new ZipkinExporter], + ['jaeger', () => new JaegerExporter], + ['console', () => new ConsoleSpanExporter] + ]); public constructor(config: NodeTracerConfig = {}) { super(config); diff --git a/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts b/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts index 428d9304280..0c564f5e07a 100644 --- a/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts +++ b/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts @@ -583,7 +583,7 @@ describe('setup exporter from env', () => { it('setup jaeger exporter and otlp exporter', async () => { env.OTEL_TRACES_EXPORTER = 'jaeger, otlp'; env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL = 'http/json'; - let sdk = new NodeSDK(); + const sdk = new NodeSDK(); await sdk.start(); const listOfProcessors = spyConfigureSpanProcessors.returnValues[0]; const listOfExporters = spyConfigureSpanProcessors.args[0][0]; From d807632b7cf2ce869a05034bd7365c8cf61b8e3b Mon Sep 17 00:00:00 2001 From: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> Date: Fri, 26 Aug 2022 11:09:12 -0500 Subject: [PATCH 24/38] feat(sdk-node): refactor sdk class and tests Signed-off-by: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> --- .../opentelemetry-sdk-node/src/sdk.ts | 50 ++++----- .../src/tracerProviderWithEnvExporter.ts | 14 ++- .../opentelemetry-sdk-node/test/sdk.test.ts | 102 +----------------- .../test/traceHelper.ts | 80 -------------- 4 files changed, 37 insertions(+), 209 deletions(-) delete mode 100644 experimental/packages/opentelemetry-sdk-node/test/traceHelper.ts diff --git a/experimental/packages/opentelemetry-sdk-node/src/sdk.ts b/experimental/packages/opentelemetry-sdk-node/src/sdk.ts index 5c549e6c5b7..3fa21adcf00 100644 --- a/experimental/packages/opentelemetry-sdk-node/src/sdk.ts +++ b/experimental/packages/opentelemetry-sdk-node/src/sdk.ts @@ -31,12 +31,11 @@ import { MeterProvider, MetricReader } from '@opentelemetry/sdk-metrics-base'; import { BatchSpanProcessor, SpanProcessor, - SpanExporter, } 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'; +import { TracerProviderWithEnvExporters } from './tracerProviderWithEnvExporter'; /** This class represents everything needed to register a fully configured OpenTelemetry Node.js SDK */ export class NodeSDK { @@ -50,10 +49,9 @@ export class NodeSDK { private _metricReader?: MetricReader; private _resource: Resource; private _autoDetectResources: boolean; - private _tracerProvider?: NodeTracerProvider; + private _tracerProvider?: NodeTracerProvider | TracerProviderWithEnvExporters; private _meterProvider?: MeterProvider; private _serviceName?: string; - private _tracerProviderWithEnvExporters?: tracerProviderWithEnvExporters; private _useEnvExporters = false; /** @@ -152,31 +150,28 @@ export class NodeSDK { {[SemanticResourceAttributes.SERVICE_NAME]: this._serviceName} )); - if (this._tracerProviderConfig) { - const tracerProvider = new NodeTracerProvider({ - ...this._tracerProviderConfig.tracerConfig, + if (this._tracerProviderConfig || this._useEnvExporters) { + const Provider = + this._tracerProviderConfig ? NodeTracerProvider : TracerProviderWithEnvExporters; + + const tracerProvider = new Provider ({ + ...this._tracerProviderConfig?.tracerConfig, resource: this._resource, }); this._tracerProvider = tracerProvider; - tracerProvider.addSpanProcessor(this._tracerProviderConfig.spanProcessor); - tracerProvider.register({ - contextManager: this._tracerProviderConfig.contextManager, - propagator: this._tracerProviderConfig.textMapPropagator, - }); - } else if (this._useEnvExporters) { - this._tracerProviderWithEnvExporters = new tracerProviderWithEnvExporters({ - resource: this._resource - }); - this._tracerProvider = this._tracerProviderWithEnvExporters; + const processors = this.retrieveSpanProcessors(tracerProvider); - if (this._tracerProviderWithEnvExporters._spanProcessors) { - this._tracerProviderWithEnvExporters._spanProcessors.forEach(processor => { - this._tracerProviderWithEnvExporters?.addSpanProcessor(processor); + if (processors) { + processors.forEach((processor: SpanProcessor) => { + tracerProvider.addSpanProcessor(processor); }); - this._tracerProviderWithEnvExporters?.register(); + tracerProvider.register({ + contextManager: this._tracerProviderConfig?.contextManager, + propagator: this._tracerProviderConfig?.textMapPropagator, + }); } } @@ -214,8 +209,15 @@ export class NodeSDK { ); } - // added for testing - public _getSpanExporter(): SpanExporter[] | undefined { - return this._tracerProviderWithEnvExporters?._configuredExporters; + private retrieveSpanProcessors(provider: NodeTracerProvider): SpanProcessor[] | undefined { + if (this._tracerProviderConfig) { + return [this._tracerProviderConfig.spanProcessor]; + } + + if (provider instanceof TracerProviderWithEnvExporters) { + return provider.spanProcessors; + } + + return undefined; } } diff --git a/experimental/packages/opentelemetry-sdk-node/src/tracerProviderWithEnvExporter.ts b/experimental/packages/opentelemetry-sdk-node/src/tracerProviderWithEnvExporter.ts index 0d366888b92..270f751ea77 100644 --- a/experimental/packages/opentelemetry-sdk-node/src/tracerProviderWithEnvExporter.ts +++ b/experimental/packages/opentelemetry-sdk-node/src/tracerProviderWithEnvExporter.ts @@ -24,10 +24,10 @@ import { OTLPTraceExporter as OTLPGrpcTraceExporter} from '@opentelemetry/export import { ZipkinExporter } from '@opentelemetry/exporter-zipkin'; import { JaegerExporter } from '@opentelemetry/exporter-jaeger'; -export class tracerProviderWithEnvExporters extends NodeTracerProvider { +export class TracerProviderWithEnvExporters extends NodeTracerProvider { static DATA_TYPE_TRACES = 'traces'; - public _configuredExporters: SpanExporter[] = []; - public _spanProcessors: (BatchSpanProcessor | SimpleSpanProcessor)[] | undefined; + public configuredExporters: SpanExporter[] = []; + public spanProcessors: (BatchSpanProcessor | SimpleSpanProcessor)[] | undefined; static configureOtlp(): SpanExporter { const protocol = this.getOtlpProtocol(this.DATA_TYPE_TRACES); @@ -40,8 +40,6 @@ export class tracerProviderWithEnvExporters extends NodeTracerProvider { case 'http/protobuf': return new OTLPProtoTraceExporter; default: - // is this what we want the default to be? - // this is causing test on line 477 and 490 to fail because an exporter is still set up diag.warn(`Unsupported OTLP traces protocol: ${protocol}. Using http/protobuf.`); return new OTLPProtoTraceExporter; } @@ -87,14 +85,14 @@ export class tracerProviderWithEnvExporters extends NodeTracerProvider { traceExportersList.forEach(exporterName => { const exporter = this._getSpanExporter(exporterName); if (exporter) { - this._configuredExporters.push(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); + if (this.configuredExporters.length > 0) { + this.spanProcessors = this.configureSpanProcessors(this.configuredExporters); } else { diag.warn('Unable to set up trace exporter(s) due to invalid exporter and/or protocol values.'); } diff --git a/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts b/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts index 0c564f5e07a..f030f7237c6 100644 --- a/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts +++ b/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts @@ -27,7 +27,7 @@ import { AsyncHooksContextManager, AsyncLocalStorageContextManager, } from '@opentelemetry/context-async-hooks'; -import { CompositePropagator, ExportResultCode } from '@opentelemetry/core'; +import { CompositePropagator } from '@opentelemetry/core'; import { ConsoleMetricExporter, MeterProvider, PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics-base'; import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node'; import { @@ -37,7 +37,6 @@ import { ConsoleSpanExporter, SimpleSpanProcessor, BatchSpanProcessor, - ReadableSpan } from '@opentelemetry/sdk-trace-base'; import * as assert from 'assert'; import * as semver from 'semver'; @@ -50,9 +49,7 @@ import { OTLPTraceExporter as OTLPHttpTraceExporter} from '@opentelemetry/export import { OTLPTraceExporter as OTLPGrpcTraceExporter} from '@opentelemetry/exporter-trace-otlp-grpc'; import { ZipkinExporter } from '@opentelemetry/exporter-zipkin'; import { JaegerExporter } from '@opentelemetry/exporter-jaeger'; -import * as http from 'http'; -import { mockedReadableSpan } from './traceHelper'; -import { tracerProviderWithEnvExporters } from '../src/tracerProviderWithEnvExporter'; +import { TracerProviderWithEnvExporters } from '../src/tracerProviderWithEnvExporter'; const DefaultContextManager = semver.gte(process.version, '14.8.0') ? AsyncLocalStorageContextManager @@ -359,9 +356,9 @@ describe('setup exporter from env', () => { let stubLoggerError: Sinon.SinonStub; beforeEach(() => { - spyExporterList = Sinon.spy(tracerProviderWithEnvExporters.prototype, 'retrieveListOfTraceExporters'); - spyConfigureSpanProcessors = Sinon.spy(tracerProviderWithEnvExporters.prototype, 'configureSpanProcessors'); - spyGetOtlpProtocol = Sinon.spy(tracerProviderWithEnvExporters, 'getOtlpProtocol'); + spyExporterList = Sinon.spy(TracerProviderWithEnvExporters.prototype, 'retrieveListOfTraceExporters'); + spyConfigureSpanProcessors = Sinon.spy(TracerProviderWithEnvExporters.prototype, 'configureSpanProcessors'); + spyGetOtlpProtocol = Sinon.spy(TracerProviderWithEnvExporters, 'getOtlpProtocol'); stubLoggerError = Sinon.stub(diag, 'warn'); }); afterEach(() => { @@ -465,34 +462,6 @@ describe('setup exporter from env', () => { ); delete env.OTEL_TRACES_EXPORTER; }); - it.skip('do not set up span processor when there are no valid exporters', async () => { - env.OTEL_TRACES_EXPORTER = 'otlp'; - env.OTEL_EXPORTER_OTLP_PROTOCOL = 'invalid'; - const sdk = new NodeSDK(); - await sdk.start(); - - 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; - delete env.OTEL_EXPORTER_OTLP_PROTOCOL; - }); - it.skip('should ignore invalid exporters when remaining exporters are valid.', async () => { - env.OTEL_TRACES_EXPORTER = 'otlp, zipkin'; - env.OTEL_EXPORTER_OTLP_PROTOCOL = 'invalid'; - const sdk = new NodeSDK(); - await sdk.start(); - const listOfProcessors = spyConfigureSpanProcessors.returnValues[0]; - const listOfExporters = spyConfigureSpanProcessors.args[0][0]; - - assert(spyConfigureSpanProcessors.called); - assert(listOfExporters.length === 1); - assert(listOfExporters[0] instanceof ZipkinExporter); - assert(listOfProcessors.length === 1); - assert(listOfProcessors[0] instanceof BatchSpanProcessor); - delete env.OTEL_TRACES_EXPORTER; - delete env.OTEL_EXPORTER_OTLP_PROTOCOL; - }); it('should warn that exporter is unrecognized and not able to be set up', async () => { env.OTEL_TRACES_EXPORTER = 'invalid'; const sdk = new NodeSDK(); @@ -573,7 +542,6 @@ describe('setup exporter from env', () => { const listOfExporters = spyConfigureSpanProcessors.args[0][0]; assert(spyExporterList.returned(['jaeger'])); - // assert(spyConfigureExporter.calledWith('jaeger')); assert(listOfExporters.length === 1); assert(listOfExporters[0] instanceof JaegerExporter); assert(listOfProcessors.length === 1); @@ -589,7 +557,6 @@ describe('setup exporter from env', () => { const listOfExporters = spyConfigureSpanProcessors.args[0][0]; assert(spyExporterList.returned(['jaeger', 'otlp'])); - // assert(spyConfigureExporter.calledTwice); assert(spyGetOtlpProtocol.returned('http/json')); assert(listOfExporters.length === 2); assert(listOfExporters[0] instanceof JaegerExporter); @@ -653,63 +620,4 @@ describe('setup exporter from env', () => { delete env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL; }); }); - describe.skip('export spans using exporter set up from environment', () => { - let spans: ReadableSpan[]; - - const server = http.createServer((_, res) => { - res.statusCode = 200; - res.end(); - }); - before(done => { - server.listen(8080, done); - }); - after(done => { - server.close(done); - }); - it('should export spans successfully using default otlp exporter', done => { - env.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://localhost:8080'; - const sdk = new NodeSDK(); - sdk.start(); - spans = []; - spans.push(Object.assign({}, mockedReadableSpan)); - - const collectorExporter = sdk._getSpanExporter()![0]; - - collectorExporter.export(spans, result => { - assert.strictEqual(result.code, ExportResultCode.SUCCESS); - done(); - }); - - delete env.OTEL_EXPORTER_OTLP_ENDPOINT; - }); - it('should export spans successfully using otlp and console exporter', done => { - env.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://localhost:8080'; - env.OTEL_TRACES_EXPORTER = 'otlp, console'; - env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL = 'http/json'; - - const sdk = new NodeSDK(); - sdk.start(); - spans = []; - spans.push(Object.assign({}, mockedReadableSpan)); - - const OTLPExporter = sdk._getSpanExporter()![0]; - const consoleExporter = sdk._getSpanExporter()![1]; - const spyConsole = Sinon.spy(console, 'dir'); - - consoleExporter.export(spans, result => { - assert.strictEqual(result.code, ExportResultCode.SUCCESS); - }); - - assert(spyConsole.args.length > 0); - - OTLPExporter.export(spans, result => { - assert.strictEqual(result.code, ExportResultCode.SUCCESS); - done(); - }); - - delete env.OTEL_EXPORTER_OTLP_ENDPOINT; - delete env.OTEL_TRACES_EXPORTER; - delete env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL; - }); - }); }); diff --git a/experimental/packages/opentelemetry-sdk-node/test/traceHelper.ts b/experimental/packages/opentelemetry-sdk-node/test/traceHelper.ts deleted file mode 100644 index 42ea7d7c500..00000000000 --- a/experimental/packages/opentelemetry-sdk-node/test/traceHelper.ts +++ /dev/null @@ -1,80 +0,0 @@ -/* - * 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 { ReadableSpan } from '@opentelemetry/sdk-trace-base'; -import { SpanStatusCode, TraceFlags } from '@opentelemetry/api'; -import { Resource } from '@opentelemetry/resources'; - -const traceIdHex = '1f1008dc8e270e85c40a0d7c3939b278'; -const spanIdHex = '5e107261f64fa53e'; -const parentIdHex = '78a8915098864388'; - -export const mockedReadableSpan: ReadableSpan = { - name: 'documentFetch', - kind: 0, - spanContext: () => { - return { - traceId: traceIdHex, - spanId: spanIdHex, - traceFlags: TraceFlags.SAMPLED, - }; - }, - parentSpanId: parentIdHex, - startTime: [1574120165, 429803070], - endTime: [1574120165, 438688070], - ended: true, - status: { code: SpanStatusCode.OK }, - attributes: { component: 'document-load' }, - links: [ - { - context: { - traceId: traceIdHex, - spanId: parentIdHex, - traceFlags: TraceFlags.SAMPLED, - }, - attributes: { component: 'document-load' }, - }, - ], - events: [ - { name: 'fetchStart', time: [1574120165, 429803070] }, - { - name: 'domainLookupStart', - time: [1574120165, 429803070], - }, - { name: 'domainLookupEnd', time: [1574120165, 429803070] }, - { - name: 'connectStart', - time: [1574120165, 429803070], - }, - { name: 'connectEnd', time: [1574120165, 429803070] }, - { - name: 'requestStart', - time: [1574120165, 435513070], - }, - { name: 'responseStart', time: [1574120165, 436923070] }, - { - name: 'responseEnd', - time: [1574120165, 438688070], - }, - ], - duration: [0, 8885000], - resource: new Resource({ - service: 'ui', - version: 1, - cost: 112.12, - }), - instrumentationLibrary: { name: 'default', version: '0.0.1' }, -}; From 405609d844d395eeedd2d42c6a2723c83b2cc0f2 Mon Sep 17 00:00:00 2001 From: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> Date: Fri, 26 Aug 2022 11:16:53 -0500 Subject: [PATCH 25/38] feat(sdk-node): fix typo Signed-off-by: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> --- experimental/packages/opentelemetry-sdk-node/src/sdk.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/experimental/packages/opentelemetry-sdk-node/src/sdk.ts b/experimental/packages/opentelemetry-sdk-node/src/sdk.ts index 3fa21adcf00..e2b4f3ec6d1 100644 --- a/experimental/packages/opentelemetry-sdk-node/src/sdk.ts +++ b/experimental/packages/opentelemetry-sdk-node/src/sdk.ts @@ -35,7 +35,7 @@ import { import { NodeTracerConfig, NodeTracerProvider } from '@opentelemetry/sdk-trace-node'; import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'; import { NodeSDKConfiguration } from './types'; -import { TracerProviderWithEnvExporters } from './tracerProviderWithEnvExporter'; +import { TracerProviderWithEnvExporters } from './TracerProviderWithEnvExporter'; /** This class represents everything needed to register a fully configured OpenTelemetry Node.js SDK */ export class NodeSDK { From d73b932ece7f2a3664c1fe6f0fb96249c37c1bfb Mon Sep 17 00:00:00 2001 From: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> Date: Fri, 26 Aug 2022 11:20:03 -0500 Subject: [PATCH 26/38] feat(sdk-node): fix typo Signed-off-by: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> --- experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts b/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts index f030f7237c6..2adad4d9bdb 100644 --- a/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts +++ b/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts @@ -49,7 +49,7 @@ import { OTLPTraceExporter as OTLPHttpTraceExporter} from '@opentelemetry/export 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'; +import { TracerProviderWithEnvExporters } from '../src/TracerProviderWithEnvExporter'; const DefaultContextManager = semver.gte(process.version, '14.8.0') ? AsyncLocalStorageContextManager From 0c634a09658a52752e64c4ac3e82d89fdaf52f7d Mon Sep 17 00:00:00 2001 From: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> Date: Fri, 26 Aug 2022 12:14:30 -0500 Subject: [PATCH 27/38] feat(sdk-node): fix metric test Signed-off-by: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> --- experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts b/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts index bfa1eb93c27..4c1fcdb1edc 100644 --- a/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts +++ b/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts @@ -176,6 +176,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, From ec26779ebc4e8c68658163f5d03d158ff00ba317 Mon Sep 17 00:00:00 2001 From: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> Date: Mon, 29 Aug 2022 17:26:54 -0500 Subject: [PATCH 28/38] feat(sdk-node): fix lint and refactor Signed-off-by: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> --- experimental/CHANGELOG.md | 1 + .../src/tracerProviderWithEnvExporter.ts | 22 +++---------- .../opentelemetry-sdk-node/test/sdk.test.ts | 32 +++++++++---------- 3 files changed, 21 insertions(+), 34 deletions(-) diff --git a/experimental/CHANGELOG.md b/experimental/CHANGELOG.md index d643f6de1e5..12532231834 100644 --- a/experimental/CHANGELOG.md +++ b/experimental/CHANGELOG.md @@ -7,6 +7,7 @@ All notable changes to experimental packages in this project will be documented ### :boom: Breaking Change ### :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) diff --git a/experimental/packages/opentelemetry-sdk-node/src/tracerProviderWithEnvExporter.ts b/experimental/packages/opentelemetry-sdk-node/src/tracerProviderWithEnvExporter.ts index 270f751ea77..4ac39265e63 100644 --- a/experimental/packages/opentelemetry-sdk-node/src/tracerProviderWithEnvExporter.ts +++ b/experimental/packages/opentelemetry-sdk-node/src/tracerProviderWithEnvExporter.ts @@ -25,12 +25,11 @@ import { ZipkinExporter } from '@opentelemetry/exporter-zipkin'; import { JaegerExporter } from '@opentelemetry/exporter-jaeger'; export class TracerProviderWithEnvExporters extends NodeTracerProvider { - static DATA_TYPE_TRACES = 'traces'; public configuredExporters: SpanExporter[] = []; public spanProcessors: (BatchSpanProcessor | SimpleSpanProcessor)[] | undefined; static configureOtlp(): SpanExporter { - const protocol = this.getOtlpProtocol(this.DATA_TYPE_TRACES); + const protocol = this.getOtlpProtocol("traces"); switch (protocol) { case 'grpc': @@ -48,16 +47,10 @@ export class TracerProviderWithEnvExporters extends NodeTracerProvider { static getOtlpProtocol(dataType: string): string | null { const parsedEnvValues = getEnvWithoutDefaults(); - switch (dataType) { - case 'traces': - return parsedEnvValues.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL ?? + return parsedEnvValues.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL ?? parsedEnvValues.OTEL_EXPORTER_OTLP_PROTOCOL ?? getEnv().OTEL_EXPORTER_OTLP_TRACES_PROTOCOL ?? getEnv().OTEL_EXPORTER_OTLP_PROTOCOL; - default: - diag.warn(`Data type not recognized: ${dataType}`); - return null; - } } protected static override _registeredExporters = new Map< @@ -72,7 +65,7 @@ export class TracerProviderWithEnvExporters extends NodeTracerProvider { public constructor(config: NodeTracerConfig = {}) { super(config); - let traceExportersList = this.retrieveListOfTraceExporters(); + 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.'); @@ -109,14 +102,7 @@ export class TracerProviderWithEnvExporters extends NodeTracerProvider { }); } - public retrieveListOfTraceExporters(): string[] { - const traceList = getEnv().OTEL_TRACES_EXPORTER.split(','); - const uniqueTraceExporters = Array.from(new Set(traceList)); - - return this.filterBlanksAndNulls(uniqueTraceExporters); - } - - private filterBlanksAndNulls(list: string[]): string[] { + public filterBlanksAndNulls(list: string[]): string[] { return list.map(item => item.trim()) .filter(s => s !== 'null' && s !== ''); } diff --git a/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts b/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts index 4c1fcdb1edc..ede531a0987 100644 --- a/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts +++ b/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts @@ -480,19 +480,19 @@ describe('Node SDK', () => { }); describe('setup exporter from env', () => { - let spyExporterList: Sinon.SinonSpy; + let spyFilteredExporterList: Sinon.SinonSpy; let spyConfigureSpanProcessors: Sinon.SinonSpy; let spyGetOtlpProtocol: Sinon.SinonSpy; let stubLoggerError: Sinon.SinonStub; beforeEach(() => { - spyExporterList = Sinon.spy(TracerProviderWithEnvExporters.prototype, 'retrieveListOfTraceExporters'); + spyFilteredExporterList = Sinon.spy(TracerProviderWithEnvExporters.prototype, 'filterBlanksAndNulls'); spyConfigureSpanProcessors = Sinon.spy(TracerProviderWithEnvExporters.prototype, 'configureSpanProcessors'); spyGetOtlpProtocol = Sinon.spy(TracerProviderWithEnvExporters, 'getOtlpProtocol'); stubLoggerError = Sinon.stub(diag, 'warn'); }); afterEach(() => { - spyExporterList.restore(); + spyFilteredExporterList.restore(); spyConfigureSpanProcessors.restore(); spyGetOtlpProtocol.restore(); stubLoggerError.restore(); @@ -506,7 +506,7 @@ describe('setup exporter from env', () => { const listOfProcessors = spyConfigureSpanProcessors.returnValues[0]; const listOfExporters = spyConfigureSpanProcessors.args[0][0]; - assert(spyExporterList.returned(['otlp'])); + assert(spyFilteredExporterList.returned(['otlp'])); assert(spyGetOtlpProtocol.returned('http/protobuf')); assert(listOfExporters.length === 1); assert(listOfExporters[0] instanceof OTLPProtoTraceExporter); @@ -521,7 +521,7 @@ describe('setup exporter from env', () => { const listOfProcessors = spyConfigureSpanProcessors.returnValues[0]; const listOfExporters = spyConfigureSpanProcessors.args[0][0]; - assert(spyExporterList.returned(['otlp'])); + assert(spyFilteredExporterList.returned(['otlp'])); assert(spyGetOtlpProtocol.returned('grpc')); assert(listOfExporters.length === 1); assert(listOfExporters[0] instanceof OTLPGrpcTraceExporter); @@ -537,7 +537,7 @@ describe('setup exporter from env', () => { const sdk = new NodeSDK(); await sdk.start(); - assert(spyExporterList.returned(['otlp'])); + assert(spyFilteredExporterList.returned(['otlp'])); assert(spyGetOtlpProtocol.returned('http/protobuf')); delete env.OTEL_TRACES_EXPORTER; delete env.OTEL_EXPORTER_OTLP_PROTOCOL; @@ -548,7 +548,7 @@ describe('setup exporter from env', () => { const sdk = new NodeSDK(); await sdk.start(); - assert(spyExporterList.returned([])); + assert(spyFilteredExporterList.returned([])); env.OTEL_TRACES_EXPORTER = ''; }); it('do not use any exporters when none value is only provided', async () => { @@ -556,7 +556,7 @@ describe('setup exporter from env', () => { const sdk = new NodeSDK(); await sdk.start(); - assert(spyExporterList.returned(['none'])); + assert(spyFilteredExporterList.returned(['none'])); delete env.OTEL_TRACES_EXPORTER; }); it('log warning that sdk will not be initalized when exporter is set to none', async () => { @@ -574,7 +574,7 @@ describe('setup exporter from env', () => { const listOfProcessors = spyConfigureSpanProcessors.returnValues[0]; const listOfExporters = spyConfigureSpanProcessors.args[0][0]; - assert(spyExporterList.returned(['otlp', 'zipkin', 'none'])); + assert(spyFilteredExporterList.returned(['otlp', 'zipkin', 'none'])); assert(listOfExporters[0] instanceof OTLPProtoTraceExporter); assert(listOfExporters.length === 1); assert(listOfExporters[0] instanceof OTLPHttpTraceExporter === false); @@ -636,7 +636,7 @@ describe('setup exporter from env', () => { const listOfProcessors = spyConfigureSpanProcessors.returnValues[0]; const listOfExporters = spyConfigureSpanProcessors.args[0][0]; - assert(spyExporterList.returned(['zipkin'])); + assert(spyFilteredExporterList.returned(['zipkin'])); assert(listOfExporters.length === 1); assert(listOfExporters[0] instanceof ZipkinExporter); assert(listOfProcessors.length === 1); @@ -651,7 +651,7 @@ describe('setup exporter from env', () => { const listOfProcessors = spyConfigureSpanProcessors.returnValues[0]; const listOfExporters = spyConfigureSpanProcessors.args[0][0]; - assert(spyExporterList.returned(['zipkin', 'otlp'])); + assert(spyFilteredExporterList.returned(['zipkin', 'otlp'])); assert(spyGetOtlpProtocol.returned('grpc')); assert(listOfExporters.length === 2); assert(listOfExporters[0] instanceof ZipkinExporter); @@ -671,7 +671,7 @@ describe('setup exporter from env', () => { const listOfProcessors = spyConfigureSpanProcessors.returnValues[0]; const listOfExporters = spyConfigureSpanProcessors.args[0][0]; - assert(spyExporterList.returned(['jaeger'])); + assert(spyFilteredExporterList.returned(['jaeger'])); assert(listOfExporters.length === 1); assert(listOfExporters[0] instanceof JaegerExporter); assert(listOfProcessors.length === 1); @@ -686,7 +686,7 @@ describe('setup exporter from env', () => { const listOfProcessors = spyConfigureSpanProcessors.returnValues[0]; const listOfExporters = spyConfigureSpanProcessors.args[0][0]; - assert(spyExporterList.returned(['jaeger', 'otlp'])); + assert(spyFilteredExporterList.returned(['jaeger', 'otlp'])); assert(spyGetOtlpProtocol.returned('http/json')); assert(listOfExporters.length === 2); assert(listOfExporters[0] instanceof JaegerExporter); @@ -706,7 +706,7 @@ describe('setup exporter from env', () => { const listOfProcessors = spyConfigureSpanProcessors.returnValues[0]; const listOfExporters = spyConfigureSpanProcessors.args[0][0]; - assert(spyExporterList.returned(['console'])); + assert(spyFilteredExporterList.returned(['console'])); assert(listOfExporters.length === 1); assert(listOfExporters[0] instanceof ConsoleSpanExporter); assert(listOfProcessors.length === 1); @@ -721,7 +721,7 @@ describe('setup exporter from env', () => { const listOfProcessors = spyConfigureSpanProcessors.returnValues[0]; const listOfExporters = spyConfigureSpanProcessors.args[0][0]; - assert(spyExporterList.returned(['console'])); + assert(spyFilteredExporterList.returned(['console'])); assert(spyGetOtlpProtocol.notCalled); assert(listOfExporters.length === 1); assert(listOfExporters[0] instanceof ConsoleSpanExporter); @@ -738,7 +738,7 @@ describe('setup exporter from env', () => { const listOfProcessors = spyConfigureSpanProcessors.returnValues[0]; const listOfExporters = spyConfigureSpanProcessors.args[0][0]; - assert(spyExporterList.returned(['console', 'otlp'])); + assert(spyFilteredExporterList.returned(['console', 'otlp'])); assert(spyGetOtlpProtocol.returned('grpc')); assert(listOfExporters.length === 2); assert(listOfExporters[0] instanceof ConsoleSpanExporter); From fe888b94188c77dece725cbc431e71201ddd31d4 Mon Sep 17 00:00:00 2001 From: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> Date: Mon, 29 Aug 2022 17:30:42 -0500 Subject: [PATCH 29/38] feat(sdk-node): fix lint Signed-off-by: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> --- .../src/tracerProviderWithEnvExporter.ts | 4 ++-- experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/experimental/packages/opentelemetry-sdk-node/src/tracerProviderWithEnvExporter.ts b/experimental/packages/opentelemetry-sdk-node/src/tracerProviderWithEnvExporter.ts index 4ac39265e63..75959da4402 100644 --- a/experimental/packages/opentelemetry-sdk-node/src/tracerProviderWithEnvExporter.ts +++ b/experimental/packages/opentelemetry-sdk-node/src/tracerProviderWithEnvExporter.ts @@ -29,7 +29,7 @@ export class TracerProviderWithEnvExporters extends NodeTracerProvider { public spanProcessors: (BatchSpanProcessor | SimpleSpanProcessor)[] | undefined; static configureOtlp(): SpanExporter { - const protocol = this.getOtlpProtocol("traces"); + const protocol = this.getOtlpProtocol(); switch (protocol) { case 'grpc': @@ -44,7 +44,7 @@ export class TracerProviderWithEnvExporters extends NodeTracerProvider { } } - static getOtlpProtocol(dataType: string): string | null { + static getOtlpProtocol(): string { const parsedEnvValues = getEnvWithoutDefaults(); return parsedEnvValues.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 ede531a0987..63e8e53bf13 100644 --- a/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts +++ b/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts @@ -178,7 +178,7 @@ 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'; + env.OTEL_TRACES_EXPORTER = 'none'; const exporter = new InMemoryMetricExporter(AggregationTemporality.CUMULATIVE); const metricReader = new PeriodicExportingMetricReader({ exporter: exporter, From c76d9d3bd0ac3faa65883fd84f455ccfac5da249 Mon Sep 17 00:00:00 2001 From: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> Date: Mon, 29 Aug 2022 17:42:05 -0500 Subject: [PATCH 30/38] Rename tracerProviderWithEnvExporter.ts to TracerProviderWithEnvExporter.ts --- ...roviderWithEnvExporter.ts => TracerProviderWithEnvExporter.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename experimental/packages/opentelemetry-sdk-node/src/{tracerProviderWithEnvExporter.ts => TracerProviderWithEnvExporter.ts} (100%) diff --git a/experimental/packages/opentelemetry-sdk-node/src/tracerProviderWithEnvExporter.ts b/experimental/packages/opentelemetry-sdk-node/src/TracerProviderWithEnvExporter.ts similarity index 100% rename from experimental/packages/opentelemetry-sdk-node/src/tracerProviderWithEnvExporter.ts rename to experimental/packages/opentelemetry-sdk-node/src/TracerProviderWithEnvExporter.ts From 0630bceb94836cccbcf7268cd494607ec1c6c0e5 Mon Sep 17 00:00:00 2001 From: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> Date: Wed, 7 Sep 2022 16:36:24 -0500 Subject: [PATCH 31/38] feat(sdk-node): move adding span processors to different class Signed-off-by: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> --- .../opentelemetry-sdk-node/package.json | 10 ++++---- .../src/TracerProviderWithEnvExporter.ts | 3 +++ .../opentelemetry-sdk-node/src/sdk.ts | 23 ++++--------------- 3 files changed, 13 insertions(+), 23 deletions(-) diff --git a/experimental/packages/opentelemetry-sdk-node/package.json b/experimental/packages/opentelemetry-sdk-node/package.json index 6227bde5343..716ef98333b 100644 --- a/experimental/packages/opentelemetry-sdk-node/package.json +++ b/experimental/packages/opentelemetry-sdk-node/package.json @@ -44,11 +44,11 @@ "access": "public" }, "dependencies": { - "@opentelemetry/exporter-jaeger": "^1.5.0", - "@opentelemetry/exporter-trace-otlp-grpc": "^0.31.0", - "@opentelemetry/exporter-trace-otlp-http": "^0.31.0", - "@opentelemetry/exporter-trace-otlp-proto": "^0.31.0", - "@opentelemetry/exporter-zipkin": "^1.5.0", + "@opentelemetry/exporter-jaeger": "1.6.0", + "@opentelemetry/exporter-trace-otlp-grpc": "0.32.0", + "@opentelemetry/exporter-trace-otlp-http": "0.32.0", + "@opentelemetry/exporter-trace-otlp-proto": "0.32.0", + "@opentelemetry/exporter-zipkin": "1.6.0", "@opentelemetry/api-metrics": "0.32.0", "@opentelemetry/core": "1.6.0", "@opentelemetry/instrumentation": "0.32.0", diff --git a/experimental/packages/opentelemetry-sdk-node/src/TracerProviderWithEnvExporter.ts b/experimental/packages/opentelemetry-sdk-node/src/TracerProviderWithEnvExporter.ts index 75959da4402..d4b43206374 100644 --- a/experimental/packages/opentelemetry-sdk-node/src/TracerProviderWithEnvExporter.ts +++ b/experimental/packages/opentelemetry-sdk-node/src/TracerProviderWithEnvExporter.ts @@ -86,6 +86,9 @@ export class TracerProviderWithEnvExporters extends NodeTracerProvider { 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.'); } diff --git a/experimental/packages/opentelemetry-sdk-node/src/sdk.ts b/experimental/packages/opentelemetry-sdk-node/src/sdk.ts index d51a7f1212c..b1fb99acc63 100644 --- a/experimental/packages/opentelemetry-sdk-node/src/sdk.ts +++ b/experimental/packages/opentelemetry-sdk-node/src/sdk.ts @@ -30,6 +30,7 @@ import { import { MeterProvider, MetricReader, View } from '@opentelemetry/sdk-metrics'; import { BatchSpanProcessor, + NoopSpanProcessor, SpanProcessor, } from '@opentelemetry/sdk-trace-base'; import { NodeTracerConfig, NodeTracerProvider } from '@opentelemetry/sdk-trace-node'; @@ -208,13 +209,11 @@ export class NodeSDK { this._tracerProvider = tracerProvider; - const processors = this.retrieveSpanProcessors(tracerProvider); - - if (processors) { - processors.forEach((processor: SpanProcessor) => { - tracerProvider.addSpanProcessor(processor); - }); + if (this._tracerProviderConfig) { + tracerProvider.addSpanProcessor(this._tracerProviderConfig.spanProcessor); + } + if (!(tracerProvider.getActiveSpanProcessor() instanceof NoopSpanProcessor)) { tracerProvider.register({ contextManager: this._tracerProviderConfig?.contextManager, propagator: this._tracerProviderConfig?.textMapPropagator, @@ -258,16 +257,4 @@ export class NodeSDK { }) ); } - - private retrieveSpanProcessors(provider: NodeTracerProvider): SpanProcessor[] | undefined { - if (this._tracerProviderConfig) { - return [this._tracerProviderConfig.spanProcessor]; - } - - if (provider instanceof TracerProviderWithEnvExporters) { - return provider.spanProcessors; - } - - return undefined; - } } From c318a879be45157361e4a6511ea4c2cfb45f54ee Mon Sep 17 00:00:00 2001 From: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> Date: Mon, 19 Sep 2022 15:39:48 -0500 Subject: [PATCH 32/38] feat(sdk-node): add more tests and reconfigure some tests Signed-off-by: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> --- .../src/TracerProviderWithEnvExporter.ts | 4 +- .../TracerProviderWithEnvExporter.test.ts | 283 +++++++++++++ .../opentelemetry-sdk-node/test/sdk.test.ts | 377 ++++++------------ 3 files changed, 415 insertions(+), 249 deletions(-) create mode 100644 experimental/packages/opentelemetry-sdk-node/test/TracerProviderWithEnvExporter.test.ts diff --git a/experimental/packages/opentelemetry-sdk-node/src/TracerProviderWithEnvExporter.ts b/experimental/packages/opentelemetry-sdk-node/src/TracerProviderWithEnvExporter.ts index d4b43206374..b866ad8ff16 100644 --- a/experimental/packages/opentelemetry-sdk-node/src/TracerProviderWithEnvExporter.ts +++ b/experimental/packages/opentelemetry-sdk-node/src/TracerProviderWithEnvExporter.ts @@ -95,7 +95,7 @@ export class TracerProviderWithEnvExporters extends NodeTracerProvider { } } - public configureSpanProcessors(exporters: SpanExporter[]): (BatchSpanProcessor | SimpleSpanProcessor)[] { + private configureSpanProcessors(exporters: SpanExporter[]): (BatchSpanProcessor | SimpleSpanProcessor)[] { return exporters.map(exporter => { if (exporter instanceof ConsoleSpanExporter) { return new SimpleSpanProcessor(exporter); @@ -105,7 +105,7 @@ export class TracerProviderWithEnvExporters extends NodeTracerProvider { }); } - public filterBlanksAndNulls(list: string[]): string[] { + private filterBlanksAndNulls(list: string[]): string[] { return list.map(item => item.trim()) .filter(s => s !== 'null' && s !== ''); } 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..2ddc4e90dc0 --- /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 63e8e53bf13..44da11a5de9 100644 --- a/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts +++ b/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts @@ -37,6 +37,7 @@ import { ConsoleSpanExporter, SimpleSpanProcessor, BatchSpanProcessor, + NoopSpanProcessor, } from '@opentelemetry/sdk-trace-base'; import * as assert from 'assert'; import * as semver from 'semver'; @@ -44,11 +45,6 @@ import * as Sinon from 'sinon'; import { NodeSDK } from '../src'; import { envDetector, processDetector } from '@opentelemetry/resources'; import { env } from 'process'; -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'; import { TracerProviderWithEnvExporters } from '../src/TracerProviderWithEnvExporter'; const DefaultContextManager = semver.gte(process.version, '14.8.0') @@ -480,274 +476,161 @@ describe('Node SDK', () => { }); describe('setup exporter from env', () => { - let spyFilteredExporterList: Sinon.SinonSpy; - let spyConfigureSpanProcessors: Sinon.SinonSpy; let spyGetOtlpProtocol: Sinon.SinonSpy; let stubLoggerError: Sinon.SinonStub; beforeEach(() => { - spyFilteredExporterList = Sinon.spy(TracerProviderWithEnvExporters.prototype, 'filterBlanksAndNulls'); - spyConfigureSpanProcessors = Sinon.spy(TracerProviderWithEnvExporters.prototype, 'configureSpanProcessors'); spyGetOtlpProtocol = Sinon.spy(TracerProviderWithEnvExporters, 'getOtlpProtocol'); stubLoggerError = Sinon.stub(diag, 'warn'); }); afterEach(() => { - spyFilteredExporterList.restore(); - spyConfigureSpanProcessors.restore(); spyGetOtlpProtocol.restore(); stubLoggerError.restore(); }); - describe('set up otlp exporter from env', () => { - it('set up default exporter when user does not define otel trace exporter', async () => { - const sdk = new NodeSDK(); - await sdk.start(); - - assert(spyConfigureSpanProcessors.calledOnce); - const listOfProcessors = spyConfigureSpanProcessors.returnValues[0]; - const listOfExporters = spyConfigureSpanProcessors.args[0][0]; + it('use default exporter - TracerProviderWithEnvExporters', async () => { + const sdk = new NodeSDK(); + await sdk.start(); + const listOfProcessors = sdk['_tracerProvider']!['_registeredSpanProcessors']!; - assert(spyFilteredExporterList.returned(['otlp'])); - 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 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 = spyConfigureSpanProcessors.returnValues[0]; - const listOfExporters = spyConfigureSpanProcessors.args[0][0]; - - assert(spyFilteredExporterList.returned(['otlp'])); - 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; + 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 }); - 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 NodeSDK(); - await sdk.start(); + await sdk.start(); + const listOfProcessors = sdk['_tracerProvider']!['_registeredSpanProcessors']!; - assert(spyFilteredExporterList.returned(['otlp'])); - assert(spyGetOtlpProtocol.returned('http/protobuf')); - delete env.OTEL_TRACES_EXPORTER; - delete env.OTEL_EXPORTER_OTLP_PROTOCOL; - delete env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL; + assert(sdk['_tracerProvider'] instanceof TracerProviderWithEnvExporters === false); + assert(listOfProcessors.length === 1); + assert(listOfProcessors[0] instanceof SimpleSpanProcessor === false); + assert(listOfProcessors[0] instanceof BatchSpanProcessor); + }); + it('ignore 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 }); - 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(); + await sdk.start(); + const listOfProcessors = sdk['_tracerProvider']!['_registeredSpanProcessors']!; - assert(spyFilteredExporterList.returned([])); - 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 NodeSDK(); - await sdk.start(); + assert(sdk['_tracerProvider'] instanceof TracerProviderWithEnvExporters === false); + assert(listOfProcessors.length === 1); + assert(listOfProcessors[0] instanceof SimpleSpanProcessor); + assert(listOfProcessors[0] instanceof BatchSpanProcessor === false); + }); + 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(); - assert(spyFilteredExporterList.returned(['none'])); - 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(); + 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(); - 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 NodeSDK(); - await sdk.start(); - const listOfProcessors = spyConfigureSpanProcessors.returnValues[0]; - const listOfExporters = spyConfigureSpanProcessors.args[0][0]; - - assert(spyFilteredExporterList.returned(['otlp', 'zipkin', 'none'])); - assert(listOfExporters[0] instanceof OTLPProtoTraceExporter); - assert(listOfExporters.length === 1); - assert(listOfExporters[0] instanceof OTLPHttpTraceExporter === 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'; - const sdk = new NodeSDK(); - await sdk.start(); + const listOfProcessors = sdk['_tracerProvider']!['_registeredSpanProcessors']!; + const activeProcessor = sdk['_tracerProvider']?.getActiveSpanProcessor(); - 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'; - const sdk = new NodeSDK(); - await sdk.start(); + 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], 'Unrecognized OTEL_TRACES_EXPORTER value: invalid.' - ); + 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(); - assert.strictEqual( - stubLoggerError.args[1][0], 'Unable to set up trace exporter(s) due to invalid exporter and/or protocol values.' - ); + const listOfProcessors = sdk['_tracerProvider']!['_registeredSpanProcessors']!; + const activeProcessor = sdk['_tracerProvider']?.getActiveSpanProcessor(); - delete env.OTEL_TRACES_EXPORTER; - }); - it('should log warning when provided protocol name is not valid', async () => { - env.OTEL_EXPORTER_OTLP_PROTOCOL = 'invalid'; - const sdk = new NodeSDK(); - await sdk.start(); + assert(listOfProcessors.length === 0); + assert(activeProcessor instanceof NoopSpanProcessor); + env.OTEL_TRACES_EXPORTER = ''; + }); - assert.strictEqual( - stubLoggerError.args[0][0], 'Unsupported OTLP traces protocol: invalid. Using http/protobuf.' - ); - delete env.OTEL_EXPORTER_OTLP_PROTOCOL; - }); - it('should log warning when provided exporter name is not valid', async () => { - env.OTEL_TRACES_EXPORTER = 'someExporter'; - const sdk = new NodeSDK(); - await sdk.start(); + 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(); - assert.strictEqual( - stubLoggerError.args[0][0], 'Unrecognized OTEL_TRACES_EXPORTER value: someExporter.' - ); - delete env.OTEL_TRACES_EXPORTER; - }); + const listOfProcessors = sdk['_tracerProvider']!['_registeredSpanProcessors']!; + assert(sdk['_tracerProvider'] instanceof TracerProviderWithEnvExporters); + assert(listOfProcessors.length === 1); + assert(listOfProcessors[0] instanceof BatchSpanProcessor); + + delete env.OTEL_TRACES_EXPORTER; }); - describe('setup zipkin exporter from env', () => { - it('use the zipkin exporter', async () => { - env.OTEL_TRACES_EXPORTER = 'zipkin'; - const sdk = new NodeSDK(); - await sdk.start(); - const listOfProcessors = spyConfigureSpanProcessors.returnValues[0]; - const listOfExporters = spyConfigureSpanProcessors.args[0][0]; - - assert(spyFilteredExporterList.returned(['zipkin'])); - 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 NodeSDK(); - await sdk.start(); - const listOfProcessors = spyConfigureSpanProcessors.returnValues[0]; - const listOfExporters = spyConfigureSpanProcessors.args[0][0]; - - assert(spyFilteredExporterList.returned(['zipkin', 'otlp'])); - assert(spyGetOtlpProtocol.returned('grpc')); - assert(listOfExporters.length === 2); - assert(listOfExporters[0] instanceof ZipkinExporter); - assert(listOfExporters[1] instanceof OTLPGrpcTraceExporter); - 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; - }); + 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; }); - describe('setup jaeger exporter from env', () => { - it('use the jaeger exporter', async () => { - env.OTEL_TRACES_EXPORTER = 'jaeger'; - const sdk = new NodeSDK(); - await sdk.start(); - const listOfProcessors = spyConfigureSpanProcessors.returnValues[0]; - const listOfExporters = spyConfigureSpanProcessors.args[0][0]; - - assert(spyFilteredExporterList.returned(['jaeger'])); - 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 NodeSDK(); - await sdk.start(); - const listOfProcessors = spyConfigureSpanProcessors.returnValues[0]; - const listOfExporters = spyConfigureSpanProcessors.args[0][0]; - - assert(spyFilteredExporterList.returned(['jaeger', 'otlp'])); - 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; - }); + 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; }); - describe('setup console exporter from env', () => { - it('use the console exporter', async () => { - env.OTEL_TRACES_EXPORTER = 'console'; - const sdk = new NodeSDK(); - await sdk.start(); - const listOfProcessors = spyConfigureSpanProcessors.returnValues[0]; - const listOfExporters = spyConfigureSpanProcessors.args[0][0]; - - assert(spyFilteredExporterList.returned(['console'])); - 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 NodeSDK(); - await sdk.start(); - const listOfProcessors = spyConfigureSpanProcessors.returnValues[0]; - const listOfExporters = spyConfigureSpanProcessors.args[0][0]; - - assert(spyFilteredExporterList.returned(['console'])); - 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 NodeSDK(); - await sdk.start(); - const listOfProcessors = spyConfigureSpanProcessors.returnValues[0]; - const listOfExporters = spyConfigureSpanProcessors.args[0][0]; - - assert(spyFilteredExporterList.returned(['console', 'otlp'])); - 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; - }); + 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; }); }); From 93f636759da28302e58f53ebecee1e3982fb9acd Mon Sep 17 00:00:00 2001 From: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> Date: Mon, 19 Sep 2022 16:02:07 -0500 Subject: [PATCH 33/38] feat(sdk-node): add missing env value deletion after test Signed-off-by: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> --- experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts b/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts index 44da11a5de9..09d18a2be39 100644 --- a/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts +++ b/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts @@ -221,6 +221,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', () => { From a42710e32237dcc599ca8fd885cb1c28a4a385fe Mon Sep 17 00:00:00 2001 From: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> Date: Mon, 19 Sep 2022 16:28:29 -0500 Subject: [PATCH 34/38] feat(sdk-node): fix lint Signed-off-by: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> --- experimental/CHANGELOG.md | 1 + experimental/packages/opentelemetry-sdk-node/src/sdk.ts | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/experimental/CHANGELOG.md b/experimental/CHANGELOG.md index 305c078c0d3..a5c0eb0ec97 100644 --- a/experimental/CHANGELOG.md +++ b/experimental/CHANGELOG.md @@ -7,6 +7,7 @@ All notable changes to experimental packages in this project will be documented ### :boom: Breaking Change ### :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) diff --git a/experimental/packages/opentelemetry-sdk-node/src/sdk.ts b/experimental/packages/opentelemetry-sdk-node/src/sdk.ts index 5aca78c92e3..140a8f40f9e 100644 --- a/experimental/packages/opentelemetry-sdk-node/src/sdk.ts +++ b/experimental/packages/opentelemetry-sdk-node/src/sdk.ts @@ -60,12 +60,12 @@ export class NodeSDK { }; private _meterProviderConfig?: MeterProviderConfig; private _instrumentations: InstrumentationOption[]; - + private _resource: Resource; private _resourceDetectors: Detector[]; private _autoDetectResources: boolean; - + private _tracerProvider?: NodeTracerProvider | TracerProviderWithEnvExporters; private _meterProvider?: MeterProvider; private _serviceName?: string; From 83febe8e073cdf1495a5b8f386ef1ca426c94cef Mon Sep 17 00:00:00 2001 From: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> Date: Fri, 23 Sep 2022 17:43:58 -0500 Subject: [PATCH 35/38] feat(sdk-node): override register method and upate logic and tests Signed-off-by: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> --- .../src/TracerProviderWithEnvExporter.ts | 20 ++++--- .../opentelemetry-sdk-node/src/sdk.ts | 36 +++++-------- .../TracerProviderWithEnvExporter.test.ts | 52 +++++++++---------- .../opentelemetry-sdk-node/test/sdk.test.ts | 20 ++++++- 4 files changed, 71 insertions(+), 57 deletions(-) diff --git a/experimental/packages/opentelemetry-sdk-node/src/TracerProviderWithEnvExporter.ts b/experimental/packages/opentelemetry-sdk-node/src/TracerProviderWithEnvExporter.ts index b866ad8ff16..f82011ba223 100644 --- a/experimental/packages/opentelemetry-sdk-node/src/TracerProviderWithEnvExporter.ts +++ b/experimental/packages/opentelemetry-sdk-node/src/TracerProviderWithEnvExporter.ts @@ -16,7 +16,7 @@ import { diag } from '@opentelemetry/api'; import { getEnv, getEnvWithoutDefaults } from '@opentelemetry/core'; -import { ConsoleSpanExporter, SpanExporter, BatchSpanProcessor, SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base'; +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'; @@ -25,8 +25,8 @@ import { ZipkinExporter } from '@opentelemetry/exporter-zipkin'; import { JaegerExporter } from '@opentelemetry/exporter-jaeger'; export class TracerProviderWithEnvExporters extends NodeTracerProvider { - public configuredExporters: SpanExporter[] = []; - public spanProcessors: (BatchSpanProcessor | SimpleSpanProcessor)[] | undefined; + private _configuredExporters: SpanExporter[] = []; + private _spanProcessors: SpanProcessor[] | undefined; static configureOtlp(): SpanExporter { const protocol = this.getOtlpProtocol(); @@ -78,15 +78,15 @@ export class TracerProviderWithEnvExporters extends NodeTracerProvider { traceExportersList.forEach(exporterName => { const exporter = this._getSpanExporter(exporterName); if (exporter) { - this.configuredExporters.push(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 => { + if (this._configuredExporters.length > 0) { + this._spanProcessors = this.configureSpanProcessors(this._configuredExporters); + this._spanProcessors.forEach(processor => { this.addSpanProcessor(processor); }); } else { @@ -95,6 +95,12 @@ export class TracerProviderWithEnvExporters extends NodeTracerProvider { } } + override register(config?: SDKRegistrationConfig) { + if (this._spanProcessors) { + super.register(config); + } + } + private configureSpanProcessors(exporters: SpanExporter[]): (BatchSpanProcessor | SimpleSpanProcessor)[] { return exporters.map(exporter => { if (exporter instanceof ConsoleSpanExporter) { diff --git a/experimental/packages/opentelemetry-sdk-node/src/sdk.ts b/experimental/packages/opentelemetry-sdk-node/src/sdk.ts index 140a8f40f9e..bb20ed94402 100644 --- a/experimental/packages/opentelemetry-sdk-node/src/sdk.ts +++ b/experimental/packages/opentelemetry-sdk-node/src/sdk.ts @@ -31,7 +31,6 @@ import { import { MeterProvider, MetricReader, View } from '@opentelemetry/sdk-metrics'; import { BatchSpanProcessor, - NoopSpanProcessor, SpanProcessor, } from '@opentelemetry/sdk-trace-base'; import { NodeTracerConfig, NodeTracerProvider } from '@opentelemetry/sdk-trace-node'; @@ -69,7 +68,6 @@ export class NodeSDK { private _tracerProvider?: NodeTracerProvider | TracerProviderWithEnvExporters; private _meterProvider?: MeterProvider; private _serviceName?: string; - private _useEnvExporters = false; /** * Create a new NodeJS SDK instance @@ -102,8 +100,6 @@ export class NodeSDK { configuration.contextManager, configuration.textMapPropagator ); - } else { - this._useEnvExporters = true; } if (configuration.metricReader || configuration.views) { @@ -198,29 +194,25 @@ export class NodeSDK { { [SemanticResourceAttributes.SERVICE_NAME]: this._serviceName } )); - if (this._tracerProviderConfig || this._useEnvExporters) { - const Provider = - this._tracerProviderConfig ? NodeTracerProvider : TracerProviderWithEnvExporters; + const Provider = + this._tracerProviderConfig ? NodeTracerProvider : TracerProviderWithEnvExporters; - const tracerProvider = new Provider ({ - ...this._tracerProviderConfig?.tracerConfig, - resource: this._resource, - }); - - this._tracerProvider = tracerProvider; + const tracerProvider = new Provider ({ + ...this._tracerProviderConfig?.tracerConfig, + resource: this._resource, + }); - if (this._tracerProviderConfig) { - tracerProvider.addSpanProcessor(this._tracerProviderConfig.spanProcessor); - } + this._tracerProvider = tracerProvider; - if (!(tracerProvider.getActiveSpanProcessor() instanceof NoopSpanProcessor)) { - tracerProvider.register({ - contextManager: this._tracerProviderConfig?.contextManager, - propagator: this._tracerProviderConfig?.textMapPropagator, - }); - } + if (this._tracerProviderConfig) { + tracerProvider.addSpanProcessor(this._tracerProviderConfig.spanProcessor); } + 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 index 2ddc4e90dc0..2ba4d0394a7 100644 --- a/experimental/packages/opentelemetry-sdk-node/test/TracerProviderWithEnvExporter.test.ts +++ b/experimental/packages/opentelemetry-sdk-node/test/TracerProviderWithEnvExporter.test.ts @@ -47,8 +47,8 @@ describe('set up trace exporter with env exporters', () => { 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; + const listOfProcessors = sdk['_spanProcessors']!; + const listOfExporters = sdk['_configuredExporters']; assert(spyGetOtlpProtocol.returned('http/protobuf')); assert(listOfExporters.length === 1); @@ -61,8 +61,8 @@ describe('set up trace exporter with env exporters', () => { env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL = 'grpc'; const sdk = new TracerProviderWithEnvExporters(); - const listOfProcessors = sdk.spanProcessors!; - const listOfExporters = sdk.configuredExporters; + const listOfProcessors = sdk['_spanProcessors']!; + const listOfExporters = sdk['_configuredExporters']; assert(spyGetOtlpProtocol.returned('grpc')); assert(listOfExporters.length === 1); @@ -79,8 +79,8 @@ describe('set up trace exporter with env exporters', () => { env.OTEL_EXPORTER_OTLP_PROTOCOL = 'grpc'; const sdk = new TracerProviderWithEnvExporters(); - const listOfProcessors = sdk.spanProcessors!; - const listOfExporters = sdk.configuredExporters; + const listOfProcessors = sdk['_spanProcessors']!; + const listOfExporters = sdk['_configuredExporters']; assert(spyGetOtlpProtocol.returned('http/protobuf')); assert(listOfExporters.length === 1); @@ -95,8 +95,8 @@ describe('set up trace exporter with env exporters', () => { 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; + const listOfProcessors = sdk['_spanProcessors']; + const listOfExporters = sdk['_configuredExporters']; assert(spyGetOtlpProtocol.notCalled); assert(listOfExporters.length === 0); @@ -106,8 +106,8 @@ describe('set up trace exporter with env exporters', () => { 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; + const listOfProcessors = sdk['_spanProcessors']; + const listOfExporters = sdk['_configuredExporters']; assert(spyGetOtlpProtocol.notCalled); assert(listOfExporters.length === 0); @@ -124,8 +124,8 @@ describe('set up trace exporter with env exporters', () => { 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; + const listOfProcessors = sdk['_spanProcessors']!; + const listOfExporters = sdk['_configuredExporters']; assert(listOfExporters[0] instanceof OTLPProtoTraceExporter); assert(listOfExporters.length === 1); @@ -172,8 +172,8 @@ describe('set up trace exporter with env exporters', () => { it('use the zipkin exporter', async () => { env.OTEL_TRACES_EXPORTER = 'zipkin'; const sdk = new TracerProviderWithEnvExporters(); - const listOfProcessors = sdk.spanProcessors!; - const listOfExporters = sdk.configuredExporters; + const listOfProcessors = sdk['_spanProcessors']!; + const listOfExporters = sdk['_configuredExporters']; assert(listOfExporters.length === 1); assert(listOfExporters[0] instanceof ZipkinExporter); @@ -186,8 +186,8 @@ describe('set up trace exporter with env exporters', () => { env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL = 'grpc'; const sdk = new TracerProviderWithEnvExporters(); - const listOfProcessors = sdk.spanProcessors!; - const listOfExporters = sdk.configuredExporters; + const listOfProcessors = sdk['_spanProcessors']!; + const listOfExporters = sdk['_configuredExporters']; assert(spyGetOtlpProtocol.returned('grpc')); assert(listOfExporters.length === 2); @@ -204,8 +204,8 @@ describe('set up trace exporter with env exporters', () => { it('use the jaeger exporter', async () => { env.OTEL_TRACES_EXPORTER = 'jaeger'; const sdk = new TracerProviderWithEnvExporters(); - const listOfProcessors = sdk.spanProcessors!; - const listOfExporters = sdk.configuredExporters; + const listOfProcessors = sdk['_spanProcessors']!; + const listOfExporters = sdk['_configuredExporters']; assert(listOfExporters.length === 1); assert(listOfExporters[0] instanceof JaegerExporter); @@ -217,8 +217,8 @@ describe('set up trace exporter with env exporters', () => { 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; + const listOfProcessors = sdk['_spanProcessors']!; + const listOfExporters = sdk['_configuredExporters']; assert(spyGetOtlpProtocol.returned('http/json')); assert(listOfExporters.length === 2); @@ -236,8 +236,8 @@ describe('set up trace exporter with env exporters', () => { it('use the console exporter', async () => { env.OTEL_TRACES_EXPORTER = 'console'; const sdk = new TracerProviderWithEnvExporters(); - const listOfProcessors = sdk.spanProcessors!; - const listOfExporters = sdk.configuredExporters; + const listOfProcessors = sdk['_spanProcessors']!; + const listOfExporters = sdk['_configuredExporters']; assert(listOfExporters.length === 1); assert(listOfExporters[0] instanceof ConsoleSpanExporter); @@ -249,8 +249,8 @@ describe('set up trace exporter with env exporters', () => { env.OTEL_TRACES_EXPORTER = 'console'; env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL = 'grpc'; const sdk = new TracerProviderWithEnvExporters(); - const listOfProcessors = sdk.spanProcessors!; - const listOfExporters = sdk.configuredExporters; + const listOfProcessors = sdk['_spanProcessors']!; + const listOfExporters = sdk['_configuredExporters']; assert(spyGetOtlpProtocol.notCalled); assert(listOfExporters.length === 1); @@ -265,8 +265,8 @@ describe('set up trace exporter with env exporters', () => { 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; + const listOfProcessors = sdk['_spanProcessors']!; + const listOfExporters = sdk['_configuredExporters']; assert(spyGetOtlpProtocol.returned('grpc')); assert(listOfExporters.length === 2); diff --git a/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts b/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts index c55d7cf016c..12a38337441 100644 --- a/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts +++ b/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts @@ -58,6 +58,7 @@ import { processDetector, Resource } from '@opentelemetry/resources'; +import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'; const DefaultContextManager = semver.gte(process.version, '14.8.0') ? AsyncLocalStorageContextManager @@ -527,7 +528,7 @@ describe('setup exporter from env', () => { spyGetOtlpProtocol.restore(); stubLoggerError.restore(); }); - it('use default exporter - TracerProviderWithEnvExporters', async () => { + 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']!; @@ -549,7 +550,7 @@ describe('setup exporter from env', () => { assert(listOfProcessors[0] instanceof SimpleSpanProcessor === false); assert(listOfProcessors[0] instanceof BatchSpanProcessor); }); - it('ignore env exporter when user provides span processor to sdk config', async () => { + 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({ @@ -563,6 +564,21 @@ describe('setup exporter from env', () => { 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'; From a5c2d939e43259b3292bdd2ae564eb8268719054 Mon Sep 17 00:00:00 2001 From: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> Date: Tue, 27 Sep 2022 10:11:30 -0500 Subject: [PATCH 36/38] feat(sdk-node): add override for addspanprocessor method Signed-off-by: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> --- .../src/TracerProviderWithEnvExporter.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/experimental/packages/opentelemetry-sdk-node/src/TracerProviderWithEnvExporter.ts b/experimental/packages/opentelemetry-sdk-node/src/TracerProviderWithEnvExporter.ts index f82011ba223..f4abb593abe 100644 --- a/experimental/packages/opentelemetry-sdk-node/src/TracerProviderWithEnvExporter.ts +++ b/experimental/packages/opentelemetry-sdk-node/src/TracerProviderWithEnvExporter.ts @@ -27,6 +27,7 @@ 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(); @@ -95,8 +96,13 @@ export class TracerProviderWithEnvExporters extends NodeTracerProvider { } } + override addSpanProcessor(spanProcessor: SpanProcessor) { + super.addSpanProcessor(spanProcessor); + this._hasSpanProcessors = true; + } + override register(config?: SDKRegistrationConfig) { - if (this._spanProcessors) { + if (this._hasSpanProcessors) { super.register(config); } } From 6bc218e0188ec922e149cefa668a30e0657b4d7d Mon Sep 17 00:00:00 2001 From: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> Date: Tue, 27 Sep 2022 12:08:14 -0500 Subject: [PATCH 37/38] feat(sdk-node): undo accidental changelog removal Signed-off-by: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> --- experimental/CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/experimental/CHANGELOG.md b/experimental/CHANGELOG.md index 0d5749266de..11a3bf8221e 100644 --- a/experimental/CHANGELOG.md +++ b/experimental/CHANGELOG.md @@ -59,6 +59,9 @@ All notable changes to experimental packages in this project will be documented ### :rocket: (Enhancement) +* Add `resourceDetectors` option to `NodeSDK` [#3210](https://github.com/open-telemetry/opentelemetry-js/issues/3210) +* feat: add Logs API @mkuba [#3117](https://github.com/open-telemetry/opentelemetry-js/pull/3117) + ### :bug: (Bug Fix) ### :books: (Refine Doc) From 7db936e1754280f2ae304a47f0862c87982af86a Mon Sep 17 00:00:00 2001 From: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> Date: Tue, 27 Sep 2022 12:11:08 -0500 Subject: [PATCH 38/38] Undo accidental empty change heading --- experimental/CHANGELOG.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/experimental/CHANGELOG.md b/experimental/CHANGELOG.md index 11a3bf8221e..04482240f8c 100644 --- a/experimental/CHANGELOG.md +++ b/experimental/CHANGELOG.md @@ -62,8 +62,6 @@ All notable changes to experimental packages in this project will be documented * Add `resourceDetectors` option to `NodeSDK` [#3210](https://github.com/open-telemetry/opentelemetry-js/issues/3210) * feat: add Logs API @mkuba [#3117](https://github.com/open-telemetry/opentelemetry-js/pull/3117) -### :bug: (Bug Fix) - ### :books: (Refine Doc) * docs(sdk-metrics): fix typos and add missing parameter docs. [#3244](https://github.com/open-telemetry/opentelemetry-js/pull/3244) @pichlermarc