diff --git a/experimental/CHANGELOG.md b/experimental/CHANGELOG.md index 4d036146cfa..f3b556773e6 100644 --- a/experimental/CHANGELOG.md +++ b/experimental/CHANGELOG.md @@ -12,6 +12,7 @@ For notes on migrating to 2.x / 0.200.x see [the upgrade guide](doc/upgrade-to-2 ### :bug: Bug Fixes +* fix(sdk-node): warn and ignore zero exporter timeout in declarative config [#6711](https://github.com/open-telemetry/opentelemetry-js/pull/6711) @MikeGoldsmith * fix(sdk-node): pass gRPC credentials and headers to span exporter in declarative config [#6705](https://github.com/open-telemetry/opentelemetry-js/pull/6705) @MikeGoldsmith * fix(otlp-transformer): do not attempt to skip groups [#6704](https://github.com/open-telemetry/opentelemetry-js/pull/6704) @pichlermarc diff --git a/experimental/packages/opentelemetry-sdk-node/src/utils.ts b/experimental/packages/opentelemetry-sdk-node/src/utils.ts index decac958513..5c3c28cbb48 100644 --- a/experimental/packages/opentelemetry-sdk-node/src/utils.ts +++ b/experimental/packages/opentelemetry-sdk-node/src/utils.ts @@ -698,6 +698,23 @@ export function getHeadersFromConfiguration( return result; } +/** + * Validate an exporter timeout value. The spec says 0 means "no limit + * (infinity)" but the JS exporters don't support that yet (see #6617). + * Warn and return undefined so the exporter falls back to its default. + */ +function validateExporterTimeout( + timeout: number | undefined +): number | undefined { + if (timeout === 0) { + diag.warn( + 'Exporter timeout of 0 (infinite) is not supported. Using default timeout.' + ); + return undefined; + } + return timeout; +} + export function getHttpAgentOptionsFromTls( tls: HttpTlsConfigModel | undefined ): { ca?: Buffer; cert?: Buffer; key?: Buffer } | undefined { @@ -777,7 +794,7 @@ export function getSpanExporter( : CompressionAlgorithm.NONE, url: exporter.otlp_http.endpoint, headers: getHeadersFromConfiguration(exporter.otlp_http.headers), - timeoutMillis: exporter.otlp_http.timeout, + timeoutMillis: validateExporterTimeout(exporter.otlp_http.timeout), httpAgentOptions: getHttpAgentOptionsFromTls(exporter.otlp_http.tls), }); } else { @@ -788,7 +805,7 @@ export function getSpanExporter( : CompressionAlgorithm.NONE, url: exporter.otlp_http.endpoint, headers: getHeadersFromConfiguration(exporter.otlp_http.headers), - timeoutMillis: exporter.otlp_http.timeout, + timeoutMillis: validateExporterTimeout(exporter.otlp_http.timeout), httpAgentOptions: getHttpAgentOptionsFromTls(exporter.otlp_http.tls), }); } @@ -799,7 +816,7 @@ export function getSpanExporter( ? CompressionAlgorithm.GZIP : CompressionAlgorithm.NONE, url: exporter.otlp_grpc.endpoint, - timeoutMillis: exporter.otlp_grpc.timeout, + timeoutMillis: validateExporterTimeout(exporter.otlp_grpc.timeout), credentials: getGrpcCredentialsFromTls(exporter.otlp_grpc.tls), metadata: getGrpcMetadataFromHeaders(exporter.otlp_grpc.headers), }); diff --git a/experimental/packages/opentelemetry-sdk-node/test/start.test.ts b/experimental/packages/opentelemetry-sdk-node/test/start.test.ts index d0751935e52..d23729a6a78 100644 --- a/experimental/packages/opentelemetry-sdk-node/test/start.test.ts +++ b/experimental/packages/opentelemetry-sdk-node/test/start.test.ts @@ -55,7 +55,11 @@ import { ATTR_SERVICE_INSTANCE_ID, } from '../src/semconv'; import { ATTR_OS_TYPE } from '@opentelemetry/resources/src/semconv'; -import { getLogRecordExporter, setupContextManager } from '../src/utils'; +import { + getLogRecordExporter, + getSpanExporter, + setupContextManager, +} from '../src/utils'; import { NOOP_SDK } from '../src/start'; import { ConsoleMetricExporter, @@ -959,6 +963,19 @@ describe('startNodeSDK', function () { assert.equal(getLogRecordExporter(exporter), undefined); }); + it('should warn when exporter timeout is 0', async () => { + const warnSpy = Sinon.spy(diag, 'warn'); + const exporter = getSpanExporter({ + otlp_http: { timeout: 0 }, + }); + assert.ok(exporter !== undefined); + assert.ok( + warnSpy.args.some(args => + String(args[0]).includes('timeout of 0 (infinite) is not supported') + ) + ); + }); + it('null context manager', async () => { setupContextManager(null); assert.equal(