diff --git a/experimental/CHANGELOG.md b/experimental/CHANGELOG.md index 7effbe6adf7..28b78539210 100644 --- a/experimental/CHANGELOG.md +++ b/experimental/CHANGELOG.md @@ -22,6 +22,7 @@ For notes on migrating to 2.x / 0.200.x see [the upgrade guide](doc/upgrade-to-2 ### :bug: Bug Fixes +* fix(sdk-logs): stop `Logger.emit()` doing work (record construction, metrics, processor `onEmit`) after the `LoggerProvider` has shut down [#6826](https://github.com/open-telemetry/opentelemetry-js/pull/6826) @anneheartrecord * fix(sdk-node): pass all config properties (endpoint, headers, timeout, TLS, compression, temporality preference, default histogram aggregation) to OTLP metric exporters in declarative config [#6814](https://github.com/open-telemetry/opentelemetry-js/pull/6814) @MikeGoldsmith * fix(sdk-logs): default BatchLogRecordProcessor `scheduleDelayMillis` is 1000 [#6796](https://github.com/open-telemetry/opentelemetry-js/pull/6796) @trentm * fix(configuration): percent-decode keys and values in `resource.attributes_list` per spec [#6787](https://github.com/open-telemetry/opentelemetry-js/pull/6787) @MikeGoldsmith diff --git a/experimental/packages/sdk-logs/src/Logger.ts b/experimental/packages/sdk-logs/src/Logger.ts index 985d5a2edd9..b8019d0ab85 100644 --- a/experimental/packages/sdk-logs/src/Logger.ts +++ b/experimental/packages/sdk-logs/src/Logger.ts @@ -74,6 +74,10 @@ export class Logger implements ILogger { severityNumber?: SeverityNumber; eventName?: string; }): boolean { + if (this._sharedState.hasShutdown) { + return false; + } + const loggerConfig = this._loggerConfig; if (loggerConfig.disabled) { diff --git a/experimental/packages/sdk-logs/src/LoggerProvider.ts b/experimental/packages/sdk-logs/src/LoggerProvider.ts index 969bfaa779e..545b6e05b6e 100644 --- a/experimental/packages/sdk-logs/src/LoggerProvider.ts +++ b/experimental/packages/sdk-logs/src/LoggerProvider.ts @@ -121,6 +121,7 @@ export class LoggerProvider implements ILoggerProvider { } private _shutdown(): Promise { + this._sharedState.hasShutdown = true; return this._sharedState.activeProcessor.shutdown(); } } diff --git a/experimental/packages/sdk-logs/src/internal/LoggerProviderSharedState.ts b/experimental/packages/sdk-logs/src/internal/LoggerProviderSharedState.ts index bd7c13037a0..4920810b33a 100644 --- a/experimental/packages/sdk-logs/src/internal/LoggerProviderSharedState.ts +++ b/experimental/packages/sdk-logs/src/internal/LoggerProviderSharedState.ts @@ -42,6 +42,7 @@ export class LoggerProviderSharedState { readonly logRecordLimits: Required; readonly processors: LogRecordProcessor[]; readonly loggerMetrics: LoggerMetrics; + hasShutdown = false; private _loggerConfigurator: LoggerConfigurator; private _loggerConfigs: Map> = new Map(); diff --git a/experimental/packages/sdk-logs/test/common/Logger.test.ts b/experimental/packages/sdk-logs/test/common/Logger.test.ts index 58524eed044..31a1b72134b 100644 --- a/experimental/packages/sdk-logs/test/common/Logger.test.ts +++ b/experimental/packages/sdk-logs/test/common/Logger.test.ts @@ -97,6 +97,49 @@ describe('Logger', () => { assert.ok(callSpy.called); }); + it('should not do any work when emitting after the LoggerProvider has shut down', async () => { + const { logger, loggerProvider, logProcessor } = + setupLoggerProvider('simple'); + const onEmitSpy = sinon.spy(logProcessor, 'onEmit'); + const makeReadonlySpy = sinon.spy( + LogRecordImpl.prototype, + '_makeReadonly' + ); + + try { + assert.strictEqual( + logger.enabled(), + true, + 'sanity: logger should be enabled before shutdown' + ); + + await loggerProvider.shutdown(); + + assert.strictEqual( + logger.enabled(), + false, + 'enabled() should report false after the provider has shut down' + ); + + logger.emit({ + body: 'log body emitted after shutdown', + }); + + assert.strictEqual( + onEmitSpy.called, + false, + 'processor.onEmit should not be called after the provider has shut down' + ); + assert.strictEqual( + makeReadonlySpy.called, + false, + 'no LogRecord should be constructed after the provider has shut down' + ); + } finally { + makeReadonlySpy.restore(); + } + }); + it('should make log record instance readonly after emit it', () => { const { logger } = setupLoggerProvider('simple'); const makeOnlySpy = sinon.spy(LogRecordImpl.prototype, '_makeReadonly');