diff --git a/experimental/CHANGELOG.md b/experimental/CHANGELOG.md index a638022f7f0..20722b9ad89 100644 --- a/experimental/CHANGELOG.md +++ b/experimental/CHANGELOG.md @@ -29,6 +29,9 @@ For notes on migrating to 2.x / 0.200.x see [the upgrade guide](doc/upgrade-to-2 ### :house: Internal +* chore(sdk-node): migrate to use the new sdk-trace package [#6828](https://github.com/open-telemetry/opentelemetry-js/pull/6828/) @trentm + * The `node` re-export of `@opentelemetry/sdk-trace-node` and `tracing` re-export of `@opentelemetry/sdk-trace-base` have been deprecated. (Historically the `@opentelemetry/sdk-node` package has [re-exported from a number of core packages](https://github.com/open-telemetry/opentelemetry-js/blob/3db60e7cb46608e68258c489b2f610c1e1540248/experimental/packages/opentelemetry-sdk-node/src/index.ts#L12-L19). It is now recommended that users directly import from those other packages.) + ## 0.219.0 ### :boom: Breaking Changes diff --git a/experimental/packages/configuration/src/index.ts b/experimental/packages/configuration/src/index.ts index 16b33c95211..5cce47f15af 100644 --- a/experimental/packages/configuration/src/index.ts +++ b/experimental/packages/configuration/src/index.ts @@ -14,6 +14,7 @@ export type { Sampler as SamplerConfigModel, SpanExporter as SpanExporterConfigModel, SpanProcessor as SpanProcessorConfigModel, + SpanLimits as SpanLimitsConfigModel, MetricProducer as MetricProducerConfigModel, NameStringValuePair as NameStringValuePairConfigModel, HttpTls as HttpTlsConfigModel, diff --git a/experimental/packages/opentelemetry-sdk-node/README.md b/experimental/packages/opentelemetry-sdk-node/README.md index d2f2058e8b7..867ee3e5734 100644 --- a/experimental/packages/opentelemetry-sdk-node/README.md +++ b/experimental/packages/opentelemetry-sdk-node/README.md @@ -174,11 +174,11 @@ An array of span processors to register to the tracer provider. ### traceExporter -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 programmatically, this package will auto setup the default `otlp` exporter with `http/protobuf` protocol with a `BatchSpanProcessor`. +Configure a trace exporter. If an exporter is configured, it will be used with a [BatchSpanProcessor](../../../packages/sdk-trace/src/platform/node/export/BatchSpanProcessor.ts). If an exporter OR span processor is not configured programmatically, this package will auto setup the default `otlp` exporter with `http/protobuf` protocol with a `BatchSpanProcessor`. ### spanLimits -Configure tracing parameters. These are the same trace parameters used to [configure a tracer](../../../packages/opentelemetry-sdk-trace-base/src/types.ts#L71). +Configure tracing parameters. These are the same trace parameters used to [configure a tracer](../../../packages/sdk-trace/src/types.ts#L20). ### serviceName @@ -245,7 +245,7 @@ linkes for details: - Metric reader metrics: [MetricReaderMetrics](../../../packages//sdk-metrics/src/export/MetricReaderMetrics.ts) - Logger metrics: [LoggerMetrics.ts](../sdk-logs/src/LoggerMetrics.ts) -- Span metrics: [TracerMetrics.ts](../../../packages/opentelemetry-sdk-trace-base/src/TracerMetrics.ts) +- Span metrics: [TracerMetrics.ts](../../../packages/sdk-trace/src/TracerMetrics.ts) ## Useful links diff --git a/experimental/packages/opentelemetry-sdk-node/package.json b/experimental/packages/opentelemetry-sdk-node/package.json index 39e61ead3e2..69f0bba7978 100644 --- a/experimental/packages/opentelemetry-sdk-node/package.json +++ b/experimental/packages/opentelemetry-sdk-node/package.json @@ -66,6 +66,7 @@ "@opentelemetry/resources": "2.8.0", "@opentelemetry/sdk-logs": "0.219.0", "@opentelemetry/sdk-metrics": "2.8.0", + "@opentelemetry/sdk-trace": "2.8.0", "@opentelemetry/sdk-trace-base": "2.8.0", "@opentelemetry/sdk-trace-node": "2.8.0", "@opentelemetry/semantic-conventions": "^1.29.0" diff --git a/experimental/packages/opentelemetry-sdk-node/src/create-from-config.ts b/experimental/packages/opentelemetry-sdk-node/src/create-from-config.ts new file mode 100644 index 00000000000..cb64e4d1580 --- /dev/null +++ b/experimental/packages/opentelemetry-sdk-node/src/create-from-config.ts @@ -0,0 +1,39 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * Create SDK components from parsed declarative config. + * https://opentelemetry.io/docs/specs/otel/configuration/sdk/#create + */ + +import type { + AttributeLimitsConfigModel, + SpanLimitsConfigModel, +} from '@opentelemetry/configuration'; +import type { SpanLimits } from '@opentelemetry/sdk-trace'; + +export function createSpanLimitsFromConfig( + limits?: SpanLimitsConfigModel, + attribute_limits?: AttributeLimitsConfigModel +): SpanLimits | undefined { + if (!limits && !attribute_limits) { + return undefined; + } + return { + attributeValueLengthLimit: + limits?.attribute_value_length_limit ?? + attribute_limits?.attribute_value_length_limit ?? + undefined, + attributeCountLimit: + limits?.attribute_count_limit ?? + attribute_limits?.attribute_count_limit ?? + undefined, + eventCountLimit: limits?.event_count_limit ?? undefined, + linkCountLimit: limits?.link_count_limit ?? undefined, + attributePerEventCountLimit: + limits?.event_attribute_count_limit ?? undefined, + attributePerLinkCountLimit: limits?.link_attribute_count_limit ?? undefined, + }; +} diff --git a/experimental/packages/opentelemetry-sdk-node/src/create-from-env.ts b/experimental/packages/opentelemetry-sdk-node/src/create-from-env.ts new file mode 100644 index 00000000000..7e02ae48c6e --- /dev/null +++ b/experimental/packages/opentelemetry-sdk-node/src/create-from-env.ts @@ -0,0 +1,113 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * Create SDK components from environment variable settings. + * https://opentelemetry.io/docs/specs/otel/configuration/sdk-environment-variables/ + */ + +import { diag } from '@opentelemetry/api'; +import { getNumberFromEnv, getStringFromEnv } from '@opentelemetry/core'; +import type { + Sampler, + SpanExporter, + SpanLimits, +} from '@opentelemetry/sdk-trace'; +import { BatchSpanProcessor } from '@opentelemetry/sdk-trace'; +import { + AlwaysOffSampler, + AlwaysOnSampler, + ParentBasedSampler, + TraceIdRatioBasedSampler, +} from '@opentelemetry/sdk-trace'; +import { getNonNegativeNumberFromEnv } from './utils'; + +const DEFAULT_RATIO = 1; + +export function createSamplerFromEnv(): Sampler | undefined { + const samplerName = getStringFromEnv('OTEL_TRACES_SAMPLER'); + if (samplerName === undefined) { + return undefined; + } + switch (samplerName) { + case 'always_on': + return new AlwaysOnSampler(); + case 'always_off': + return new AlwaysOffSampler(); + case 'parentbased_always_on': + return new ParentBasedSampler({ + root: new AlwaysOnSampler(), + }); + case 'parentbased_always_off': + return new ParentBasedSampler({ + root: new AlwaysOffSampler(), + }); + case 'traceidratio': + return new TraceIdRatioBasedSampler(getSamplerRatioFromEnv()); + case 'parentbased_traceidratio': + return new ParentBasedSampler({ + root: new TraceIdRatioBasedSampler(getSamplerRatioFromEnv()), + }); + default: + diag.error( + `unknown OTEL_TRACES_SAMPLER value "${samplerName}", using default` + ); + return undefined; + } +} + +function getSamplerRatioFromEnv(): number | undefined { + const ratio = getNumberFromEnv('OTEL_TRACES_SAMPLER_ARG'); + if (ratio == null) { + diag.error( + `OTEL_TRACES_SAMPLER_ARG is blank, defaulting to ${DEFAULT_RATIO}.` + ); + return DEFAULT_RATIO; + } + + if (ratio < 0 || ratio > 1) { + diag.error( + `OTEL_TRACES_SAMPLER_ARG=${ratio} was given, but it is out of range ([0..1]), defaulting to ${DEFAULT_RATIO}.` + ); + return DEFAULT_RATIO; + } + + return ratio; +} + +export function createSpanLimitsFromEnv(): SpanLimits | undefined { + return { + attributeCountLimit: + getNumberFromEnv('OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT') ?? + getNumberFromEnv('OTEL_ATTRIBUTE_COUNT_LIMIT'), + attributeValueLengthLimit: + getNumberFromEnv('OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT') ?? + getNumberFromEnv('OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT'), + eventCountLimit: getNumberFromEnv('OTEL_SPAN_EVENT_COUNT_LIMIT'), + linkCountLimit: getNumberFromEnv('OTEL_SPAN_LINK_COUNT_LIMIT'), + attributePerEventCountLimit: getNumberFromEnv( + 'OTEL_SPAN_ATTRIBUTE_PER_EVENT_COUNT_LIMIT' + ), + attributePerLinkCountLimit: getNumberFromEnv( + 'OTEL_SPAN_ATTRIBUTE_PER_LINK_COUNT_LIMIT' + ), + }; +} + +export function createBatchSpanProcessorFromEnv( + exporter: SpanExporter +): BatchSpanProcessor { + return new BatchSpanProcessor({ + exporter, + maxQueueSize: getNonNegativeNumberFromEnv('OTEL_BSP_MAX_QUEUE_SIZE'), + scheduledDelayMillis: getNonNegativeNumberFromEnv( + 'OTEL_BSP_SCHEDULE_DELAY' + ), + exportTimeoutMillis: getNonNegativeNumberFromEnv('OTEL_BSP_EXPORT_TIMEOUT'), + maxExportBatchSize: getNonNegativeNumberFromEnv( + 'OTEL_BSP_MAX_EXPORT_BATCH_SIZE' + ), + }); +} diff --git a/experimental/packages/opentelemetry-sdk-node/src/index.ts b/experimental/packages/opentelemetry-sdk-node/src/index.ts index 6cd2759f05d..adcc4986844 100644 --- a/experimental/packages/opentelemetry-sdk-node/src/index.ts +++ b/experimental/packages/opentelemetry-sdk-node/src/index.ts @@ -14,8 +14,15 @@ export * as contextBase from '@opentelemetry/api'; export * as core from '@opentelemetry/core'; export * as logs from '@opentelemetry/sdk-logs'; export * as metrics from '@opentelemetry/sdk-metrics'; -export * as node from '@opentelemetry/sdk-trace-node'; export * as resources from '@opentelemetry/resources'; + +/** + * @deprecated Import directly from `@opentelemetry/sdk-trace` instead. + */ +export * as node from '@opentelemetry/sdk-trace-node'; +/** + * @deprecated Import directly from `@opentelemetry/sdk-trace` instead. + */ export * as tracing from '@opentelemetry/sdk-trace-base'; /* eslint-enable no-restricted-syntax */ diff --git a/experimental/packages/opentelemetry-sdk-node/src/sdk.ts b/experimental/packages/opentelemetry-sdk-node/src/sdk.ts index 80af8bb1006..b75b3bfc756 100644 --- a/experimental/packages/opentelemetry-sdk-node/src/sdk.ts +++ b/experimental/packages/opentelemetry-sdk-node/src/sdk.ts @@ -39,10 +39,7 @@ import { ConsoleMetricExporter, PeriodicExportingMetricReader, } from '@opentelemetry/sdk-metrics'; -import type { SpanProcessor } from '@opentelemetry/sdk-trace-base'; -import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-base'; -import type { NodeTracerConfig } from '@opentelemetry/sdk-trace-node'; -import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node'; +import { TracerProvider } from '@opentelemetry/sdk-trace'; import { ATTR_SERVICE_NAME } from '@opentelemetry/semantic-conventions'; import type { NodeSDKConfiguration } from './types'; import { @@ -62,11 +59,11 @@ import { getBatchLogRecordProcessorFromEnv, getLoggerProviderConfigFromEnv, } from './utils'; - -type TracerProviderConfig = { - tracerConfig: NodeTracerConfig; - spanProcessors: SpanProcessor[]; -}; +import { + createBatchSpanProcessorFromEnv, + createSamplerFromEnv, + createSpanLimitsFromEnv, +} from './create-from-env'; export type MeterProviderConfig = { /** @@ -150,7 +147,6 @@ function getMetricReadersFromEnv(): IMetricReader[] { * nodeSdk.start(); // registers all configured SDK components */ export class NodeSDK { - private _tracerProviderConfig?: TracerProviderConfig; private _loggerProviderConfig?: LoggerProviderConfig; private _meterProviderConfig?: MeterProviderConfig; private _instrumentations: Instrumentation[]; @@ -160,7 +156,7 @@ export class NodeSDK { private _autoDetectResources: boolean; - private _tracerProvider?: NodeTracerProvider; + private _tracerProvider?: TracerProvider; private _loggerProvider?: LoggerProvider; private _meterProvider?: MeterProvider; private _serviceName?: string; @@ -201,40 +197,10 @@ export class NodeSDK { this._serviceName = configuration.serviceName; - // If a tracer provider can be created from manual configuration, create it - if ( - configuration.traceExporter || - configuration.spanProcessor || - configuration.spanProcessors - ) { - const tracerProviderConfig: NodeTracerConfig = {}; - - if (configuration.sampler) { - tracerProviderConfig.sampler = configuration.sampler; - } - if (configuration.spanLimits) { - tracerProviderConfig.spanLimits = configuration.spanLimits; - } - if (configuration.idGenerator) { - tracerProviderConfig.idGenerator = configuration.idGenerator; - } - - if (configuration.spanProcessor) { - diag.warn( - "The 'spanProcessor' option is deprecated. Please use 'spanProcessors' instead." - ); - } - - const spanProcessor = - configuration.spanProcessor ?? - new BatchSpanProcessor(configuration.traceExporter!); - - const spanProcessors = configuration.spanProcessors ?? [spanProcessor]; - - this._tracerProviderConfig = { - tracerConfig: tracerProviderConfig, - spanProcessors, - }; + if (configuration.spanProcessor) { + diag.warn( + "The 'spanProcessor' option is deprecated. Please use 'spanProcessors' instead." + ); } if (configuration.logRecordProcessors) { @@ -340,16 +306,31 @@ export class NodeSDK { } } - const spanProcessors = this._tracerProviderConfig - ? this._tracerProviderConfig.spanProcessors - : getSpanProcessorsFromEnv(); + // Determine `spanProcessors` from multiple possible options. + let spanProcessors; + if (this._configuration?.spanProcessors) { + spanProcessors = this._configuration.spanProcessors; + } else if (this._configuration?.spanProcessor) { + spanProcessors = [this._configuration.spanProcessor]; + } else if (this._configuration?.traceExporter) { + spanProcessors = [ + createBatchSpanProcessorFromEnv(this._configuration.traceExporter!), + ]; + } else { + spanProcessors = getSpanProcessorsFromEnv(); + } // Only register if there is a span processor if (spanProcessors.length > 0) { - this._tracerProvider = new NodeTracerProvider({ - ...this._configuration, + this._tracerProvider = new TracerProvider({ + sampler: this._configuration?.sampler ?? createSamplerFromEnv(), + spanLimits: { + ...createSpanLimitsFromEnv(), + ...this._configuration?.spanLimits, + }, resource: this._resource, meterProvider: sdkMetricsEnabled ? this._meterProvider : undefined, + idGenerator: this._configuration?.idGenerator, spanProcessors, }); trace.setGlobalTracerProvider(this._tracerProvider); diff --git a/experimental/packages/opentelemetry-sdk-node/src/start.ts b/experimental/packages/opentelemetry-sdk-node/src/start.ts index aa98b896d2a..5631f7045a7 100644 --- a/experimental/packages/opentelemetry-sdk-node/src/start.ts +++ b/experimental/packages/opentelemetry-sdk-node/src/start.ts @@ -23,12 +23,12 @@ import { getPropagatorFromConfiguration, getResourceDetectorsFromConfiguration, getResourceFromConfiguration, - getSpanLimitsFromConfiguration, getSpanProcessorsFromConfiguration, } from './utils'; import { registerInstrumentations } from '@opentelemetry/instrumentation'; import type { SDKComponents, SDKOptions } from './types'; import { MeterProvider } from '@opentelemetry/sdk-metrics'; +import { TracerProvider } from '@opentelemetry/sdk-trace'; import { logs } from '@opentelemetry/api-logs'; import type { Resource, @@ -42,8 +42,8 @@ import { } from '@opentelemetry/resources'; import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks'; import { ATTR_SERVICE_INSTANCE_ID } from './semconv'; -import { BasicTracerProvider } from '@opentelemetry/sdk-trace-base'; import { diagLogLevelFromSeverityNumberConfig } from './diag'; +import { createSpanLimitsFromConfig } from './create-from-config'; // Exported for testing. export const NOOP_SDK = { @@ -166,18 +166,14 @@ function create( const spanProcessors = getSpanProcessorsFromConfiguration(config); if (spanProcessors) { - const spanLimits = getSpanLimitsFromConfiguration(config); // TODO (6506): support sampler configuration from config - const tracerProvider = new BasicTracerProvider({ + const tracerProvider = new TracerProvider({ resource, spanProcessors, - spanLimits, - generalLimits: { - attributeValueLengthLimit: - config.attribute_limits?.attribute_value_length_limit ?? undefined, - attributeCountLimit: - config.attribute_limits?.attribute_count_limit ?? undefined, - }, + spanLimits: createSpanLimitsFromConfig( + config.tracer_provider?.limits, + config.attribute_limits + ), // TODO (6616): support idGenerator configuration from config // TODO (6624): support for `meterProvider: components.meterProvider` }); diff --git a/experimental/packages/opentelemetry-sdk-node/src/types.ts b/experimental/packages/opentelemetry-sdk-node/src/types.ts index 5d25964764e..3cb1b5b0daa 100644 --- a/experimental/packages/opentelemetry-sdk-node/src/types.ts +++ b/experimental/packages/opentelemetry-sdk-node/src/types.ts @@ -19,8 +19,8 @@ import type { SpanLimits, SpanProcessor, IdGenerator, -} from '@opentelemetry/sdk-trace-base'; -import type { BasicTracerProvider } from '@opentelemetry/sdk-trace-base'; +} from '@opentelemetry/sdk-trace'; +import type { TracerProvider } from '@opentelemetry/sdk-trace'; export interface NodeSDKConfiguration { autoDetectResources: boolean; @@ -58,6 +58,6 @@ export interface SDKComponents { contextManager?: ContextManager; loggerProvider?: LoggerProvider; meterProvider?: MeterProvider; - tracerProvider?: BasicTracerProvider; + tracerProvider?: TracerProvider; propagator?: TextMapPropagator; } diff --git a/experimental/packages/opentelemetry-sdk-node/src/utils.ts b/experimental/packages/opentelemetry-sdk-node/src/utils.ts index c4f3e24b620..b622d88afd5 100644 --- a/experimental/packages/opentelemetry-sdk-node/src/utils.ts +++ b/experimental/packages/opentelemetry-sdk-node/src/utils.ts @@ -33,9 +33,8 @@ import { import type { Sampler, SpanExporter, - SpanLimits, SpanProcessor, -} from '@opentelemetry/sdk-trace-base'; +} from '@opentelemetry/sdk-trace'; import { AlwaysOffSampler, AlwaysOnSampler, @@ -44,7 +43,7 @@ import { ParentBasedSampler, SimpleSpanProcessor, TraceIdRatioBasedSampler, -} from '@opentelemetry/sdk-trace-base'; +} from '@opentelemetry/sdk-trace'; import { B3InjectEncoding, B3Propagator } from '@opentelemetry/propagator-b3'; import { JaegerPropagator } from '@opentelemetry/propagator-jaeger'; import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks'; @@ -111,6 +110,7 @@ import { LoggerProvider, } from '@opentelemetry/sdk-logs'; import * as fs from 'fs'; +import { createBatchSpanProcessorFromEnv } from './create-from-env'; const RESOURCE_DETECTOR_ENVIRONMENT = 'env'; const RESOURCE_DETECTOR_HOST = 'host'; @@ -258,7 +258,7 @@ export function getSpanProcessorsFromEnv(): SpanProcessor[] { if (exp instanceof ConsoleSpanExporter) { processors.push(new SimpleSpanProcessor(exp)); } else { - processors.push(new BatchSpanProcessor(exp)); + processors.push(createBatchSpanProcessorFromEnv(exp)); } } @@ -935,7 +935,8 @@ export function getSpanProcessorsFromConfiguration( const exporter = getSpanExporter(processor.batch.exporter); if (exporter) { spanProcessors.push( - new BatchSpanProcessor(exporter, { + new BatchSpanProcessor({ + exporter, maxQueueSize: processor.batch.max_queue_size ?? undefined, maxExportBatchSize: processor.batch.max_export_batch_size ?? undefined, @@ -958,30 +959,6 @@ export function getSpanProcessorsFromConfiguration( return undefined; } -export function getSpanLimitsFromConfiguration( - config: ConfigurationModel -): SpanLimits | undefined { - if (config.tracer_provider?.limits) { - const limitsConfig = config.tracer_provider.limits; - const spanLimits: SpanLimits = {}; - spanLimits.attributeCountLimit = limitsConfig.attribute_count_limit ?? 128; - spanLimits.eventCountLimit = limitsConfig.event_count_limit ?? 128; - spanLimits.linkCountLimit = limitsConfig.link_count_limit ?? 128; - spanLimits.attributePerLinkCountLimit = - limitsConfig.link_attribute_count_limit ?? 128; - spanLimits.attributePerEventCountLimit = - limitsConfig.event_attribute_count_limit ?? 128; - - if (limitsConfig.attribute_value_length_limit != null) { - spanLimits.attributeValueLengthLimit = - limitsConfig.attribute_value_length_limit; - } - - return spanLimits; - } - return undefined; -} - export function getMeterReadersFromConfiguration( config: ConfigurationModel ): IMetricReader[] | undefined { diff --git a/experimental/packages/opentelemetry-sdk-node/test/buildSamplerFromConfig.test.ts b/experimental/packages/opentelemetry-sdk-node/test/buildSamplerFromConfig.test.ts index a556b866f41..f23e2458171 100644 --- a/experimental/packages/opentelemetry-sdk-node/test/buildSamplerFromConfig.test.ts +++ b/experimental/packages/opentelemetry-sdk-node/test/buildSamplerFromConfig.test.ts @@ -9,7 +9,7 @@ import { AlwaysOnSampler, ParentBasedSampler, TraceIdRatioBasedSampler, -} from '@opentelemetry/sdk-trace-base'; +} from '@opentelemetry/sdk-trace'; import { buildSamplerFromConfig } from '../src/utils'; describe('buildSamplerFromConfig()', () => { diff --git a/experimental/packages/opentelemetry-sdk-node/test/create-from-config.test.ts b/experimental/packages/opentelemetry-sdk-node/test/create-from-config.test.ts new file mode 100644 index 00000000000..f971a0ebccf --- /dev/null +++ b/experimental/packages/opentelemetry-sdk-node/test/create-from-config.test.ts @@ -0,0 +1,104 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as assert from 'assert'; +import type { ConfigurationModel } from '@opentelemetry/configuration'; +import type { SpanLimits } from '@opentelemetry/sdk-trace'; +import { createSpanLimitsFromConfig } from '../src/create-from-config'; + +describe('create-from-config', () => { + describe('createSpanLimitsFromConfig', () => { + const corpus: { + testName: string; + config: ConfigurationModel; + spanLimits: SpanLimits | undefined; + only?: boolean; + }[] = [ + { + testName: 'empty', + config: {}, + spanLimits: undefined, + }, + { + testName: 'just general limits', + config: { + attribute_limits: { + attribute_count_limit: 1, + attribute_value_length_limit: 2, + }, + }, + spanLimits: { + attributeCountLimit: 1, + attributeValueLengthLimit: 2, + eventCountLimit: undefined, + attributePerEventCountLimit: undefined, + linkCountLimit: undefined, + attributePerLinkCountLimit: undefined, + }, + }, + { + testName: 'just span limits', + config: { + tracer_provider: { + processors: [{ simple: { exporter: { console: null } } }], + limits: { + attribute_count_limit: 10, + attribute_value_length_limit: 11, + event_count_limit: 12, + event_attribute_count_limit: 13, + link_count_limit: 14, + link_attribute_count_limit: 15, + }, + }, + }, + spanLimits: { + attributeCountLimit: 10, + attributeValueLengthLimit: 11, + eventCountLimit: 12, + attributePerEventCountLimit: 13, + linkCountLimit: 14, + attributePerLinkCountLimit: 15, + }, + }, + { + testName: 'span limits beat general limits', + config: { + attribute_limits: { + attribute_count_limit: 1, + attribute_value_length_limit: 2, + }, + tracer_provider: { + processors: [{ simple: { exporter: { console: null } } }], + limits: { + attribute_count_limit: 10, + event_count_limit: 12, + event_attribute_count_limit: 13, + link_count_limit: 14, + link_attribute_count_limit: 15, + }, + }, + }, + spanLimits: { + attributeCountLimit: 10, + attributeValueLengthLimit: 2, + eventCountLimit: 12, + attributePerEventCountLimit: 13, + linkCountLimit: 14, + attributePerLinkCountLimit: 15, + }, + }, + ]; + + for (const item of corpus) { + (item.only ? it.only : it)(item.testName, function () { + const spanLimits = createSpanLimitsFromConfig( + item.config.tracer_provider?.limits, + item.config.attribute_limits + ); + assert.deepStrictEqual(spanLimits, item.spanLimits); + }); + } + }); +}); diff --git a/experimental/packages/opentelemetry-sdk-node/test/create-from-env.test.ts b/experimental/packages/opentelemetry-sdk-node/test/create-from-env.test.ts new file mode 100644 index 00000000000..33bd47dffac --- /dev/null +++ b/experimental/packages/opentelemetry-sdk-node/test/create-from-env.test.ts @@ -0,0 +1,119 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as assert from 'assert'; +import * as sinon from 'sinon'; +import { diag } from '@opentelemetry/api'; +import { createSamplerFromEnv } from '../src/create-from-env'; + +function clearOTelEnv() { + for (const key of Object.keys(process.env)) { + if (key.startsWith('OTEL_')) { + delete process.env[key]; + } + } +} + +function setEnv(env: Record) { + for (const [k, v] of Object.entries(env)) { + process.env[k] = v; + } +} + +describe('create-from-env', () => { + let diagErrorSpy: sinon.SinonSpy; + + beforeEach(function () { + clearOTelEnv(); + diagErrorSpy = sinon.spy(diag, 'error'); + }); + + afterEach(function () { + clearOTelEnv(); + sinon.restore(); + }); + + describe('createSamplerFromEnv', () => { + const corpus: { + env: Record; + samplerRepr: string; + diagErr?: string; + only?: boolean; + }[] = [ + { + // empty env returns no sampler + env: {}, + samplerRepr: 'undefined', + }, + { + env: { OTEL_TRACES_SAMPLER: 'always_on' }, + samplerRepr: 'AlwaysOnSampler', + }, + { + env: { OTEL_TRACES_SAMPLER: 'always_off' }, + samplerRepr: 'AlwaysOffSampler', + }, + { + env: { OTEL_TRACES_SAMPLER: 'parentbased_always_on' }, + samplerRepr: + 'ParentBased{root=AlwaysOnSampler, remoteParentSampled=AlwaysOnSampler, remoteParentNotSampled=AlwaysOffSampler, localParentSampled=AlwaysOnSampler, localParentNotSampled=AlwaysOffSampler}', + }, + { + env: { OTEL_TRACES_SAMPLER: 'parentbased_always_off' }, + samplerRepr: + 'ParentBased{root=AlwaysOffSampler, remoteParentSampled=AlwaysOnSampler, remoteParentNotSampled=AlwaysOffSampler, localParentSampled=AlwaysOnSampler, localParentNotSampled=AlwaysOffSampler}', + }, + { + env: { OTEL_TRACES_SAMPLER: 'traceidratio' }, + samplerRepr: 'TraceIdRatioBased{1}', + }, + { + env: { + OTEL_TRACES_SAMPLER: 'traceidratio', + OTEL_TRACES_SAMPLER_ARG: '0.2', + }, + samplerRepr: 'TraceIdRatioBased{0.2}', + }, + { + env: { + OTEL_TRACES_SAMPLER: 'traceidratio', + OTEL_TRACES_SAMPLER_ARG: '-42', + }, + samplerRepr: 'TraceIdRatioBased{1}', + diagErr: + 'OTEL_TRACES_SAMPLER_ARG=-42 was given, but it is out of range', + }, + { + env: { + OTEL_TRACES_SAMPLER: 'parentbased_traceidratio', + OTEL_TRACES_SAMPLER_ARG: '0.2', + }, + samplerRepr: + 'ParentBased{root=TraceIdRatioBased{0.2}, remoteParentSampled=AlwaysOnSampler, remoteParentNotSampled=AlwaysOffSampler, localParentSampled=AlwaysOnSampler, localParentNotSampled=AlwaysOffSampler}', + }, + { + env: { OTEL_TRACES_SAMPLER: 'bogus' }, + samplerRepr: 'undefined', + diagErr: 'unknown OTEL_TRACES_SAMPLER value "bogus", using default', + }, + ]; + + for (const item of corpus) { + const testName = + Object.entries(item.env) + .map(([k, v]) => `${k}=${v}`) + .join(', ') || '(empty env)'; + (item.only ? it.only : it)(testName, function () { + setEnv(item.env); + const sampler = createSamplerFromEnv(); + + assert.deepStrictEqual(String(sampler), item.samplerRepr); + if ('diagErr' in item) { + sinon.assert.calledOnceWithMatch(diagErrorSpy, item.diagErr); + } + }); + } + }); +}); diff --git a/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts b/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts index 02829a82619..d2de469d4eb 100644 --- a/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts +++ b/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts @@ -29,19 +29,19 @@ import { OTLPMetricExporter as OTLPGrpcMetricExporter } from '@opentelemetry/exp import { OTLPMetricExporter as OTLPProtoMetricExporter } from '@opentelemetry/exporter-metrics-otlp-proto'; import { OTLPMetricExporter as OTLPHttpMetricExporter } from '@opentelemetry/exporter-metrics-otlp-http'; import { PrometheusExporter as PrometheusMetricExporter } from '@opentelemetry/exporter-prometheus'; -import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node'; +import { AlwaysOnSampler, TracerProvider } from '@opentelemetry/sdk-trace'; import { assertServiceInstanceIdIsUUID, assertServiceResource, } from './util/resource-assertions'; -import type { IdGenerator, SpanProcessor } from '@opentelemetry/sdk-trace-base'; +import type { IdGenerator, SpanProcessor } from '@opentelemetry/sdk-trace'; import { ConsoleSpanExporter, SimpleSpanProcessor, BatchSpanProcessor, NoopSpanProcessor, AlwaysOffSampler, -} from '@opentelemetry/sdk-trace-base'; +} from '@opentelemetry/sdk-trace'; import * as assert from 'assert'; import * as Sinon from 'sinon'; import { NodeSDK } from '../src'; @@ -87,7 +87,15 @@ function assertDefaultPropagatorRegistered() { ]); } -describe('Node SDK', () => { +function clearOTelEnv() { + for (const key of Object.keys(process.env)) { + if (key.startsWith('OTEL_')) { + delete process.env[key]; + } + } +} + +describe('NodeSDK', () => { let setGlobalTracerProviderSpy: Sinon.SinonSpy; let setGlobalLoggerProviderSpy: Sinon.SinonSpy; @@ -193,8 +201,7 @@ describe('Node SDK', () => { assert.strictEqual(setGlobalTracerProviderSpy.callCount, 1); assert.ok( - setGlobalTracerProviderSpy.lastCall.args[0] instanceof - NodeTracerProvider + setGlobalTracerProviderSpy.lastCall.args[0] instanceof TracerProvider ); await sdk.shutdown(); }); @@ -216,8 +223,7 @@ describe('Node SDK', () => { 'tracer provider should have changed once' ); assert.ok( - setGlobalTracerProviderSpy.lastCall.args[0] instanceof - NodeTracerProvider + setGlobalTracerProviderSpy.lastCall.args[0] instanceof TracerProvider ); await sdk.shutdown(); }); @@ -229,7 +235,7 @@ describe('Node SDK', () => { spanProcessors: [ new NoopSpanProcessor(), new SimpleSpanProcessor(exporter), - new BatchSpanProcessor(exporter), + new BatchSpanProcessor({ exporter }), ], autoDetectResources: false, }); @@ -239,11 +245,11 @@ describe('Node SDK', () => { assertDefaultContextManagerRegistered(); assertDefaultPropagatorRegistered(); - const nodeTracerProvider = setGlobalTracerProviderSpy.lastCall.args[0]; + const tracerProvider = setGlobalTracerProviderSpy.lastCall.args[0]; assert.strictEqual(setGlobalTracerProviderSpy.callCount, 1); - assert.ok(nodeTracerProvider instanceof NodeTracerProvider); + assert.ok(tracerProvider instanceof TracerProvider); - const spanProcessor = nodeTracerProvider['_activeSpanProcessor'] as any; + const spanProcessor = tracerProvider['_activeSpanProcessor'] as any; assert.ok( spanProcessor.constructor.name === 'MultiSpanProcessor', @@ -435,7 +441,7 @@ describe('Node SDK', () => { assert.strictEqual(setGlobalTracerProviderSpy.callCount, 1); const tracerProvider = setGlobalTracerProviderSpy.lastCall.args[0]; - assert.ok(tracerProvider instanceof NodeTracerProvider); + assert.ok(tracerProvider instanceof TracerProvider); assert.ok( (tracerProvider as any)._tracerOptions.meterProvider instanceof MeterProvider @@ -1735,7 +1741,7 @@ describe('Node SDK', () => { const getSdkSpanProcessors = (sdk: NodeSDK) => { const tracerProvider = sdk['_tracerProvider']; - assert.ok(tracerProvider instanceof NodeTracerProvider); + assert.ok(tracerProvider instanceof TracerProvider); const activeSpanProcessor = tracerProvider['_activeSpanProcessor']; @@ -2048,4 +2054,142 @@ describe('Node SDK', () => { await sdk.shutdown(); }); }); + + describe('configure sampler', async () => { + beforeEach(function () { + // Undo some of the env setup in the top-level `beforeEach`. + clearOTelEnv(); + }); + afterEach(function () { + clearOTelEnv(); + }); + + it('should configure default sampler', async () => { + const sdk = new NodeSDK(); + sdk.start(); + + const tracer = trace.getTracer('test'); + const samplerRepr = (tracer as any)._sampler.toString(); + assert.equal( + samplerRepr, + 'ParentBased{root=AlwaysOnSampler, remoteParentSampled=AlwaysOnSampler, remoteParentNotSampled=AlwaysOffSampler, localParentSampled=AlwaysOnSampler, localParentNotSampled=AlwaysOffSampler}' + ); + + await sdk.shutdown(); + }); + + it('should use given sampler', async () => { + const sdk = new NodeSDK({ + sampler: new AlwaysOffSampler(), + }); + sdk.start(); + + const tracer = trace.getTracer('test'); + const samplerRepr = (tracer as any)._sampler.toString(); + assert.equal(samplerRepr, 'AlwaysOffSampler'); + + await sdk.shutdown(); + }); + + it('should use sampler from env', async () => { + process.env.OTEL_TRACES_SAMPLER = 'traceidratio'; + process.env.OTEL_TRACES_SAMPLER_ARG = '0.42'; + const sdk = new NodeSDK(); + sdk.start(); + + const tracer = trace.getTracer('test'); + const samplerRepr = (tracer as any)._sampler.toString(); + assert.equal(samplerRepr, 'TraceIdRatioBased{0.42}'); + + await sdk.shutdown(); + }); + + it('given sampler should win over env', async () => { + process.env.OTEL_TRACES_SAMPLER = 'traceidratio'; + process.env.OTEL_TRACES_SAMPLER_ARG = '0.42'; + const sdk = new NodeSDK({ + sampler: new AlwaysOnSampler(), + }); + sdk.start(); + + const tracer = trace.getTracer('test'); + const samplerRepr = (tracer as any)._sampler.toString(); + assert.equal(samplerRepr, 'AlwaysOnSampler'); + + await sdk.shutdown(); + }); + }); + + describe('configure spanLimits', async () => { + beforeEach(function () { + // Undo some of the env setup in the top-level `beforeEach`. + clearOTelEnv(); + }); + afterEach(function () { + clearOTelEnv(); + }); + + it('should configure default span limits', async () => { + const sdk = new NodeSDK(); + sdk.start(); + + const tracer = trace.getTracer('test'); + const spanLimits = (tracer as any)._spanLimits; + assert.deepStrictEqual(spanLimits, { + attributeCountLimit: 128, + attributePerEventCountLimit: 128, + attributePerLinkCountLimit: 128, + attributeValueLengthLimit: Infinity, + eventCountLimit: 128, + linkCountLimit: 128, + }); + + await sdk.shutdown(); + }); + + it('should use given spanLimits and envvars', async () => { + process.env.OTEL_ATTRIBUTE_COUNT_LIMIT = '42'; // loses to `attributeCountLimit` arg + process.env.OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT = '12'; + process.env.OTEL_SPAN_ATTRIBUTE_PER_LINK_COUNT_LIMIT = '13'; + const sdk = new NodeSDK({ + spanLimits: { + attributeCountLimit: 10, + attributePerEventCountLimit: 11, + }, + }); + sdk.start(); + + const tracer = trace.getTracer('test'); + const spanLimits = (tracer as any)._spanLimits; + assert.deepStrictEqual(spanLimits, { + attributeCountLimit: 10, + attributePerEventCountLimit: 11, + attributePerLinkCountLimit: 13, + attributeValueLengthLimit: 12, + eventCountLimit: 128, + linkCountLimit: 128, + }); + + await sdk.shutdown(); + }); + }); + + describe('configure BatchSpanProcessor from env', async () => { + beforeEach(clearOTelEnv); + afterEach(clearOTelEnv); + + it('should configure using OTEL_BSP_ env vars', async () => { + process.env.OTEL_BSP_MAX_QUEUE_SIZE = '1000'; + const sdk = new NodeSDK(); + sdk.start(); + + const tracer = trace.getTracer('test'); + const bsp = (tracer as any)._spanProcessor._spanProcessors[0]; + assert.strictEqual(bsp._maxQueueSize, 1000); // from env + assert.strictEqual(bsp._maxExportBatchSize, 512); // default value + assert.ok(bsp instanceof BatchSpanProcessor); + + await sdk.shutdown(); + }); + }); }); diff --git a/experimental/packages/opentelemetry-sdk-node/test/start.test.ts b/experimental/packages/opentelemetry-sdk-node/test/start.test.ts index b2c16d0277f..64b13bad087 100644 --- a/experimental/packages/opentelemetry-sdk-node/test/start.test.ts +++ b/experimental/packages/opentelemetry-sdk-node/test/start.test.ts @@ -67,13 +67,13 @@ import { MeterProvider, PeriodicExportingMetricReader, } from '@opentelemetry/sdk-metrics'; -import type { SpanProcessor } from '@opentelemetry/sdk-trace-node'; -import { BasicTracerProvider } from '@opentelemetry/sdk-trace-base'; +import type { SpanProcessor } from '@opentelemetry/sdk-trace'; import { BatchSpanProcessor, ConsoleSpanExporter, SimpleSpanProcessor, -} from '@opentelemetry/sdk-trace-node'; + TracerProvider, +} from '@opentelemetry/sdk-trace'; describe('startNodeSDK', function () { let setGlobalLoggerProviderSpy: Sinon.SinonSpy; @@ -372,7 +372,7 @@ describe('startNodeSDK', function () { assert.strictEqual(setGlobalTracerProviderSpy.callCount, 1); assert.ok( - setGlobalTracerProviderSpy.lastCall.args[0] instanceof BasicTracerProvider + setGlobalTracerProviderSpy.lastCall.args[0] instanceof TracerProvider ); const tracerProvider = trace.getTracerProvider(); diff --git a/experimental/packages/opentelemetry-sdk-node/test/utils.test.ts b/experimental/packages/opentelemetry-sdk-node/test/utils.test.ts index c446334a4cd..218f078aa7a 100644 --- a/experimental/packages/opentelemetry-sdk-node/test/utils.test.ts +++ b/experimental/packages/opentelemetry-sdk-node/test/utils.test.ts @@ -15,7 +15,6 @@ import { getResourceDetectorsFromConfiguration, getHeadersFromConfiguration, getMeterViewsFromConfiguration, - getSpanLimitsFromConfiguration, getHttpAgentOptionsFromTls, } from '../src/utils'; import * as assert from 'assert'; @@ -35,7 +34,6 @@ import { } from '@opentelemetry/resources'; import type { LoggerProviderOptions } from '@opentelemetry/sdk-logs'; import { AggregationType, InstrumentType } from '@opentelemetry/sdk-metrics'; -import type { SpanLimits } from '@opentelemetry/sdk-trace-node'; describe('getPropagatorFromEnv', function () { afterEach(() => { @@ -788,42 +786,6 @@ describe('getMeterViewsFromConfiguration', function () { }); }); -describe('getSpanLimitsFromConfiguration', function () { - it('return undefined with no config for tracer limits', async () => { - assert.equal( - getSpanLimitsFromConfiguration({} as ConfigurationModel), - undefined - ); - }); - - it('return span limits', async () => { - const config: ConfigurationModel = { - tracer_provider: { - processors: [], - limits: { - attribute_count_limit: 10, - event_count_limit: 20, - link_count_limit: 30, - attribute_value_length_limit: 40, - event_attribute_count_limit: 50, - link_attribute_count_limit: 60, - }, - }, - } as ConfigurationModel; - const expectedSpanLimits: SpanLimits = { - attributeCountLimit: 10, - eventCountLimit: 20, - linkCountLimit: 30, - attributeValueLengthLimit: 40, - attributePerEventCountLimit: 50, - attributePerLinkCountLimit: 60, - }; - - const spanLimits = getSpanLimitsFromConfiguration(config); - assert.deepEqual(spanLimits, expectedSpanLimits); - }); -}); - describe('getHttpAgentOptionsFromTls', function () { afterEach(() => { sinon.restore(); diff --git a/experimental/packages/opentelemetry-sdk-node/tsconfig.json b/experimental/packages/opentelemetry-sdk-node/tsconfig.json index 82d161fc604..ef9199a6c0f 100644 --- a/experimental/packages/opentelemetry-sdk-node/tsconfig.json +++ b/experimental/packages/opentelemetry-sdk-node/tsconfig.json @@ -39,6 +39,9 @@ { "path": "../../../packages/sdk-metrics" }, + { + "path": "../../../packages/sdk-trace" + }, { "path": "../../../semantic-conventions" }, diff --git a/package-lock.json b/package-lock.json index a9e074901b5..0999051075e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1419,6 +1419,7 @@ "@opentelemetry/resources": "2.8.0", "@opentelemetry/sdk-logs": "0.219.0", "@opentelemetry/sdk-metrics": "2.8.0", + "@opentelemetry/sdk-trace": "2.8.0", "@opentelemetry/sdk-trace-base": "2.8.0", "@opentelemetry/sdk-trace-node": "2.8.0", "@opentelemetry/semantic-conventions": "^1.29.0"