From df253adbf32944acc5cff263fcc5f97ef651874f Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Mon, 27 Nov 2023 14:55:01 +0000 Subject: [PATCH 1/3] fix: Wrap all console log instances in console sandbox --- packages/core/src/hub.ts | 6 ++--- packages/core/src/sdk.ts | 8 +++--- packages/deno/src/transports/index.ts | 10 ++++--- packages/integration-shims/package.json | 3 ++- .../integration-shims/src/BrowserTracing.ts | 7 +++-- packages/integration-shims/src/Replay.ts | 7 +++-- .../src/common/wrapApiHandlerWithSentry.ts | 23 +++++++++++----- packages/nextjs/src/config/webpack.ts | 6 ++--- .../src/integrations/utils/errorhandling.ts | 8 +++--- packages/node/src/transports/http.ts | 11 +++++--- .../opentelemetry/src/custom/hubextensions.ts | 7 +++-- packages/replay/src/integration.ts | 18 ++++++++----- packages/replay/src/util/getPrivacyOptions.ts | 12 ++++++--- packages/sveltekit/src/client/handleError.ts | 7 +++-- packages/utils/src/dsn.ts | 8 +++--- packages/vue/src/errorhandler.ts | 7 +++-- packages/vue/src/integration.ts | 26 +++++++++++-------- 17 files changed, 110 insertions(+), 64 deletions(-) diff --git a/packages/core/src/hub.ts b/packages/core/src/hub.ts index 892ecf7b3b52..6d19b463b73b 100644 --- a/packages/core/src/hub.ts +++ b/packages/core/src/hub.ts @@ -377,13 +377,11 @@ export class Hub implements HubInterface { if (DEBUG_BUILD && !result) { const client = this.getClient(); if (!client) { - // eslint-disable-next-line no-console - console.warn( + logger.warn( "Tracing extension 'startTransaction' is missing. You should 'init' the SDK before calling 'startTransaction'", ); } else { - // eslint-disable-next-line no-console - console.warn(`Tracing extension 'startTransaction' has not been added. Call 'addTracingExtensions' before calling 'init': + logger.warn(`Tracing extension 'startTransaction' has not been added. Call 'addTracingExtensions' before calling 'init': Sentry.addTracingExtensions(); Sentry.init({...}); `); diff --git a/packages/core/src/sdk.ts b/packages/core/src/sdk.ts index 451a94df335c..a7131df5528d 100644 --- a/packages/core/src/sdk.ts +++ b/packages/core/src/sdk.ts @@ -1,5 +1,5 @@ import type { Client, ClientOptions } from '@sentry/types'; -import { logger } from '@sentry/utils'; +import { consoleSandbox, logger } from '@sentry/utils'; import { DEBUG_BUILD } from './debug-build'; import { getCurrentHub } from './hub'; @@ -23,8 +23,10 @@ export function initAndBind( logger.enable(); } else { // use `console.warn` rather than `logger.warn` since by non-debug bundles have all `logger.x` statements stripped - // eslint-disable-next-line no-console - console.warn('[Sentry] Cannot initialize SDK with `debug` option using a non-debug bundle.'); + consoleSandbox(() => { + // eslint-disable-next-line no-console + console.warn('[Sentry] Cannot initialize SDK with `debug` option using a non-debug bundle.'); + }); } } const hub = getCurrentHub(); diff --git a/packages/deno/src/transports/index.ts b/packages/deno/src/transports/index.ts index 62da327c5d83..964c1a9347af 100644 --- a/packages/deno/src/transports/index.ts +++ b/packages/deno/src/transports/index.ts @@ -1,6 +1,6 @@ import { createTransport } from '@sentry/core'; import type { BaseTransportOptions, Transport, TransportMakeRequestResponse, TransportRequest } from '@sentry/types'; -import { rejectedSyncPromise } from '@sentry/utils'; +import { consoleSandbox, rejectedSyncPromise } from '@sentry/utils'; export interface DenoTransportOptions extends BaseTransportOptions { /** Custom headers for the transport. Used by the XHRTransport and FetchTransport */ @@ -14,9 +14,11 @@ export function makeFetchTransport(options: DenoTransportOptions): Transport { const url = new URL(options.url); if (Deno.permissions.querySync({ name: 'net', host: url.host }).state !== 'granted') { - // eslint-disable-next-line no-console - console.warn(`Sentry SDK requires 'net' permission to send events. -Run with '--allow-net=${url.host}' to grant the requires permissions.`); + consoleSandbox(() => { + // eslint-disable-next-line no-console + console.warn(`Sentry SDK requires 'net' permission to send events. + Run with '--allow-net=${url.host}' to grant the requires permissions.`); + }); } function makeRequest(request: TransportRequest): PromiseLike { diff --git a/packages/integration-shims/package.json b/packages/integration-shims/package.json index 15ca58cb6a73..0dfe2f331d89 100644 --- a/packages/integration-shims/package.json +++ b/packages/integration-shims/package.json @@ -43,7 +43,8 @@ "url": "https://github.com/getsentry/sentry-javascript/issues" }, "dependencies": { - "@sentry/types": "7.81.1" + "@sentry/types": "7.81.1", + "@sentry/utils": "7.81.1" }, "engines": { "node": ">=12" diff --git a/packages/integration-shims/src/BrowserTracing.ts b/packages/integration-shims/src/BrowserTracing.ts index 4b2d90d029b6..60acf4fc5df5 100644 --- a/packages/integration-shims/src/BrowserTracing.ts +++ b/packages/integration-shims/src/BrowserTracing.ts @@ -1,4 +1,5 @@ import type { Integration } from '@sentry/types'; +import { consoleSandbox } from '@sentry/utils'; /** * This is a shim for the BrowserTracing integration. @@ -20,8 +21,10 @@ class BrowserTracingShim implements Integration { public constructor(_options: any) { this.name = BrowserTracingShim.id; - // eslint-disable-next-line no-console - console.error('You are using new BrowserTracing() even though this bundle does not include tracing.'); + consoleSandbox(() => { + // eslint-disable-next-line no-console + console.error('You are using new BrowserTracing() even though this bundle does not include tracing.'); + }); } /** jsdoc */ diff --git a/packages/integration-shims/src/Replay.ts b/packages/integration-shims/src/Replay.ts index a6983344ce6f..921a25c4bdfa 100644 --- a/packages/integration-shims/src/Replay.ts +++ b/packages/integration-shims/src/Replay.ts @@ -1,4 +1,5 @@ import type { Integration } from '@sentry/types'; +import { consoleSandbox } from '@sentry/utils'; /** * This is a shim for the Replay integration. @@ -20,8 +21,10 @@ class ReplayShim implements Integration { public constructor(_options: any) { this.name = ReplayShim.id; - // eslint-disable-next-line no-console - console.error('You are using new Replay() even though this bundle does not include replay.'); + consoleSandbox(() => { + // eslint-disable-next-line no-console + console.error('You are using new Replay() even though this bundle does not include replay.'); + }); } /** jsdoc */ diff --git a/packages/nextjs/src/common/wrapApiHandlerWithSentry.ts b/packages/nextjs/src/common/wrapApiHandlerWithSentry.ts index da5094f03834..fb83b60a360d 100644 --- a/packages/nextjs/src/common/wrapApiHandlerWithSentry.ts +++ b/packages/nextjs/src/common/wrapApiHandlerWithSentry.ts @@ -6,7 +6,14 @@ import { startTransaction, } from '@sentry/core'; import type { Transaction } from '@sentry/types'; -import { isString, logger, objectify, stripUrlQueryAndFragment, tracingContextFromHeaders } from '@sentry/utils'; +import { + consoleSandbox, + isString, + logger, + objectify, + stripUrlQueryAndFragment, + tracingContextFromHeaders, +} from '@sentry/utils'; import { DEBUG_BUILD } from './debug-build'; import type { AugmentedNextApiRequest, AugmentedNextApiResponse, NextApiHandler } from './types'; @@ -165,12 +172,14 @@ export function withSentry(apiHandler: NextApiHandler, parameterizedRoute?: stri // This can only happen (not always) when the user is using `withSentry` manually, which we're deprecating. // Warning suppression on Next.JS is only necessary in that case. ) { - // eslint-disable-next-line no-console - console.warn( - `[sentry] If Next.js logs a warning "API resolved without sending a response", it's a false positive, which may happen when you use \`withSentry\` manually to wrap your routes. - To suppress this warning, set \`SENTRY_IGNORE_API_RESOLUTION_ERROR\` to 1 in your env. - To suppress the nextjs warning, use the \`externalResolver\` API route option (see https://nextjs.org/docs/api-routes/api-middlewares#custom-config for details).`, - ); + consoleSandbox(() => { + // eslint-disable-next-line no-console + console.warn( + `[sentry] If Next.js logs a warning "API resolved without sending a response", it's a false positive, which may happen when you use \`withSentry\` manually to wrap your routes. + To suppress this warning, set \`SENTRY_IGNORE_API_RESOLUTION_ERROR\` to 1 in your env. + To suppress the nextjs warning, use the \`externalResolver\` API route option (see https://nextjs.org/docs/api-routes/api-middlewares#custom-config for details).`, + ); + }); } return handlerResult; diff --git a/packages/nextjs/src/config/webpack.ts b/packages/nextjs/src/config/webpack.ts index d8ce23b802d7..5c29b2884f41 100644 --- a/packages/nextjs/src/config/webpack.ts +++ b/packages/nextjs/src/config/webpack.ts @@ -1071,7 +1071,7 @@ class SentryCliDownloadPlugin implements WebpackPluginInstance { if (!downloadingCliAttempted) { downloadingCliAttempted = true; // eslint-disable-next-line no-console - console.log( + logger.info( `\n${chalk.cyan('info')} - ${chalk.bold( 'Sentry binary to upload source maps not found.', )} Package manager post-install scripts are likely disabled or there is a caching issue. Manually downloading instead...`, @@ -1087,12 +1087,12 @@ class SentryCliDownloadPlugin implements WebpackPluginInstance { cliDownloadPromise.then( () => { // eslint-disable-next-line no-console - console.log(`${chalk.cyan('info')} - Sentry binary was successfully downloaded.\n`); + logger.info(`${chalk.cyan('info')} - Sentry binary was successfully downloaded.\n`); return callback(); }, e => { // eslint-disable-next-line no-console - console.error(`${chalk.red('error')} - Sentry binary download failed:`, e); + logger.error(`${chalk.red('error')} - Sentry binary download failed:`, e); return callback(); }, ); diff --git a/packages/node/src/integrations/utils/errorhandling.ts b/packages/node/src/integrations/utils/errorhandling.ts index 86d55c2c8086..5d3abb90afce 100644 --- a/packages/node/src/integrations/utils/errorhandling.ts +++ b/packages/node/src/integrations/utils/errorhandling.ts @@ -1,5 +1,5 @@ import { getClient } from '@sentry/core'; -import { logger } from '@sentry/utils'; +import { consoleSandbox, logger } from '@sentry/utils'; import type { NodeClient } from '../../client'; import { DEBUG_BUILD } from '../../debug-build'; @@ -10,8 +10,10 @@ const DEFAULT_SHUTDOWN_TIMEOUT = 2000; * @hidden */ export function logAndExitProcess(error: Error): void { - // eslint-disable-next-line no-console - console.error(error); + consoleSandbox(() => { + // eslint-disable-next-line no-console + console.error(error); + }); const client = getClient(); diff --git a/packages/node/src/transports/http.ts b/packages/node/src/transports/http.ts index 340bcf4800f0..7a2cd50df319 100644 --- a/packages/node/src/transports/http.ts +++ b/packages/node/src/transports/http.ts @@ -6,6 +6,7 @@ import type { TransportRequest, TransportRequestExecutor, } from '@sentry/types'; +import { consoleSandbox } from '@sentry/utils'; import * as http from 'http'; import * as https from 'https'; import { HttpsProxyAgent } from 'https-proxy-agent'; @@ -53,10 +54,12 @@ export function makeNodeTransport(options: NodeTransportOptions): Transport { try { urlSegments = new URL(options.url); } catch (e) { - // eslint-disable-next-line no-console - console.warn( - '[@sentry/node]: Invalid dsn or tunnel option, will not send any events. The tunnel option must be a full URL when used.', - ); + consoleSandbox(() => { + // eslint-disable-next-line no-console + console.warn( + '[@sentry/node]: Invalid dsn or tunnel option, will not send any events. The tunnel option must be a full URL when used.', + ); + }); return createTransport(options, () => Promise.resolve({})); } diff --git a/packages/opentelemetry/src/custom/hubextensions.ts b/packages/opentelemetry/src/custom/hubextensions.ts index 4e839a9f3314..a70cb1aaca33 100644 --- a/packages/opentelemetry/src/custom/hubextensions.ts +++ b/packages/opentelemetry/src/custom/hubextensions.ts @@ -1,5 +1,6 @@ import { addTracingExtensions as _addTracingExtensions, getMainCarrier } from '@sentry/core'; import type { CustomSamplingContext, TransactionContext } from '@sentry/types'; +import { consoleSandbox } from '@sentry/utils'; /** * Add tracing extensions, ensuring a patched `startTransaction` to work with OTEL. @@ -22,8 +23,10 @@ function startTransactionNoop( _transactionContext: TransactionContext, _customSamplingContext?: CustomSamplingContext, ): unknown { - // eslint-disable-next-line no-console - console.warn('startTransaction is a noop in @sentry/opentelemetry. Use `startSpan` instead.'); + consoleSandbox(() => { + // eslint-disable-next-line no-console + console.warn('startTransaction is a noop in @sentry/opentelemetry. Use `startSpan` instead.'); + }); // We return an object here as hub.ts checks for the result of this // and renders a different warning if this is empty return {}; diff --git a/packages/replay/src/integration.ts b/packages/replay/src/integration.ts index b94907d44938..80bbeca5fdf3 100644 --- a/packages/replay/src/integration.ts +++ b/packages/replay/src/integration.ts @@ -1,6 +1,6 @@ import { getClient } from '@sentry/core'; import type { BrowserClientReplayOptions, Integration } from '@sentry/types'; -import { dropUndefinedKeys, isBrowser } from '@sentry/utils'; +import { consoleSandbox, dropUndefinedKeys, isBrowser } from '@sentry/utils'; import { DEFAULT_FLUSH_MAX_DELAY, @@ -349,8 +349,10 @@ function loadReplayOptionsFromClient(initialOptions: InitialReplayPluginOptions) const finalOptions = { sessionSampleRate: 0, errorSampleRate: 0, ...dropUndefinedKeys(initialOptions) }; if (!opt) { - // eslint-disable-next-line no-console - console.warn('SDK client is not available.'); + consoleSandbox(() => { + // eslint-disable-next-line no-console + console.warn('SDK client is not available.'); + }); return finalOptions; } @@ -360,10 +362,12 @@ function loadReplayOptionsFromClient(initialOptions: InitialReplayPluginOptions) opt.replaysSessionSampleRate == null && opt.replaysOnErrorSampleRate == null ) { - // eslint-disable-next-line no-console - console.warn( - 'Replay is disabled because neither `replaysSessionSampleRate` nor `replaysOnErrorSampleRate` are set.', - ); + consoleSandbox(() => { + // eslint-disable-next-line no-console + console.warn( + 'Replay is disabled because neither `replaysSessionSampleRate` nor `replaysOnErrorSampleRate` are set.', + ); + }); } if (typeof opt.replaysSessionSampleRate === 'number') { diff --git a/packages/replay/src/util/getPrivacyOptions.ts b/packages/replay/src/util/getPrivacyOptions.ts index c37075923d90..7d0584b7ba7b 100644 --- a/packages/replay/src/util/getPrivacyOptions.ts +++ b/packages/replay/src/util/getPrivacyOptions.ts @@ -1,3 +1,5 @@ +import { consoleSandbox } from '@sentry/utils'; + import type { DeprecatedPrivacyOptions, ReplayIntegrationPrivacyOptions } from '../types'; type GetPrivacyOptions = Required> & @@ -37,10 +39,12 @@ function getOption( allSelectors.push(`.${deprecatedClassOption}`); } - // eslint-disable-next-line no-console - console.warn( - '[Replay] You are using a deprecated configuration item for privacy. Read the documentation on how to use the new privacy configuration.', - ); + consoleSandbox(() => { + // eslint-disable-next-line no-console + console.warn( + '[Replay] You are using a deprecated configuration item for privacy. Read the documentation on how to use the new privacy configuration.', + ); + }); } return allSelectors.join(','); diff --git a/packages/sveltekit/src/client/handleError.ts b/packages/sveltekit/src/client/handleError.ts index 6c8a30fb1e03..6c16cee0ebbd 100644 --- a/packages/sveltekit/src/client/handleError.ts +++ b/packages/sveltekit/src/client/handleError.ts @@ -1,4 +1,5 @@ import { captureException } from '@sentry/svelte'; +import { consoleSandbox } from '@sentry/utils'; // For now disable the import/no-unresolved rule, because we don't have a way to // tell eslint that we are only importing types from the @sveltejs/kit package without // adding a custom resolver, which will take too much time. @@ -8,8 +9,10 @@ import type { HandleClientError, NavigationEvent } from '@sveltejs/kit'; // The SvelteKit default error handler just logs the error to the console // see: https://github.com/sveltejs/kit/blob/369e7d6851f543a40c947e033bfc4a9506fdc0a8/packages/kit/src/core/sync/write_client_manifest.js#LL127C2-L127C2 function defaultErrorHandler({ error }: Parameters[0]): ReturnType { - // eslint-disable-next-line no-console - console.error(error); + consoleSandbox(() => { + // eslint-disable-next-line no-console + console.error(error); + }); } /** diff --git a/packages/utils/src/dsn.ts b/packages/utils/src/dsn.ts index 574e9c78b4eb..7bf735c10780 100644 --- a/packages/utils/src/dsn.ts +++ b/packages/utils/src/dsn.ts @@ -1,7 +1,7 @@ import type { DsnComponents, DsnLike, DsnProtocol } from '@sentry/types'; import { DEBUG_BUILD } from './debug-build'; -import { logger } from './logger'; +import { consoleSandbox, logger } from './logger'; /** Regular expression used to parse a Dsn. */ const DSN_REGEX = /^(?:(\w+):)\/\/(?:(\w+)(?::(\w+)?)?@)([\w.-]+)(?::(\d+))?\/(.+)/; @@ -38,8 +38,10 @@ export function dsnFromString(str: string): DsnComponents | undefined { if (!match) { // This should be logged to the console - // eslint-disable-next-line no-console - console.error(`Invalid Sentry Dsn: ${str}`); + consoleSandbox(() => { + // eslint-disable-next-line no-console + console.error(`Invalid Sentry Dsn: ${str}`); + }); return undefined; } diff --git a/packages/vue/src/errorhandler.ts b/packages/vue/src/errorhandler.ts index 40a950ae9374..9d09bdf8c181 100644 --- a/packages/vue/src/errorhandler.ts +++ b/packages/vue/src/errorhandler.ts @@ -1,4 +1,5 @@ import { getCurrentHub } from '@sentry/browser'; +import { consoleSandbox } from '@sentry/utils'; import type { ViewModel, Vue, VueOptions } from './types'; import { formatComponentName, generateComponentTrace } from './vendor/components'; @@ -46,8 +47,10 @@ export const attachErrorHandler = (app: Vue, options: VueOptions): void => { if (warnHandler) { (warnHandler as UnknownFunc).call(null, message, vm, trace); } else if (hasConsole && !silent) { - // eslint-disable-next-line no-console - console.error(`[Vue warn]: ${message}${trace}`); + consoleSandbox(() => { + // eslint-disable-next-line no-console + console.error(`[Vue warn]: ${message}${trace}`); + }); } } }; diff --git a/packages/vue/src/integration.ts b/packages/vue/src/integration.ts index 9a3969f5c415..57d1fc6ccb2b 100644 --- a/packages/vue/src/integration.ts +++ b/packages/vue/src/integration.ts @@ -1,6 +1,6 @@ import { hasTracingEnabled } from '@sentry/core'; import type { Hub, Integration } from '@sentry/types'; -import { arrayify, GLOBAL_OBJ } from '@sentry/utils'; +import { arrayify, consoleSandbox, GLOBAL_OBJ } from '@sentry/utils'; import { DEFAULT_HOOKS } from './constants'; import { attachErrorHandler } from './errorhandler'; @@ -50,12 +50,14 @@ export class VueIntegration implements Integration { const options: Options = { ...DEFAULT_CONFIG, ...(client && client.getOptions()), ...this._options }; if (!options.Vue && !options.app) { - // eslint-disable-next-line no-console - console.warn( - `[@sentry/vue]: Misconfigured SDK. Vue specific errors will not be captured. -Update your \`Sentry.init\` call with an appropriate config option: -\`app\` (Application Instance - Vue 3) or \`Vue\` (Vue Constructor - Vue 2).`, - ); + consoleSandbox(() => { + // eslint-disable-next-line no-console + console.warn( + `[@sentry/vue]: Misconfigured SDK. Vue specific errors will not be captured. + Update your \`Sentry.init\` call with an appropriate config option: + \`app\` (Application Instance - Vue 3) or \`Vue\` (Vue Constructor - Vue 2).`, + ); + }); return; } @@ -80,10 +82,12 @@ const vueInit = (app: Vue, options: Options): void => { const isMounted = appWithInstance._instance && appWithInstance._instance.isMounted; if (isMounted === true) { - // eslint-disable-next-line no-console - console.warn( - '[@sentry/vue]: Misconfigured SDK. Vue app is already mounted. Make sure to call `app.mount()` after `Sentry.init()`.', - ); + consoleSandbox(() => { + // eslint-disable-next-line no-console + console.warn( + '[@sentry/vue]: Misconfigured SDK. Vue app is already mounted. Make sure to call `app.mount()` after `Sentry.init()`.', + ); + }); } attachErrorHandler(app, options); From 479c50a8f6f5dcd08f5c07d1904ba24b43364725 Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Mon, 27 Nov 2023 15:46:58 +0000 Subject: [PATCH 2/3] debug_build usage --- packages/remix/src/utils/instrumentServer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/remix/src/utils/instrumentServer.ts b/packages/remix/src/utils/instrumentServer.ts index f4676a3e8845..47d48f4a9d9c 100644 --- a/packages/remix/src/utils/instrumentServer.ts +++ b/packages/remix/src/utils/instrumentServer.ts @@ -93,7 +93,7 @@ export async function captureRemixServerException(err: unknown, name: string, re // Skip capturing if the request is aborted as Remix docs suggest // Ref: https://remix.run/docs/en/main/file-conventions/entry.server#handleerror if (request.signal.aborted) { - __DEBUG_BUILD__ && logger.warn('Skipping capture of aborted request'); + DEBUG_BUILD && logger.warn('Skipping capture of aborted request'); return; } From 4d6941f10240f8929b58871526db68c28aece896 Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Mon, 27 Nov 2023 16:20:19 +0000 Subject: [PATCH 3/3] omg --- packages/vue/src/integration.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/vue/src/integration.ts b/packages/vue/src/integration.ts index 57d1fc6ccb2b..3f35c66fe554 100644 --- a/packages/vue/src/integration.ts +++ b/packages/vue/src/integration.ts @@ -54,8 +54,8 @@ export class VueIntegration implements Integration { // eslint-disable-next-line no-console console.warn( `[@sentry/vue]: Misconfigured SDK. Vue specific errors will not be captured. - Update your \`Sentry.init\` call with an appropriate config option: - \`app\` (Application Instance - Vue 3) or \`Vue\` (Vue Constructor - Vue 2).`, +Update your \`Sentry.init\` call with an appropriate config option: +\`app\` (Application Instance - Vue 3) or \`Vue\` (Vue Constructor - Vue 2).`, ); }); return;