diff --git a/packages/opentelemetry-core/src/diag/SuppressedDiagLogger.ts b/packages/opentelemetry-core/src/diag/SuppressedDiagLogger.ts new file mode 100644 index 00000000000..24d751c3f6e --- /dev/null +++ b/packages/opentelemetry-core/src/diag/SuppressedDiagLogger.ts @@ -0,0 +1,73 @@ +/* + * 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 { DiagLogger, DiagLogFunction } from '@opentelemetry/api'; +import { isLoggingSuppressed, suppressLogging, unsuppressLogging } from './suppress-logging'; + +// Internal class - not exported +class SuppressedDiagLogger implements DiagLogger { + private _logger: DiagLogger; + + constructor(logger: DiagLogger) { + this._logger = logger; + } + + get error(): DiagLogFunction { + return this._proxy('error'); + } + + get warn(): DiagLogFunction { + return this._proxy('warn'); + } + + get info(): DiagLogFunction { + return this._proxy('info'); + } + + get debug(): DiagLogFunction { + return this._proxy('debug'); + } + + get verbose(): DiagLogFunction { + return this._proxy('verbose'); + } + + private _proxy(method: keyof DiagLogger): DiagLogFunction { + return (...args) => { + const wasSuppressed = isLoggingSuppressed(); + suppressLogging(); + try { + this._logger[method](...args); + } finally { + if (!wasSuppressed) { + unsuppressLogging(); + } + } + }; + } +} + +/** + * Creates a DiagLogger that wraps the provided logger and sets the logging + * suppression context before each log call. This is useful for preventing + * infinite loops when console instrumentations capture diag API calls. + * + * @param logger - The DiagLogger to wrap + * @returns A new DiagLogger that suppresses logging context during calls + */ +export function createSuppressedDiagLogger(logger: DiagLogger): DiagLogger { + return new SuppressedDiagLogger(logger); +} diff --git a/packages/opentelemetry-core/src/diag/suppress-logging.ts b/packages/opentelemetry-core/src/diag/suppress-logging.ts new file mode 100644 index 00000000000..d03860282e0 --- /dev/null +++ b/packages/opentelemetry-core/src/diag/suppress-logging.ts @@ -0,0 +1,29 @@ +/* + * 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. + */ + +let _loggingSuppressed = false; + +export function suppressLogging(): void { + _loggingSuppressed = true; +} + +export function unsuppressLogging(): void { + _loggingSuppressed = false; +} + +export function isLoggingSuppressed(): boolean { + return _loggingSuppressed; +} diff --git a/packages/opentelemetry-core/src/index.ts b/packages/opentelemetry-core/src/index.ts index 2a14ace80c9..3042f8df62a 100644 --- a/packages/opentelemetry-core/src/index.ts +++ b/packages/opentelemetry-core/src/index.ts @@ -71,6 +71,12 @@ export { suppressTracing, unsuppressTracing, } from './trace/suppress-tracing'; +export { + isLoggingSuppressed, + suppressLogging, + unsuppressLogging, +} from './diag/suppress-logging'; +export { createSuppressedDiagLogger } from './diag/SuppressedDiagLogger'; export { TraceState } from './trace/TraceState'; export { merge } from './utils/merge'; export { TimeoutError, callWithTimeout } from './utils/timeout';