diff --git a/dev-packages/e2e-tests/test-applications/nestjs-11/tests/cron-decorator.test.ts b/dev-packages/e2e-tests/test-applications/nestjs-11/tests/cron-decorator.test.ts index e6ac7ae855ae..bf5e29004066 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs-11/tests/cron-decorator.test.ts +++ b/dev-packages/e2e-tests/test-applications/nestjs-11/tests/cron-decorator.test.ts @@ -71,6 +71,11 @@ test('Sends exceptions to Sentry on error in cron job', async ({ baseURL }) => { expect(errorEvent.exception?.values).toHaveLength(1); expect(errorEvent.exception?.values?.[0]?.value).toBe('Test error from cron job'); + expect(errorEvent.exception?.values?.[0]?.mechanism).toEqual({ + handled: false, + type: 'auto.cron.nestjs.async', + }); + expect(errorEvent.contexts?.trace).toEqual({ trace_id: expect.stringMatching(/[a-f0-9]{32}/), span_id: expect.stringMatching(/[a-f0-9]{16}/), diff --git a/dev-packages/e2e-tests/test-applications/nestjs-11/tests/errors.test.ts b/dev-packages/e2e-tests/test-applications/nestjs-11/tests/errors.test.ts index a24d1010eca4..e4b4423e8719 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs-11/tests/errors.test.ts +++ b/dev-packages/e2e-tests/test-applications/nestjs-11/tests/errors.test.ts @@ -13,6 +13,10 @@ test('Sends exception to Sentry', async ({ baseURL }) => { expect(errorEvent.exception?.values).toHaveLength(1); expect(errorEvent.exception?.values?.[0]?.value).toBe('This is an exception with id 123'); + expect(errorEvent.exception?.values?.[0]?.mechanism).toEqual({ + handled: false, + type: 'auto.http.nestjs.global_filter', + }); expect(errorEvent.request).toEqual({ method: 'GET', diff --git a/dev-packages/e2e-tests/test-applications/nestjs-8/tests/errors.test.ts b/dev-packages/e2e-tests/test-applications/nestjs-8/tests/errors.test.ts index e60e94691210..4b05da9c4d98 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs-8/tests/errors.test.ts +++ b/dev-packages/e2e-tests/test-applications/nestjs-8/tests/errors.test.ts @@ -13,6 +13,10 @@ test('Sends exception to Sentry', async ({ baseURL }) => { expect(errorEvent.exception?.values).toHaveLength(1); expect(errorEvent.exception?.values?.[0]?.value).toBe('This is an exception with id 123'); + expect(errorEvent.exception?.values?.[0]?.mechanism).toEqual({ + handled: false, + type: 'auto.http.nestjs.global_filter', + }); expect(errorEvent.request).toEqual({ method: 'GET', diff --git a/dev-packages/e2e-tests/test-applications/nestjs-basic-with-graphql/tests/errors.test.ts b/dev-packages/e2e-tests/test-applications/nestjs-basic-with-graphql/tests/errors.test.ts index 62103cfafbd9..3c9d8532c889 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs-basic-with-graphql/tests/errors.test.ts +++ b/dev-packages/e2e-tests/test-applications/nestjs-basic-with-graphql/tests/errors.test.ts @@ -14,6 +14,11 @@ test('Sends exception to Sentry', async ({ baseURL }) => { expect(errorEvent.exception?.values).toHaveLength(1); expect(errorEvent.exception?.values?.[0]?.value).toBe('This is an exception with id 123'); + expect(errorEvent.exception?.values?.[0]?.mechanism).toEqual({ + handled: false, + type: 'auto.http.nestjs.global_filter', + }); + expect(errorEvent.request).toEqual({ method: 'GET', cookies: {}, @@ -102,6 +107,11 @@ test('Sends graphql exception to Sentry', async ({ baseURL }) => { expect(errorEvent.exception?.values).toHaveLength(1); expect(errorEvent.exception?.values?.[0]?.value).toBe('This is an exception!'); + expect(errorEvent.exception?.values?.[0]?.mechanism).toEqual({ + handled: false, + type: 'auto.graphql.nestjs.global_filter', + }); + expect(errorEvent.request).toEqual({ method: 'POST', cookies: {}, diff --git a/dev-packages/e2e-tests/test-applications/nestjs-basic/tests/cron-decorator.test.ts b/dev-packages/e2e-tests/test-applications/nestjs-basic/tests/cron-decorator.test.ts index c193a94911c1..e0610f36c676 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs-basic/tests/cron-decorator.test.ts +++ b/dev-packages/e2e-tests/test-applications/nestjs-basic/tests/cron-decorator.test.ts @@ -75,6 +75,11 @@ test('Sends exceptions to Sentry on error in async cron job', async ({ baseURL } span_id: expect.stringMatching(/[a-f0-9]{16}/), }); + expect(errorEvent.exception?.values?.[0]?.mechanism).toEqual({ + handled: false, + type: 'auto.cron.nestjs.async', + }); + // kill cron so tests don't get stuck await fetch(`${baseURL}/kill-test-cron/test-async-cron-error`); }); @@ -92,6 +97,11 @@ test('Sends exceptions to Sentry on error in sync cron job', async ({ baseURL }) span_id: expect.stringMatching(/[a-f0-9]{16}/), }); + expect(errorEvent.exception?.values?.[0]?.mechanism).toEqual({ + handled: false, + type: 'auto.cron.nestjs', + }); + // kill cron so tests don't get stuck await fetch(`${baseURL}/kill-test-cron/test-sync-cron-error`); }); diff --git a/dev-packages/e2e-tests/test-applications/nestjs-basic/tests/errors.test.ts b/dev-packages/e2e-tests/test-applications/nestjs-basic/tests/errors.test.ts index 748730985cf6..09effba34198 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs-basic/tests/errors.test.ts +++ b/dev-packages/e2e-tests/test-applications/nestjs-basic/tests/errors.test.ts @@ -13,6 +13,10 @@ test('Sends exception to Sentry', async ({ baseURL }) => { expect(errorEvent.exception?.values).toHaveLength(1); expect(errorEvent.exception?.values?.[0]?.value).toBe('This is an exception with id 123'); + expect(errorEvent.exception?.values?.[0]?.mechanism).toEqual({ + handled: false, + type: 'auto.http.nestjs.global_filter', + }); expect(errorEvent.request).toEqual({ method: 'GET', diff --git a/dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/tests/events.test.ts b/dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/tests/events.test.ts index 62781e32e37c..60c1ad6590af 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/tests/events.test.ts +++ b/dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/tests/events.test.ts @@ -21,7 +21,10 @@ test('Event emitter', async () => { type: 'Error', value: 'Test error from event handler', stacktrace: expect.any(Object), - mechanism: expect.any(Object), + mechanism: { + handled: false, + type: 'auto.event.nestjs', + }, }, ], }); diff --git a/dev-packages/e2e-tests/test-applications/nestjs-fastify/tests/cron-decorator.test.ts b/dev-packages/e2e-tests/test-applications/nestjs-fastify/tests/cron-decorator.test.ts index e352e8fdba8f..1e9d62c2c96a 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs-fastify/tests/cron-decorator.test.ts +++ b/dev-packages/e2e-tests/test-applications/nestjs-fastify/tests/cron-decorator.test.ts @@ -71,6 +71,10 @@ test('Sends exceptions to Sentry on error in cron job', async ({ baseURL }) => { expect(errorEvent.exception?.values).toHaveLength(1); expect(errorEvent.exception?.values?.[0]?.value).toBe('Test error from cron job'); + expect(errorEvent.exception?.values?.[0]?.mechanism).toEqual({ + handled: false, + type: 'auto.cron.nestjs.async', + }); expect(errorEvent.contexts?.trace).toEqual({ trace_id: expect.stringMatching(/[a-f0-9]{32}/), span_id: expect.stringMatching(/[a-f0-9]{16}/), diff --git a/dev-packages/e2e-tests/test-applications/nestjs-fastify/tests/errors.test.ts b/dev-packages/e2e-tests/test-applications/nestjs-fastify/tests/errors.test.ts index 4eea05edd36f..1cbddb8256a3 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs-fastify/tests/errors.test.ts +++ b/dev-packages/e2e-tests/test-applications/nestjs-fastify/tests/errors.test.ts @@ -14,6 +14,11 @@ test('Sends exception to Sentry', async ({ baseURL }) => { expect(errorEvent.exception?.values).toHaveLength(1); expect(errorEvent.exception?.values?.[0]?.value).toBe('This is an exception with id 123'); + expect(errorEvent.exception?.values?.[0]?.mechanism).toEqual({ + handled: false, + type: 'auto.http.nestjs.global_filter', + }); + expect(errorEvent.request).toEqual({ method: 'GET', cookies: {}, diff --git a/dev-packages/e2e-tests/test-applications/nestjs-graphql/tests/errors.test.ts b/dev-packages/e2e-tests/test-applications/nestjs-graphql/tests/errors.test.ts index 9f9e7cdc0d17..6b0047366203 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs-graphql/tests/errors.test.ts +++ b/dev-packages/e2e-tests/test-applications/nestjs-graphql/tests/errors.test.ts @@ -32,6 +32,11 @@ test('Sends exception to Sentry', async ({ baseURL }) => { expect(errorEvent.exception?.values).toHaveLength(1); expect(errorEvent.exception?.values?.[0]?.value).toBe('This is an exception!'); + expect(errorEvent.exception?.values?.[0]?.mechanism).toEqual({ + handled: false, + type: 'auto.graphql.nestjs.global_filter', + }); + expect(errorEvent.request).toEqual({ method: 'POST', cookies: {}, diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/tests/errors.test.ts b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/tests/errors.test.ts index fc7520fbcb7b..4e82d8f1d913 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/tests/errors.test.ts +++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/tests/errors.test.ts @@ -22,6 +22,11 @@ test('Sends unexpected exception to Sentry if thrown in module with global filte expect(errorEvent.exception?.values).toHaveLength(1); expect(errorEvent.exception?.values?.[0]?.value).toBe('This is an uncaught exception!'); + expect(errorEvent.exception?.values?.[0]?.mechanism).toEqual({ + handled: false, + type: 'auto.function.nestjs.exception_captured', + }); + expect(errorEvent.request).toEqual({ method: 'GET', cookies: {}, @@ -59,6 +64,11 @@ test('Sends unexpected exception to Sentry if thrown in module with local filter expect(errorEvent.exception?.values).toHaveLength(1); expect(errorEvent.exception?.values?.[0]?.value).toBe('This is an uncaught exception!'); + expect(errorEvent.exception?.values?.[0]?.mechanism).toEqual({ + handled: false, + type: 'auto.function.nestjs.exception_captured', + }); + expect(errorEvent.request).toEqual({ method: 'GET', cookies: {}, @@ -98,6 +108,11 @@ test('Sends unexpected exception to Sentry if thrown in module that was register expect(errorEvent.exception?.values).toHaveLength(1); expect(errorEvent.exception?.values?.[0]?.value).toBe('This is an uncaught exception!'); + expect(errorEvent.exception?.values?.[0]?.mechanism).toEqual({ + handled: false, + type: 'auto.function.nestjs.exception_captured', + }); + expect(errorEvent.request).toEqual({ method: 'GET', cookies: {}, diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/tests/errors.test.ts b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/tests/errors.test.ts index e29d4677397f..eb3a3d809417 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/tests/errors.test.ts +++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/tests/errors.test.ts @@ -14,6 +14,11 @@ test('Sends unexpected exception to Sentry if thrown in module with global filte expect(errorEvent.exception?.values).toHaveLength(1); expect(errorEvent.exception?.values?.[0]?.value).toBe('This is an uncaught exception!'); + expect(errorEvent.exception?.values?.[0]?.mechanism).toEqual({ + handled: false, + type: 'auto.http.nestjs.global_filter', + }); + expect(errorEvent.request).toEqual({ method: 'GET', cookies: {}, @@ -43,6 +48,11 @@ test('Sends unexpected exception to Sentry if thrown in module with local filter expect(errorEvent.exception?.values).toHaveLength(1); expect(errorEvent.exception?.values?.[0]?.value).toBe('This is an uncaught exception!'); + expect(errorEvent.exception?.values?.[0]?.mechanism).toEqual({ + handled: false, + type: 'auto.http.nestjs.global_filter', + }); + expect(errorEvent.request).toEqual({ method: 'GET', cookies: {}, @@ -74,6 +84,11 @@ test('Sends unexpected exception to Sentry if thrown in module that was register expect(errorEvent.exception?.values).toHaveLength(1); expect(errorEvent.exception?.values?.[0]?.value).toBe('This is an uncaught exception!'); + expect(errorEvent.exception?.values?.[0]?.mechanism).toEqual({ + handled: false, + type: 'auto.http.nestjs.global_filter', + }); + expect(errorEvent.request).toEqual({ method: 'GET', cookies: {}, diff --git a/packages/nestjs/src/decorators.ts b/packages/nestjs/src/decorators.ts index 7ac4941be877..dd053009d580 100644 --- a/packages/nestjs/src/decorators.ts +++ b/packages/nestjs/src/decorators.ts @@ -19,12 +19,12 @@ export const SentryCron = (monitorSlug: string, monitorConfig?: MonitorConfig): try { result = originalMethod.apply(this, args); } catch (e) { - captureException(e); + captureException(e, { mechanism: { handled: false, type: 'auto.cron.nestjs' } }); throw e; } if (isThenable(result)) { return result.then(undefined, e => { - captureException(e); + captureException(e, { mechanism: { handled: false, type: 'auto.cron.nestjs.async' } }); throw e; }); } @@ -77,7 +77,7 @@ export function SentryExceptionCaptured() { return originalCatch.apply(this, [exception, host, ...args]); } - captureException(exception); + captureException(exception, { mechanism: { handled: false, type: 'auto.function.nestjs.exception_captured' } }); return originalCatch.apply(this, [exception, host, ...args]); }; diff --git a/packages/nestjs/src/integrations/sentry-nest-event-instrumentation.ts b/packages/nestjs/src/integrations/sentry-nest-event-instrumentation.ts index a572bb93a52f..f8076087fd5d 100644 --- a/packages/nestjs/src/integrations/sentry-nest-event-instrumentation.ts +++ b/packages/nestjs/src/integrations/sentry-nest-event-instrumentation.ts @@ -109,7 +109,12 @@ export class SentryNestEventInstrumentation extends InstrumentationBase { return result; } catch (error) { // exceptions from event handlers are not caught by global error filter - captureException(error); + captureException(error, { + mechanism: { + handled: false, + type: 'auto.event.nestjs', + }, + }); throw error; } }); diff --git a/packages/nestjs/src/setup.ts b/packages/nestjs/src/setup.ts index c1af5938e3c2..cc6d65514b77 100644 --- a/packages/nestjs/src/setup.ts +++ b/packages/nestjs/src/setup.ts @@ -101,7 +101,12 @@ class SentryGlobalFilter extends BaseExceptionFilter { this._logger.error(exception.message, exception.stack); } - captureException(exception); + captureException(exception, { + mechanism: { + handled: false, + type: 'auto.graphql.nestjs.global_filter', + }, + }); throw exception; } @@ -117,7 +122,12 @@ class SentryGlobalFilter extends BaseExceptionFilter { // Handle any other kind of error if (!(exception instanceof Error)) { if (!isExpectedError(exception)) { - captureException(exception); + captureException(exception, { + mechanism: { + handled: false, + type: 'auto.rpc.nestjs.global_filter', + }, + }); } throw exception; } @@ -125,7 +135,12 @@ class SentryGlobalFilter extends BaseExceptionFilter { // In this case we're likely running into an RpcException, which the user should handle with a dedicated filter // https://github.com/nestjs/nest/blob/master/sample/03-microservices/src/common/filters/rpc-exception.filter.ts if (!isExpectedError(exception)) { - captureException(exception); + captureException(exception, { + mechanism: { + handled: false, + type: 'auto.rpc.nestjs.global_filter', + }, + }); } this._logger.warn( @@ -139,7 +154,12 @@ class SentryGlobalFilter extends BaseExceptionFilter { // HTTP exceptions if (!isExpectedError(exception)) { - captureException(exception); + captureException(exception, { + mechanism: { + handled: false, + type: 'auto.http.nestjs.global_filter', + }, + }); } return super.catch(exception, host); diff --git a/packages/nestjs/test/decorators.test.ts b/packages/nestjs/test/decorators.test.ts index 329f2fbe8fa4..04b653fb7abb 100644 --- a/packages/nestjs/test/decorators.test.ts +++ b/packages/nestjs/test/decorators.test.ts @@ -275,7 +275,12 @@ describe('SentryExceptionCaptured decorator', () => { decoratedMethod(exception, host); expect(captureExceptionSpy).toHaveBeenCalledTimes(1); - expect(captureExceptionSpy).toHaveBeenCalledWith(exception); + expect(captureExceptionSpy).toHaveBeenCalledWith(exception, { + mechanism: { + handled: false, + type: 'auto.function.nestjs.exception_captured', + }, + }); expect(originalCatch).toHaveBeenCalledWith(exception, host); isExpectedErrorSpy.mockRestore(); diff --git a/packages/nestjs/test/integrations/nest.test.ts b/packages/nestjs/test/integrations/nest.test.ts index 2eecfbc6b240..69fb022441dd 100644 --- a/packages/nestjs/test/integrations/nest.test.ts +++ b/packages/nestjs/test/integrations/nest.test.ts @@ -97,7 +97,12 @@ describe('Nest', () => { decorated(mockTarget, 'testMethod', descriptor); await expect(descriptor.value()).rejects.toThrow(error); - expect(core.captureException).toHaveBeenCalledWith(error); + expect(core.captureException).toHaveBeenCalledWith(error, { + mechanism: { + handled: false, + type: 'auto.event.nestjs', + }, + }); }); it('should skip wrapping for internal Sentry handlers', () => { diff --git a/packages/nestjs/test/sentry-global-filter.test.ts b/packages/nestjs/test/sentry-global-filter.test.ts index fc8a3444aecd..d9b4ff3d1b1f 100644 --- a/packages/nestjs/test/sentry-global-filter.test.ts +++ b/packages/nestjs/test/sentry-global-filter.test.ts @@ -133,7 +133,12 @@ describe('SentryGlobalFilter', () => { filter.catch(error, mockArgumentsHost); }).toThrow(error); - expect(mockCaptureException).toHaveBeenCalledWith(error); + expect(mockCaptureException).toHaveBeenCalledWith(error, { + mechanism: { + handled: false, + type: 'auto.graphql.nestjs.global_filter', + }, + }); expect(mockLoggerError).toHaveBeenCalledWith(error.message, error.stack); }); });