diff --git a/packages/node/src/backend.ts b/packages/node/src/backend.ts index ad335f33eb01..0d2d3d267fc1 100644 --- a/packages/node/src/backend.ts +++ b/packages/node/src/backend.ts @@ -53,7 +53,9 @@ export class NodeBackend extends BaseBackend { public eventFromException(exception: any, hint?: EventHint): PromiseLike { // eslint-disable-next-line @typescript-eslint/no-explicit-any let ex: any = exception; - const mechanism: Mechanism = { + const providedMechanism: Mechanism | undefined = + hint && hint.data && (hint.data as { mechanism: Mechanism }).mechanism; + const mechanism: Mechanism = providedMechanism || { handled: true, type: 'generic', }; diff --git a/packages/node/src/integrations/onuncaughtexception.ts b/packages/node/src/integrations/onuncaughtexception.ts index 030628f3e85c..6d5bb9d7ab5f 100644 --- a/packages/node/src/integrations/onuncaughtexception.ts +++ b/packages/node/src/integrations/onuncaughtexception.ts @@ -78,7 +78,10 @@ export class OnUncaughtException implements Integration { if (hub.getIntegration(OnUncaughtException)) { hub.withScope((scope: Scope) => { scope.setLevel(Severity.Fatal); - hub.captureException(error, { originalException: error }); + hub.captureException(error, { + originalException: error, + data: { mechanism: { handled: false, type: 'onuncaughtexception' } }, + }); if (!calledFatalError) { calledFatalError = true; onFatalError(error); diff --git a/packages/node/src/integrations/onunhandledrejection.ts b/packages/node/src/integrations/onunhandledrejection.ts index 92ede9f8d1d8..19f733b1f908 100644 --- a/packages/node/src/integrations/onunhandledrejection.ts +++ b/packages/node/src/integrations/onunhandledrejection.ts @@ -69,7 +69,10 @@ export class OnUnhandledRejection implements Integration { scope.setExtras(context.extra); } - hub.captureException(reason, { originalException: promise }); + hub.captureException(reason, { + originalException: promise, + data: { mechanism: { handled: false, type: 'onunhandledrejection' } }, + }); }); /* eslint-disable @typescript-eslint/no-unsafe-member-access */ diff --git a/packages/node/test/onuncaughtexception.test.ts b/packages/node/test/onuncaughtexception.test.ts new file mode 100644 index 000000000000..7527eea00993 --- /dev/null +++ b/packages/node/test/onuncaughtexception.test.ts @@ -0,0 +1,34 @@ +import { Hub } from '@sentry/hub'; + +import { OnUncaughtException } from '../src/integrations/onuncaughtexception'; + +jest.mock('@sentry/hub', () => { + // we just want to short-circuit it, so dont worry about types + const original = jest.requireActual('@sentry/hub'); + original.Hub.prototype.getIntegration = () => true; + return { + ...original, + getCurrentHub: () => new Hub(), + }; +}); + +describe('uncaught exceptions', () => { + test('install global listener', () => { + const integration = new OnUncaughtException(); + integration.setupOnce(); + expect(process.listeners('uncaughtException')).toHaveLength(1); + }); + + test('sendUncaughtException', () => { + const integration = new OnUncaughtException({ onFatalError: jest.fn() }); + integration.setupOnce(); + + const captureException = jest.spyOn(Hub.prototype, 'captureException'); + + integration.handler({ message: 'message', name: 'name' }); + + expect(captureException.mock.calls[0][1]?.data).toEqual({ + mechanism: { handled: false, type: 'onuncaughtexception' }, + }); + }); +}); diff --git a/packages/node/test/onunhandledrejection.test.ts b/packages/node/test/onunhandledrejection.test.ts index 9118523cd289..5d8d498079e5 100644 --- a/packages/node/test/onunhandledrejection.test.ts +++ b/packages/node/test/onunhandledrejection.test.ts @@ -42,6 +42,9 @@ describe('unhandled promises', () => { integration.sendUnhandledPromise('bla', promise); + expect(captureException.mock.calls[0][1]?.data).toEqual({ + mechanism: { handled: false, type: 'onunhandledrejection' }, + }); expect(captureException.mock.calls[0][0]).toBe('bla'); expect(setUser.mock.calls[0][0]).toEqual({ id: 1 }); expect(setExtra.mock.calls[0]).toEqual(['unhandledPromiseRejection', true]);