diff --git a/dev-packages/e2e-tests/test-applications/node-nestjs-app/src/main.ts b/dev-packages/e2e-tests/test-applications/node-nestjs-app/src/main.ts index f852b29c8e06..b168cc19e7c3 100644 --- a/dev-packages/e2e-tests/test-applications/node-nestjs-app/src/main.ts +++ b/dev-packages/e2e-tests/test-applications/node-nestjs-app/src/main.ts @@ -1,4 +1,4 @@ -import { NestFactory } from '@nestjs/core'; +import { BaseExceptionFilter, HttpAdapterHost, NestFactory } from '@nestjs/core'; import * as Sentry from '@sentry/node'; import { AppModule1, AppModule2 } from './app.module'; @@ -15,7 +15,9 @@ async function bootstrap() { }); const app1 = await NestFactory.create(AppModule1); - Sentry.setupNestErrorHandler(app1); + + const { httpAdapter } = app1.get(HttpAdapterHost); + Sentry.setupNestErrorHandler(app1, new BaseExceptionFilter(httpAdapter)); await app1.listen(app1Port); diff --git a/dev-packages/e2e-tests/test-applications/node-nestjs-app/tests/errors.test.ts b/dev-packages/e2e-tests/test-applications/node-nestjs-app/tests/errors.test.ts index ba7b0cf1849b..7ad93315a84f 100644 --- a/dev-packages/e2e-tests/test-applications/node-nestjs-app/tests/errors.test.ts +++ b/dev-packages/e2e-tests/test-applications/node-nestjs-app/tests/errors.test.ts @@ -47,9 +47,12 @@ test('Sends exception to Sentry', async ({ baseURL }) => { }); try { - axios.get(`${baseURL}/test-exception/123`); - } catch { - // this results in an error, but we don't care - we want to check the error event + await axios.get(`${baseURL}/test-exception/123`); + // Should never be reached! + expect(false).toBe(true); + } catch (error) { + expect(error).toBeInstanceOf(AxiosError); + expect(error.response?.status).toBe(500); } const errorEvent = await errorEventPromise; diff --git a/dev-packages/node-integration-tests/suites/tracing/nestjs-errors-no-express/scenario.ts b/dev-packages/node-integration-tests/suites/tracing/nestjs-errors-no-express/scenario.ts index bcf4c3adb432..295eba00fd9a 100644 --- a/dev-packages/node-integration-tests/suites/tracing/nestjs-errors-no-express/scenario.ts +++ b/dev-packages/node-integration-tests/suites/tracing/nestjs-errors-no-express/scenario.ts @@ -15,7 +15,7 @@ Sentry.init({ }); import { Controller, Get, Injectable, Module, Param } from '@nestjs/common'; -import { NestFactory } from '@nestjs/core'; +import { BaseExceptionFilter, HttpAdapterHost, NestFactory } from '@nestjs/core'; const port = 3480; @@ -49,7 +49,8 @@ class AppModule {} async function init(): Promise { const app = await NestFactory.create(AppModule); - Sentry.setupNestErrorHandler(app); + const { httpAdapter } = app.get(HttpAdapterHost); + Sentry.setupNestErrorHandler(app, new BaseExceptionFilter(httpAdapter)); await app.listen(port); sendPortToRunner(port); } diff --git a/dev-packages/node-integration-tests/suites/tracing/nestjs-errors/scenario.ts b/dev-packages/node-integration-tests/suites/tracing/nestjs-errors/scenario.ts index 8a00c25fab7a..09a59eb8c7c7 100644 --- a/dev-packages/node-integration-tests/suites/tracing/nestjs-errors/scenario.ts +++ b/dev-packages/node-integration-tests/suites/tracing/nestjs-errors/scenario.ts @@ -13,7 +13,7 @@ Sentry.init({ }); import { Controller, Get, Injectable, Module, Param } from '@nestjs/common'; -import { NestFactory } from '@nestjs/core'; +import { BaseExceptionFilter, HttpAdapterHost, NestFactory } from '@nestjs/core'; const port = 3460; @@ -47,7 +47,8 @@ class AppModule {} async function init(): Promise { const app = await NestFactory.create(AppModule); - Sentry.setupNestErrorHandler(app); + const { httpAdapter } = app.get(HttpAdapterHost); + Sentry.setupNestErrorHandler(app, new BaseExceptionFilter(httpAdapter)); await app.listen(port); sendPortToRunner(port); } diff --git a/dev-packages/node-integration-tests/suites/tracing/nestjs-no-express/scenario.ts b/dev-packages/node-integration-tests/suites/tracing/nestjs-no-express/scenario.ts index cffc8de263d2..209f517193dc 100644 --- a/dev-packages/node-integration-tests/suites/tracing/nestjs-no-express/scenario.ts +++ b/dev-packages/node-integration-tests/suites/tracing/nestjs-no-express/scenario.ts @@ -15,7 +15,7 @@ Sentry.init({ }); import { Controller, Get, Injectable, Module, Param } from '@nestjs/common'; -import { NestFactory } from '@nestjs/core'; +import { BaseExceptionFilter, HttpAdapterHost, NestFactory } from '@nestjs/core'; const port = 3470; @@ -49,7 +49,8 @@ class AppModule {} async function init(): Promise { const app = await NestFactory.create(AppModule); - Sentry.setupNestErrorHandler(app); + const { httpAdapter } = app.get(HttpAdapterHost); + Sentry.setupNestErrorHandler(app, new BaseExceptionFilter(httpAdapter)); await app.listen(port); sendPortToRunner(port); } diff --git a/packages/node/src/integrations/tracing/nest.ts b/packages/node/src/integrations/tracing/nest.ts index b0b143774d2d..220b60d68a59 100644 --- a/packages/node/src/integrations/tracing/nest.ts +++ b/packages/node/src/integrations/tracing/nest.ts @@ -17,8 +17,14 @@ interface MinimalNestJsExecutionContext { }; }; } + +interface NestJsErrorFilter { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + catch(exception: any, host: any): void; +} + interface MinimalNestJsApp { - useGlobalFilters: (arg0: { catch(exception: unknown): void }) => void; + useGlobalFilters: (arg0: NestJsErrorFilter) => void; useGlobalInterceptors: (interceptor: { intercept: (context: MinimalNestJsExecutionContext, next: { handle: () => void }) => void; }) => void; @@ -40,16 +46,10 @@ const _nestIntegration = (() => { */ export const nestIntegration = defineIntegration(_nestIntegration); -const SentryNestExceptionFilter = { - catch(exception: unknown) { - captureException(exception); - }, -}; - /** * Setup an error handler for Nest. */ -export function setupNestErrorHandler(app: MinimalNestJsApp): void { +export function setupNestErrorHandler(app: MinimalNestJsApp, baseFilter: NestJsErrorFilter): void { app.useGlobalInterceptors({ intercept(context, next) { if (getIsolationScope() === getDefaultIsolationScope()) { @@ -65,5 +65,19 @@ export function setupNestErrorHandler(app: MinimalNestJsApp): void { }, }); - app.useGlobalFilters(SentryNestExceptionFilter); + const wrappedFilter = new Proxy(baseFilter, { + get(target, prop, receiver) { + if (prop === 'catch') { + const originalCatch = Reflect.get(target, prop, receiver); + + return (exception: unknown, host: unknown) => { + captureException(exception); + return originalCatch.apply(target, [exception, host]); + }; + } + return Reflect.get(target, prop, receiver); + }, + }); + + app.useGlobalFilters(wrappedFilter); }