Skip to content

Commit 7150e3f

Browse files
authored
feat(nestjs): Filter all HttpExceptions (#13120)
So far we are filtering exceptions on the status code and do not report 4xx errors. However, we actually only want to capture unexpected exceptions but all HttpExceptions are only ever thrown explicitly. Hence, we do not want to capture them and they should be filtered. In `@sentry/nest` we can use the nest `HttpException` directly, since we have `@nest/common` as a dependency. In `@sentry/node` this is not the case, so we filter based on whether a property called status is defined.
1 parent b4f2067 commit 7150e3f

File tree

8 files changed

+79
-30
lines changed

8 files changed

+79
-30
lines changed

dev-packages/e2e-tests/test-applications/nestjs-basic/src/app.controller.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,14 @@ export class AppController {
2020
return this.appService.testException(id);
2121
}
2222

23-
@Get('test-expected-exception/:id')
24-
async testExpectedException(@Param('id') id: string) {
25-
return this.appService.testExpectedException(id);
23+
@Get('test-expected-400-exception/:id')
24+
async testExpected400Exception(@Param('id') id: string) {
25+
return this.appService.testExpected400Exception(id);
26+
}
27+
28+
@Get('test-expected-500-exception/:id')
29+
async testExpected500Exception(@Param('id') id: string) {
30+
return this.appService.testExpected500Exception(id);
2631
}
2732

2833
@Get('test-span-decorator-async')

dev-packages/e2e-tests/test-applications/nestjs-basic/src/app.service.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,12 @@ export class AppService {
3030
throw new Error(`This is an exception with id ${id}`);
3131
}
3232

33-
testExpectedException(id: string) {
34-
throw new HttpException(`This is an expected exception with id ${id}`, HttpStatus.FORBIDDEN);
33+
testExpected400Exception(id: string) {
34+
throw new HttpException(`This is an expected 400 exception with id ${id}`, HttpStatus.BAD_REQUEST);
35+
}
36+
37+
testExpected500Exception(id: string) {
38+
throw new HttpException(`This is an expected 500 exception with id ${id}`, HttpStatus.INTERNAL_SERVER_ERROR);
3539
}
3640

3741
@SentryTraced('wait and return a string')

dev-packages/e2e-tests/test-applications/nestjs-basic/tests/errors.test.ts

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,25 +29,41 @@ test('Sends exception to Sentry', async ({ baseURL }) => {
2929
});
3030
});
3131

32-
test('Does not send expected exception to Sentry', async ({ baseURL }) => {
32+
test('Does not send HttpExceptions to Sentry', async ({ baseURL }) => {
3333
let errorEventOccurred = false;
3434

3535
waitForError('nestjs', event => {
36-
if (!event.type && event.exception?.values?.[0]?.value === 'This is an expected exception with id 123') {
36+
if (!event.type && event.exception?.values?.[0]?.value === 'This is an expected 400 exception with id 123') {
3737
errorEventOccurred = true;
3838
}
3939

40-
return event?.transaction === 'GET /test-expected-exception/:id';
40+
return event?.transaction === 'GET /test-expected-400-exception/:id';
4141
});
4242

43-
const transactionEventPromise = waitForTransaction('nestjs', transactionEvent => {
44-
return transactionEvent?.transaction === 'GET /test-expected-exception/:id';
43+
waitForError('nestjs', event => {
44+
if (!event.type && event.exception?.values?.[0]?.value === 'This is an expected 500 exception with id 123') {
45+
errorEventOccurred = true;
46+
}
47+
48+
return event?.transaction === 'GET /test-expected-500-exception/:id';
4549
});
4650

47-
const response = await fetch(`${baseURL}/test-expected-exception/123`);
48-
expect(response.status).toBe(403);
51+
const transactionEventPromise400 = waitForTransaction('nestjs', transactionEvent => {
52+
return transactionEvent?.transaction === 'GET /test-expected-400-exception/:id';
53+
});
54+
55+
const transactionEventPromise500 = waitForTransaction('nestjs', transactionEvent => {
56+
return transactionEvent?.transaction === 'GET /test-expected-500-exception/:id';
57+
});
58+
59+
const response400 = await fetch(`${baseURL}/test-expected-400-exception/123`);
60+
expect(response400.status).toBe(400);
61+
62+
const response500 = await fetch(`${baseURL}/test-expected-500-exception/123`);
63+
expect(response500.status).toBe(500);
4964

50-
await transactionEventPromise;
65+
await transactionEventPromise400;
66+
await transactionEventPromise500;
5167

5268
await new Promise(resolve => setTimeout(resolve, 10000));
5369

dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/app.controller.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,14 @@ export class AppController {
2020
return this.appService.testException(id);
2121
}
2222

23-
@Get('test-expected-exception/:id')
24-
async testExpectedException(@Param('id') id: string) {
25-
return this.appService.testExpectedException(id);
23+
@Get('test-expected-400-exception/:id')
24+
async testExpected400Exception(@Param('id') id: string) {
25+
return this.appService.testExpected400Exception(id);
26+
}
27+
28+
@Get('test-expected-500-exception/:id')
29+
async testExpected500Exception(@Param('id') id: string) {
30+
return this.appService.testExpected500Exception(id);
2631
}
2732

2833
@Get('test-span-decorator-async')

dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/app.service.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,12 @@ export class AppService {
3030
throw new Error(`This is an exception with id ${id}`);
3131
}
3232

33-
testExpectedException(id: string) {
34-
throw new HttpException(`This is an expected exception with id ${id}`, HttpStatus.FORBIDDEN);
33+
testExpected400Exception(id: string) {
34+
throw new HttpException(`This is an expected 400 exception with id ${id}`, HttpStatus.BAD_REQUEST);
35+
}
36+
37+
testExpected500Exception(id: string) {
38+
throw new HttpException(`This is an expected 500 exception with id ${id}`, HttpStatus.INTERNAL_SERVER_ERROR);
3539
}
3640

3741
@SentryTraced('wait and return a string')

dev-packages/e2e-tests/test-applications/node-nestjs-basic/tests/errors.test.ts

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,25 +29,41 @@ test('Sends exception to Sentry', async ({ baseURL }) => {
2929
});
3030
});
3131

32-
test('Does not send expected exception to Sentry', async ({ baseURL }) => {
32+
test('Does not send HttpExceptions to Sentry', async ({ baseURL }) => {
3333
let errorEventOccurred = false;
3434

3535
waitForError('nestjs', event => {
36-
if (!event.type && event.exception?.values?.[0]?.value === 'This is an expected exception with id 123') {
36+
if (!event.type && event.exception?.values?.[0]?.value === 'This is an expected 400 exception with id 123') {
3737
errorEventOccurred = true;
3838
}
3939

40-
return event?.transaction === 'GET /test-expected-exception/:id';
40+
return event?.transaction === 'GET /test-expected-400-exception/:id';
4141
});
4242

43-
const transactionEventPromise = waitForTransaction('nestjs', transactionEvent => {
44-
return transactionEvent?.transaction === 'GET /test-expected-exception/:id';
43+
waitForError('nestjs', event => {
44+
if (!event.type && event.exception?.values?.[0]?.value === 'This is an expected 500 exception with id 123') {
45+
errorEventOccurred = true;
46+
}
47+
48+
return event?.transaction === 'GET /test-expected-500-exception/:id';
4549
});
4650

47-
const response = await fetch(`${baseURL}/test-expected-exception/123`);
48-
expect(response.status).toBe(403);
51+
const transactionEventPromise400 = waitForTransaction('nestjs', transactionEvent => {
52+
return transactionEvent?.transaction === 'GET /test-expected-400-exception/:id';
53+
});
54+
55+
const transactionEventPromise500 = waitForTransaction('nestjs', transactionEvent => {
56+
return transactionEvent?.transaction === 'GET /test-expected-500-exception/:id';
57+
});
58+
59+
const response400 = await fetch(`${baseURL}/test-expected-400-exception/123`);
60+
expect(response400.status).toBe(400);
61+
62+
const response500 = await fetch(`${baseURL}/test-expected-500-exception/123`);
63+
expect(response500.status).toBe(500);
4964

50-
await transactionEventPromise;
65+
await transactionEventPromise400;
66+
await transactionEventPromise500;
5167

5268
await new Promise(resolve => setTimeout(resolve, 10000));
5369

packages/nestjs/src/setup.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import type {
66
NestInterceptor,
77
OnModuleInit,
88
} from '@nestjs/common';
9+
import { HttpException } from '@nestjs/common';
910
import { Catch } from '@nestjs/common';
1011
import { Injectable } from '@nestjs/common';
1112
import { Global, Module } from '@nestjs/common';
@@ -63,10 +64,8 @@ class SentryGlobalFilter extends BaseExceptionFilter {
6364
* Catches exceptions and reports them to Sentry unless they are expected errors.
6465
*/
6566
public catch(exception: unknown, host: ArgumentsHost): void {
66-
const status_code = (exception as { status?: number }).status;
67-
6867
// don't report expected errors
69-
if (status_code !== undefined && status_code >= 400 && status_code < 500) {
68+
if (exception instanceof HttpException) {
7069
return super.catch(exception, host);
7170
}
7271

packages/node/src/integrations/tracing/nest.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ export function setupNestErrorHandler(app: MinimalNestJsApp, baseFilter: NestJsE
262262
const status_code = (exception as { status?: number }).status;
263263

264264
// don't report expected errors
265-
if (status_code !== undefined && status_code >= 400 && status_code < 500) {
265+
if (status_code !== undefined) {
266266
return originalCatch.apply(target, [exception, host]);
267267
}
268268

0 commit comments

Comments
 (0)