diff --git a/packages/integrations/src/captureconsole.ts b/packages/integrations/src/captureconsole.ts index 59a5cec99fd4..b66459c9a4ae 100644 --- a/packages/integrations/src/captureconsole.ts +++ b/packages/integrations/src/captureconsole.ts @@ -18,12 +18,12 @@ export class CaptureConsole implements Integration { /** * @inheritDoc */ - private readonly _levels: string[] = CONSOLE_LEVELS; + private readonly _levels: typeof CONSOLE_LEVELS = CONSOLE_LEVELS; /** * @inheritDoc */ - public constructor(options: { levels?: string[] } = {}) { + public constructor(options: { levels?: typeof CONSOLE_LEVELS } = {}) { if (options.levels) { this._levels = options.levels; } diff --git a/packages/utils/src/logger.ts b/packages/utils/src/logger.ts index efb258e5fc38..b4855c219531 100644 --- a/packages/utils/src/logger.ts +++ b/packages/utils/src/logger.ts @@ -1,8 +1,7 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ import { WrappedFunction } from '@sentry/types'; import { IS_DEBUG_BUILD } from './flags'; -import { getGlobalObject } from './global'; +import { getGlobalObject, getGlobalSingleton } from './global'; // TODO: Implement different loggers for different environments const global = getGlobalObject(); @@ -10,109 +9,91 @@ const global = getGlobalObject(); /** Prefix for logging strings */ const PREFIX = 'Sentry Logger '; -export const CONSOLE_LEVELS = ['debug', 'info', 'warn', 'error', 'log', 'assert']; +export const CONSOLE_LEVELS = ['debug', 'info', 'warn', 'error', 'log', 'assert'] as const; + +type LoggerMethod = (...args: unknown[]) => void; +type LoggerConsoleMethods = Record; /** JSDoc */ -interface ExtensibleConsole extends Console { - [key: string]: any; +interface Logger extends LoggerConsoleMethods { + disable(): void; + enable(): void; } /** - * Temporarily unwrap `console.log` and friends in order to perform the given callback using the original methods. - * Restores wrapping after the callback completes. + * Temporarily disable sentry console instrumentations. * * @param callback The function to run against the original `console` messages * @returns The results of the callback */ -export function consoleSandbox(callback: () => any): any { +export function consoleSandbox(callback: () => T): T { const global = getGlobalObject(); if (!('console' in global)) { return callback(); } - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - const originalConsole = (global as any).console as ExtensibleConsole; - const wrappedLevels: { [key: string]: any } = {}; + const originalConsole = global.console as Console & Record; + const wrappedLevels: Partial = {}; // Restore all wrapped console methods CONSOLE_LEVELS.forEach(level => { - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - if (level in (global as any).console && (originalConsole[level] as WrappedFunction).__sentry_original__) { - wrappedLevels[level] = originalConsole[level] as WrappedFunction; - originalConsole[level] = (originalConsole[level] as WrappedFunction).__sentry_original__; + // TODO(v7): Remove this check as it's only needed for Node 6 + const originalWrappedFunc = + originalConsole[level] && (originalConsole[level] as WrappedFunction).__sentry_original__; + if (level in global.console && originalWrappedFunc) { + wrappedLevels[level] = originalConsole[level] as LoggerConsoleMethods[typeof level]; + originalConsole[level] = originalWrappedFunc as Console[typeof level]; } }); - // Perform callback manipulations - const result = callback(); - - // Revert restoration to wrapped state - Object.keys(wrappedLevels).forEach(level => { - originalConsole[level] = wrappedLevels[level]; - }); - - return result; -} - -/** JSDoc */ -class Logger { - /** JSDoc */ - private _enabled: boolean; - - /** JSDoc */ - public constructor() { - this._enabled = false; - } - - /** JSDoc */ - public disable(): void { - this._enabled = false; - } - - /** JSDoc */ - public enable(): void { - this._enabled = true; - } - - /** JSDoc */ - public log(...args: any[]): void { - if (!this._enabled) { - return; - } - consoleSandbox(() => { - global.console.log(`${PREFIX}[Log]:`, ...args); + try { + return callback(); + } finally { + // Revert restoration to wrapped state + Object.keys(wrappedLevels).forEach(level => { + originalConsole[level] = wrappedLevels[level as typeof CONSOLE_LEVELS[number]]; }); } +} - /** JSDoc */ - public warn(...args: any[]): void { - if (!this._enabled) { - return; - } - consoleSandbox(() => { - global.console.warn(`${PREFIX}[Warn]:`, ...args); +function makeLogger(): Logger { + let enabled = false; + const logger: Partial = { + enable: () => { + enabled = true; + }, + disable: () => { + enabled = false; + }, + }; + + if (IS_DEBUG_BUILD) { + CONSOLE_LEVELS.forEach(name => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + logger[name] = (...args: any[]) => { + if (enabled) { + consoleSandbox(() => { + global.console[name](`${PREFIX}[${name}]:`, ...args); + }); + } + }; }); - } - - /** JSDoc */ - public error(...args: any[]): void { - if (!this._enabled) { - return; - } - consoleSandbox(() => { - global.console.error(`${PREFIX}[Error]:`, ...args); + } else { + CONSOLE_LEVELS.forEach(name => { + logger[name] = () => undefined; }); } -} -const sentryGlobal = global.__SENTRY__ || {}; -const logger = (sentryGlobal.logger as Logger) || new Logger(); + return logger as Logger; +} +// Ensure we only have a single logger instance, even if multiple versions of @sentry/utils are being used +let logger: Logger; if (IS_DEBUG_BUILD) { - // Ensure we only have a single logger instance, even if multiple versions of @sentry/utils are being used - sentryGlobal.logger = logger; - global.__SENTRY__ = sentryGlobal; + logger = getGlobalSingleton('logger', makeLogger); +} else { + logger = makeLogger(); } export { logger };