diff --git a/dev-packages/browser-integration-tests/suites/integrations/captureConsole/test.ts b/dev-packages/browser-integration-tests/suites/integrations/captureConsole/test.ts index 7630d2a7824f..6f8cfc20f4aa 100644 --- a/dev-packages/browser-integration-tests/suites/integrations/captureConsole/test.ts +++ b/dev-packages/browser-integration-tests/suites/integrations/captureConsole/test.ts @@ -30,6 +30,7 @@ sentryTest('it captures console messages correctly', async ({ getLocalTestUrl, p extra: { arguments: ['console log'], }, + message: 'console log', }), ); expect(logEvent?.exception).toBeUndefined(); @@ -40,6 +41,7 @@ sentryTest('it captures console messages correctly', async ({ getLocalTestUrl, p extra: { arguments: ['console warn'], }, + message: 'console warn', }), ); expect(warnEvent?.exception).toBeUndefined(); @@ -50,6 +52,7 @@ sentryTest('it captures console messages correctly', async ({ getLocalTestUrl, p extra: { arguments: ['console info'], }, + message: 'console info', }), ); expect(infoEvent?.exception).toBeUndefined(); @@ -60,6 +63,7 @@ sentryTest('it captures console messages correctly', async ({ getLocalTestUrl, p extra: { arguments: ['console error'], }, + message: 'console error', }), ); expect(errorEvent?.exception).toBeUndefined(); @@ -70,6 +74,7 @@ sentryTest('it captures console messages correctly', async ({ getLocalTestUrl, p extra: { arguments: ['console trace'], }, + message: 'console trace', }), ); expect(traceEvent?.exception).toBeUndefined(); @@ -90,6 +95,11 @@ sentryTest('it captures console messages correctly', async ({ getLocalTestUrl, p }), ); expect(errorWithErrorEvent?.exception?.values?.[0].value).toBe('console error with error object'); + expect(errorWithErrorEvent?.exception?.values?.[0].mechanism).toEqual({ + // TODO (v9): Adjust to true after changing the integration's default value + handled: false, + type: 'console', + }); expect(traceWithErrorEvent).toEqual( expect.objectContaining({ level: 'log', diff --git a/packages/core/src/integrations/captureconsole.ts b/packages/core/src/integrations/captureconsole.ts index 563296511fd6..c180dcbe99e7 100644 --- a/packages/core/src/integrations/captureconsole.ts +++ b/packages/core/src/integrations/captureconsole.ts @@ -11,12 +11,24 @@ import { GLOBAL_OBJ } from '../utils-hoist/worldwide'; interface CaptureConsoleOptions { levels?: string[]; + + // TODO(v9): Flip default value to `true` and adjust JSDoc! + /** + * By default, Sentry will mark captured console messages as unhandled. + * Set this to `true` if you want to mark them as handled instead. + * + * Note: in v9 of the SDK, this option will default to `true`, meaning the default behavior will change to mark console messages as handled. + * @default false + */ + handled?: boolean; } const INTEGRATION_NAME = 'CaptureConsole'; const _captureConsoleIntegration = ((options: CaptureConsoleOptions = {}) => { const levels = options.levels || CONSOLE_LEVELS; + // TODO(v9): Flip default value to `true` + const handled = !!options.handled; return { name: INTEGRATION_NAME, @@ -30,7 +42,7 @@ const _captureConsoleIntegration = ((options: CaptureConsoleOptions = {}) => { return; } - consoleHandler(args, level); + consoleHandler(args, level, handled); }); }, }; @@ -41,7 +53,7 @@ const _captureConsoleIntegration = ((options: CaptureConsoleOptions = {}) => { */ export const captureConsoleIntegration = defineIntegration(_captureConsoleIntegration); -function consoleHandler(args: unknown[], level: string): void { +function consoleHandler(args: unknown[], level: string, handled: boolean): void { const captureContext: CaptureContext = { level: severityLevelFromString(level), extra: { @@ -54,7 +66,7 @@ function consoleHandler(args: unknown[], level: string): void { event.logger = 'console'; addExceptionMechanism(event, { - handled: false, + handled, type: 'console', }); diff --git a/packages/core/test/lib/integrations/captureconsole.test.ts b/packages/core/test/lib/integrations/captureconsole.test.ts index 3c646d378f88..4d480757fff1 100644 --- a/packages/core/test/lib/integrations/captureconsole.test.ts +++ b/packages/core/test/lib/integrations/captureconsole.test.ts @@ -305,29 +305,78 @@ describe('CaptureConsole setup', () => { }).not.toThrow(); }); - it("marks captured exception's mechanism as unhandled", () => { - // const addExceptionMechanismSpy = jest.spyOn(utils, 'addExceptionMechanism'); + describe('exception mechanism', () => { + // TODO (v9): Flip this below after adjusting the default value for `handled` in the integration + it("marks captured exception's mechanism as unhandled by default", () => { + const captureConsole = captureConsoleIntegration({ levels: ['error'] }); + captureConsole.setup?.(mockClient); - const captureConsole = captureConsoleIntegration({ levels: ['error'] }); - captureConsole.setup?.(mockClient); + const someError = new Error('some error'); + GLOBAL_OBJ.console.error(someError); - const someError = new Error('some error'); - GLOBAL_OBJ.console.error(someError); + const addedEventProcessor = (mockScope.addEventProcessor as jest.Mock).mock.calls[0][0]; + const someEvent: Event = { + exception: { + values: [{}], + }, + }; + addedEventProcessor(someEvent); - const addedEventProcessor = (mockScope.addEventProcessor as jest.Mock).mock.calls[0][0]; - const someEvent: Event = { - exception: { - values: [{}], - }, - }; - addedEventProcessor(someEvent); + expect(captureException).toHaveBeenCalledTimes(1); + expect(mockScope.addEventProcessor).toHaveBeenCalledTimes(1); - expect(captureException).toHaveBeenCalledTimes(1); - expect(mockScope.addEventProcessor).toHaveBeenCalledTimes(1); + expect(someEvent.exception?.values?.[0]?.mechanism).toEqual({ + handled: false, + type: 'console', + }); + }); + + it("marks captured exception's mechanism as handled if set in the options", () => { + const captureConsole = captureConsoleIntegration({ levels: ['error'], handled: true }); + captureConsole.setup?.(mockClient); - expect(someEvent.exception?.values?.[0]?.mechanism).toEqual({ - handled: false, - type: 'console', + const someError = new Error('some error'); + GLOBAL_OBJ.console.error(someError); + + const addedEventProcessor = (mockScope.addEventProcessor as jest.Mock).mock.calls[0][0]; + const someEvent: Event = { + exception: { + values: [{}], + }, + }; + addedEventProcessor(someEvent); + + expect(captureException).toHaveBeenCalledTimes(1); + expect(mockScope.addEventProcessor).toHaveBeenCalledTimes(1); + + expect(someEvent.exception?.values?.[0]?.mechanism).toEqual({ + handled: true, + type: 'console', + }); + }); + + it("marks captured exception's mechanism as unhandled if set in the options", () => { + const captureConsole = captureConsoleIntegration({ levels: ['error'], handled: false }); + captureConsole.setup?.(mockClient); + + const someError = new Error('some error'); + GLOBAL_OBJ.console.error(someError); + + const addedEventProcessor = (mockScope.addEventProcessor as jest.Mock).mock.calls[0][0]; + const someEvent: Event = { + exception: { + values: [{}], + }, + }; + addedEventProcessor(someEvent); + + expect(captureException).toHaveBeenCalledTimes(1); + expect(mockScope.addEventProcessor).toHaveBeenCalledTimes(1); + + expect(someEvent.exception?.values?.[0]?.mechanism).toEqual({ + handled: false, + type: 'console', + }); }); }); });