From bb17d2e71ec3d85ba5a60be1a87cf95a72d57091 Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Wed, 11 Mar 2026 09:11:07 +0100 Subject: [PATCH] feat(nestjs): Use more specific span origins for NestJS guards, pipes, interceptors, and exception filters --- .../nestjs-11/tests/transactions.test.ts | 32 ++--- .../nestjs-8/tests/transactions.test.ts | 32 ++--- .../nestjs-basic/tests/transactions.test.ts | 32 ++--- .../nestjs-fastify/tests/transactions.test.ts | 32 ++--- .../tests/transactions.test.ts | 12 +- .../tests/transactions.test.ts | 12 +- packages/nestjs/src/integrations/helpers.ts | 9 +- .../sentry-nest-instrumentation.ts | 120 ++++++++++-------- 8 files changed, 147 insertions(+), 134 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/nestjs-11/tests/transactions.test.ts b/dev-packages/e2e-tests/test-applications/nestjs-11/tests/transactions.test.ts index d0f34311822e..a8b8f25e46c1 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs-11/tests/transactions.test.ts +++ b/dev-packages/e2e-tests/test-applications/nestjs-11/tests/transactions.test.ts @@ -232,7 +232,7 @@ test('API route transaction includes nest guard span and span started in guard i trace_id: expect.stringMatching(/[a-f0-9]{32}/), data: { 'sentry.op': 'middleware.nestjs', - 'sentry.origin': 'auto.middleware.nestjs', + 'sentry.origin': 'auto.middleware.nestjs.guard', }, description: 'ExampleGuard', parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), @@ -240,7 +240,7 @@ test('API route transaction includes nest guard span and span started in guard i timestamp: expect.any(Number), status: 'ok', op: 'middleware.nestjs', - origin: 'auto.middleware.nestjs', + origin: 'auto.middleware.nestjs.guard', }, ]), }), @@ -296,7 +296,7 @@ test('API route transaction includes nest pipe span for valid request', async ({ trace_id: expect.stringMatching(/[a-f0-9]{32}/), data: { 'sentry.op': 'middleware.nestjs', - 'sentry.origin': 'auto.middleware.nestjs', + 'sentry.origin': 'auto.middleware.nestjs.pipe', }, description: 'ParseIntPipe', parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), @@ -304,7 +304,7 @@ test('API route transaction includes nest pipe span for valid request', async ({ timestamp: expect.any(Number), status: 'ok', op: 'middleware.nestjs', - origin: 'auto.middleware.nestjs', + origin: 'auto.middleware.nestjs.pipe', }, ]), }), @@ -333,7 +333,7 @@ test('API route transaction includes nest pipe span for invalid request', async trace_id: expect.stringMatching(/[a-f0-9]{32}/), data: { 'sentry.op': 'middleware.nestjs', - 'sentry.origin': 'auto.middleware.nestjs', + 'sentry.origin': 'auto.middleware.nestjs.pipe', }, description: 'ParseIntPipe', parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), @@ -341,7 +341,7 @@ test('API route transaction includes nest pipe span for invalid request', async timestamp: expect.any(Number), status: 'internal_error', op: 'middleware.nestjs', - origin: 'auto.middleware.nestjs', + origin: 'auto.middleware.nestjs.pipe', }, ]), }), @@ -372,7 +372,7 @@ test('API route transaction includes nest interceptor spans before route executi trace_id: expect.stringMatching(/[a-f0-9]{32}/), data: { 'sentry.op': 'middleware.nestjs', - 'sentry.origin': 'auto.middleware.nestjs', + 'sentry.origin': 'auto.middleware.nestjs.interceptor', }, description: 'ExampleInterceptor1', parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), @@ -380,14 +380,14 @@ test('API route transaction includes nest interceptor spans before route executi timestamp: expect.any(Number), status: 'ok', op: 'middleware.nestjs', - origin: 'auto.middleware.nestjs', + origin: 'auto.middleware.nestjs.interceptor', }, { span_id: expect.stringMatching(/[a-f0-9]{16}/), trace_id: expect.stringMatching(/[a-f0-9]{32}/), data: { 'sentry.op': 'middleware.nestjs', - 'sentry.origin': 'auto.middleware.nestjs', + 'sentry.origin': 'auto.middleware.nestjs.interceptor', }, description: 'ExampleInterceptor2', parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), @@ -395,7 +395,7 @@ test('API route transaction includes nest interceptor spans before route executi timestamp: expect.any(Number), status: 'ok', op: 'middleware.nestjs', - origin: 'auto.middleware.nestjs', + origin: 'auto.middleware.nestjs.interceptor', }, ]), }), @@ -490,7 +490,7 @@ test('API route transaction includes exactly one nest interceptor span after rou trace_id: expect.stringMatching(/[a-f0-9]{32}/), data: { 'sentry.op': 'middleware.nestjs', - 'sentry.origin': 'auto.middleware.nestjs', + 'sentry.origin': 'auto.middleware.nestjs.interceptor', }, description: 'Interceptors - After Route', parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), @@ -498,7 +498,7 @@ test('API route transaction includes exactly one nest interceptor span after rou timestamp: expect.any(Number), status: 'ok', op: 'middleware.nestjs', - origin: 'auto.middleware.nestjs', + origin: 'auto.middleware.nestjs.interceptor', }, ]), }), @@ -572,7 +572,7 @@ test('API route transaction includes nest async interceptor spans before route e trace_id: expect.stringMatching(/[a-f0-9]{32}/), data: { 'sentry.op': 'middleware.nestjs', - 'sentry.origin': 'auto.middleware.nestjs', + 'sentry.origin': 'auto.middleware.nestjs.interceptor', }, description: 'AsyncInterceptor', parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), @@ -580,7 +580,7 @@ test('API route transaction includes nest async interceptor spans before route e timestamp: expect.any(Number), status: 'ok', op: 'middleware.nestjs', - origin: 'auto.middleware.nestjs', + origin: 'auto.middleware.nestjs.interceptor', }, ]), }), @@ -657,7 +657,7 @@ test('API route transaction includes exactly one nest async interceptor span aft trace_id: expect.stringMatching(/[a-f0-9]{32}/), data: { 'sentry.op': 'middleware.nestjs', - 'sentry.origin': 'auto.middleware.nestjs', + 'sentry.origin': 'auto.middleware.nestjs.interceptor', }, description: 'Interceptors - After Route', parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), @@ -665,7 +665,7 @@ test('API route transaction includes exactly one nest async interceptor span aft timestamp: expect.any(Number), status: 'ok', op: 'middleware.nestjs', - origin: 'auto.middleware.nestjs', + origin: 'auto.middleware.nestjs.interceptor', }, ]), }), diff --git a/dev-packages/e2e-tests/test-applications/nestjs-8/tests/transactions.test.ts b/dev-packages/e2e-tests/test-applications/nestjs-8/tests/transactions.test.ts index f5f1c64a9726..862f730636c0 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs-8/tests/transactions.test.ts +++ b/dev-packages/e2e-tests/test-applications/nestjs-8/tests/transactions.test.ts @@ -236,7 +236,7 @@ test('API route transaction includes nest guard span and span started in guard i trace_id: expect.stringMatching(/[a-f0-9]{32}/), data: { 'sentry.op': 'middleware.nestjs', - 'sentry.origin': 'auto.middleware.nestjs', + 'sentry.origin': 'auto.middleware.nestjs.guard', }, description: 'ExampleGuard', parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), @@ -244,7 +244,7 @@ test('API route transaction includes nest guard span and span started in guard i timestamp: expect.any(Number), status: 'ok', op: 'middleware.nestjs', - origin: 'auto.middleware.nestjs', + origin: 'auto.middleware.nestjs.guard', }, ]), }), @@ -300,7 +300,7 @@ test('API route transaction includes nest pipe span for valid request', async ({ trace_id: expect.stringMatching(/[a-f0-9]{32}/), data: { 'sentry.op': 'middleware.nestjs', - 'sentry.origin': 'auto.middleware.nestjs', + 'sentry.origin': 'auto.middleware.nestjs.pipe', }, description: 'ParseIntPipe', parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), @@ -308,7 +308,7 @@ test('API route transaction includes nest pipe span for valid request', async ({ timestamp: expect.any(Number), status: 'ok', op: 'middleware.nestjs', - origin: 'auto.middleware.nestjs', + origin: 'auto.middleware.nestjs.pipe', }, ]), }), @@ -337,7 +337,7 @@ test('API route transaction includes nest pipe span for invalid request', async trace_id: expect.stringMatching(/[a-f0-9]{32}/), data: { 'sentry.op': 'middleware.nestjs', - 'sentry.origin': 'auto.middleware.nestjs', + 'sentry.origin': 'auto.middleware.nestjs.pipe', }, description: 'ParseIntPipe', parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), @@ -345,7 +345,7 @@ test('API route transaction includes nest pipe span for invalid request', async timestamp: expect.any(Number), status: 'internal_error', op: 'middleware.nestjs', - origin: 'auto.middleware.nestjs', + origin: 'auto.middleware.nestjs.pipe', }, ]), }), @@ -376,7 +376,7 @@ test('API route transaction includes nest interceptor spans before route executi trace_id: expect.stringMatching(/[a-f0-9]{32}/), data: { 'sentry.op': 'middleware.nestjs', - 'sentry.origin': 'auto.middleware.nestjs', + 'sentry.origin': 'auto.middleware.nestjs.interceptor', }, description: 'ExampleInterceptor1', parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), @@ -384,14 +384,14 @@ test('API route transaction includes nest interceptor spans before route executi timestamp: expect.any(Number), status: 'ok', op: 'middleware.nestjs', - origin: 'auto.middleware.nestjs', + origin: 'auto.middleware.nestjs.interceptor', }, { span_id: expect.stringMatching(/[a-f0-9]{16}/), trace_id: expect.stringMatching(/[a-f0-9]{32}/), data: { 'sentry.op': 'middleware.nestjs', - 'sentry.origin': 'auto.middleware.nestjs', + 'sentry.origin': 'auto.middleware.nestjs.interceptor', }, description: 'ExampleInterceptor2', parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), @@ -399,7 +399,7 @@ test('API route transaction includes nest interceptor spans before route executi timestamp: expect.any(Number), status: 'ok', op: 'middleware.nestjs', - origin: 'auto.middleware.nestjs', + origin: 'auto.middleware.nestjs.interceptor', }, ]), }), @@ -494,7 +494,7 @@ test('API route transaction includes exactly one nest interceptor span after rou trace_id: expect.stringMatching(/[a-f0-9]{32}/), data: { 'sentry.op': 'middleware.nestjs', - 'sentry.origin': 'auto.middleware.nestjs', + 'sentry.origin': 'auto.middleware.nestjs.interceptor', }, description: 'Interceptors - After Route', parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), @@ -502,7 +502,7 @@ test('API route transaction includes exactly one nest interceptor span after rou timestamp: expect.any(Number), status: 'ok', op: 'middleware.nestjs', - origin: 'auto.middleware.nestjs', + origin: 'auto.middleware.nestjs.interceptor', }, ]), }), @@ -576,7 +576,7 @@ test('API route transaction includes nest async interceptor spans before route e trace_id: expect.stringMatching(/[a-f0-9]{32}/), data: { 'sentry.op': 'middleware.nestjs', - 'sentry.origin': 'auto.middleware.nestjs', + 'sentry.origin': 'auto.middleware.nestjs.interceptor', }, description: 'AsyncInterceptor', parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), @@ -584,7 +584,7 @@ test('API route transaction includes nest async interceptor spans before route e timestamp: expect.any(Number), status: 'ok', op: 'middleware.nestjs', - origin: 'auto.middleware.nestjs', + origin: 'auto.middleware.nestjs.interceptor', }, ]), }), @@ -661,7 +661,7 @@ test('API route transaction includes exactly one nest async interceptor span aft trace_id: expect.stringMatching(/[a-f0-9]{32}/), data: { 'sentry.op': 'middleware.nestjs', - 'sentry.origin': 'auto.middleware.nestjs', + 'sentry.origin': 'auto.middleware.nestjs.interceptor', }, description: 'Interceptors - After Route', parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), @@ -669,7 +669,7 @@ test('API route transaction includes exactly one nest async interceptor span aft timestamp: expect.any(Number), status: 'ok', op: 'middleware.nestjs', - origin: 'auto.middleware.nestjs', + origin: 'auto.middleware.nestjs.interceptor', }, ]), }), diff --git a/dev-packages/e2e-tests/test-applications/nestjs-basic/tests/transactions.test.ts b/dev-packages/e2e-tests/test-applications/nestjs-basic/tests/transactions.test.ts index 508c1e670946..440a1391556a 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs-basic/tests/transactions.test.ts +++ b/dev-packages/e2e-tests/test-applications/nestjs-basic/tests/transactions.test.ts @@ -232,7 +232,7 @@ test('API route transaction includes nest guard span and span started in guard i trace_id: expect.stringMatching(/[a-f0-9]{32}/), data: { 'sentry.op': 'middleware.nestjs', - 'sentry.origin': 'auto.middleware.nestjs', + 'sentry.origin': 'auto.middleware.nestjs.guard', }, description: 'ExampleGuard', parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), @@ -240,7 +240,7 @@ test('API route transaction includes nest guard span and span started in guard i timestamp: expect.any(Number), status: 'ok', op: 'middleware.nestjs', - origin: 'auto.middleware.nestjs', + origin: 'auto.middleware.nestjs.guard', }, ]), }), @@ -296,7 +296,7 @@ test('API route transaction includes nest pipe span for valid request', async ({ trace_id: expect.stringMatching(/[a-f0-9]{32}/), data: { 'sentry.op': 'middleware.nestjs', - 'sentry.origin': 'auto.middleware.nestjs', + 'sentry.origin': 'auto.middleware.nestjs.pipe', }, description: 'ParseIntPipe', parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), @@ -304,7 +304,7 @@ test('API route transaction includes nest pipe span for valid request', async ({ timestamp: expect.any(Number), status: 'ok', op: 'middleware.nestjs', - origin: 'auto.middleware.nestjs', + origin: 'auto.middleware.nestjs.pipe', }, ]), }), @@ -333,7 +333,7 @@ test('API route transaction includes nest pipe span for invalid request', async trace_id: expect.stringMatching(/[a-f0-9]{32}/), data: { 'sentry.op': 'middleware.nestjs', - 'sentry.origin': 'auto.middleware.nestjs', + 'sentry.origin': 'auto.middleware.nestjs.pipe', }, description: 'ParseIntPipe', parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), @@ -341,7 +341,7 @@ test('API route transaction includes nest pipe span for invalid request', async timestamp: expect.any(Number), status: 'internal_error', op: 'middleware.nestjs', - origin: 'auto.middleware.nestjs', + origin: 'auto.middleware.nestjs.pipe', }, ]), }), @@ -372,7 +372,7 @@ test('API route transaction includes nest interceptor spans before route executi trace_id: expect.stringMatching(/[a-f0-9]{32}/), data: { 'sentry.op': 'middleware.nestjs', - 'sentry.origin': 'auto.middleware.nestjs', + 'sentry.origin': 'auto.middleware.nestjs.interceptor', }, description: 'ExampleInterceptor1', parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), @@ -380,14 +380,14 @@ test('API route transaction includes nest interceptor spans before route executi timestamp: expect.any(Number), status: 'ok', op: 'middleware.nestjs', - origin: 'auto.middleware.nestjs', + origin: 'auto.middleware.nestjs.interceptor', }, { span_id: expect.stringMatching(/[a-f0-9]{16}/), trace_id: expect.stringMatching(/[a-f0-9]{32}/), data: { 'sentry.op': 'middleware.nestjs', - 'sentry.origin': 'auto.middleware.nestjs', + 'sentry.origin': 'auto.middleware.nestjs.interceptor', }, description: 'ExampleInterceptor2', parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), @@ -395,7 +395,7 @@ test('API route transaction includes nest interceptor spans before route executi timestamp: expect.any(Number), status: 'ok', op: 'middleware.nestjs', - origin: 'auto.middleware.nestjs', + origin: 'auto.middleware.nestjs.interceptor', }, ]), }), @@ -490,7 +490,7 @@ test('API route transaction includes exactly one nest interceptor span after rou trace_id: expect.stringMatching(/[a-f0-9]{32}/), data: { 'sentry.op': 'middleware.nestjs', - 'sentry.origin': 'auto.middleware.nestjs', + 'sentry.origin': 'auto.middleware.nestjs.interceptor', }, description: 'Interceptors - After Route', parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), @@ -498,7 +498,7 @@ test('API route transaction includes exactly one nest interceptor span after rou timestamp: expect.any(Number), status: 'ok', op: 'middleware.nestjs', - origin: 'auto.middleware.nestjs', + origin: 'auto.middleware.nestjs.interceptor', }, ]), }), @@ -572,7 +572,7 @@ test('API route transaction includes nest async interceptor spans before route e trace_id: expect.stringMatching(/[a-f0-9]{32}/), data: { 'sentry.op': 'middleware.nestjs', - 'sentry.origin': 'auto.middleware.nestjs', + 'sentry.origin': 'auto.middleware.nestjs.interceptor', }, description: 'AsyncInterceptor', parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), @@ -580,7 +580,7 @@ test('API route transaction includes nest async interceptor spans before route e timestamp: expect.any(Number), status: 'ok', op: 'middleware.nestjs', - origin: 'auto.middleware.nestjs', + origin: 'auto.middleware.nestjs.interceptor', }, ]), }), @@ -657,7 +657,7 @@ test('API route transaction includes exactly one nest async interceptor span aft trace_id: expect.stringMatching(/[a-f0-9]{32}/), data: { 'sentry.op': 'middleware.nestjs', - 'sentry.origin': 'auto.middleware.nestjs', + 'sentry.origin': 'auto.middleware.nestjs.interceptor', }, description: 'Interceptors - After Route', parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), @@ -665,7 +665,7 @@ test('API route transaction includes exactly one nest async interceptor span aft timestamp: expect.any(Number), status: 'ok', op: 'middleware.nestjs', - origin: 'auto.middleware.nestjs', + origin: 'auto.middleware.nestjs.interceptor', }, ]), }), diff --git a/dev-packages/e2e-tests/test-applications/nestjs-fastify/tests/transactions.test.ts b/dev-packages/e2e-tests/test-applications/nestjs-fastify/tests/transactions.test.ts index 093375e11e06..43d3afc63468 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs-fastify/tests/transactions.test.ts +++ b/dev-packages/e2e-tests/test-applications/nestjs-fastify/tests/transactions.test.ts @@ -276,7 +276,7 @@ test('API route transaction includes nest guard span and span started in guard i trace_id: expect.stringMatching(/[a-f0-9]{32}/), data: { 'sentry.op': 'middleware.nestjs', - 'sentry.origin': 'auto.middleware.nestjs', + 'sentry.origin': 'auto.middleware.nestjs.guard', }, description: 'ExampleGuard', parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), @@ -284,7 +284,7 @@ test('API route transaction includes nest guard span and span started in guard i timestamp: expect.any(Number), status: 'ok', op: 'middleware.nestjs', - origin: 'auto.middleware.nestjs', + origin: 'auto.middleware.nestjs.guard', }, ]), }), @@ -340,7 +340,7 @@ test('API route transaction includes nest pipe span for valid request', async ({ trace_id: expect.stringMatching(/[a-f0-9]{32}/), data: { 'sentry.op': 'middleware.nestjs', - 'sentry.origin': 'auto.middleware.nestjs', + 'sentry.origin': 'auto.middleware.nestjs.pipe', }, description: 'ParseIntPipe', parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), @@ -348,7 +348,7 @@ test('API route transaction includes nest pipe span for valid request', async ({ timestamp: expect.any(Number), status: 'ok', op: 'middleware.nestjs', - origin: 'auto.middleware.nestjs', + origin: 'auto.middleware.nestjs.pipe', }, ]), }), @@ -377,7 +377,7 @@ test('API route transaction includes nest pipe span for invalid request', async trace_id: expect.stringMatching(/[a-f0-9]{32}/), data: { 'sentry.op': 'middleware.nestjs', - 'sentry.origin': 'auto.middleware.nestjs', + 'sentry.origin': 'auto.middleware.nestjs.pipe', }, description: 'ParseIntPipe', parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), @@ -385,7 +385,7 @@ test('API route transaction includes nest pipe span for invalid request', async timestamp: expect.any(Number), status: 'internal_error', op: 'middleware.nestjs', - origin: 'auto.middleware.nestjs', + origin: 'auto.middleware.nestjs.pipe', }, ]), }), @@ -416,7 +416,7 @@ test('API route transaction includes nest interceptor spans before route executi trace_id: expect.stringMatching(/[a-f0-9]{32}/), data: { 'sentry.op': 'middleware.nestjs', - 'sentry.origin': 'auto.middleware.nestjs', + 'sentry.origin': 'auto.middleware.nestjs.interceptor', }, description: 'ExampleInterceptor1', parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), @@ -424,14 +424,14 @@ test('API route transaction includes nest interceptor spans before route executi timestamp: expect.any(Number), status: 'ok', op: 'middleware.nestjs', - origin: 'auto.middleware.nestjs', + origin: 'auto.middleware.nestjs.interceptor', }, { span_id: expect.stringMatching(/[a-f0-9]{16}/), trace_id: expect.stringMatching(/[a-f0-9]{32}/), data: { 'sentry.op': 'middleware.nestjs', - 'sentry.origin': 'auto.middleware.nestjs', + 'sentry.origin': 'auto.middleware.nestjs.interceptor', }, description: 'ExampleInterceptor2', parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), @@ -439,7 +439,7 @@ test('API route transaction includes nest interceptor spans before route executi timestamp: expect.any(Number), status: 'ok', op: 'middleware.nestjs', - origin: 'auto.middleware.nestjs', + origin: 'auto.middleware.nestjs.interceptor', }, ]), }), @@ -534,7 +534,7 @@ test('API route transaction includes exactly one nest interceptor span after rou trace_id: expect.stringMatching(/[a-f0-9]{32}/), data: { 'sentry.op': 'middleware.nestjs', - 'sentry.origin': 'auto.middleware.nestjs', + 'sentry.origin': 'auto.middleware.nestjs.interceptor', }, description: 'Interceptors - After Route', parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), @@ -542,7 +542,7 @@ test('API route transaction includes exactly one nest interceptor span after rou timestamp: expect.any(Number), status: 'ok', op: 'middleware.nestjs', - origin: 'auto.middleware.nestjs', + origin: 'auto.middleware.nestjs.interceptor', }, ]), }), @@ -616,7 +616,7 @@ test('API route transaction includes nest async interceptor spans before route e trace_id: expect.stringMatching(/[a-f0-9]{32}/), data: { 'sentry.op': 'middleware.nestjs', - 'sentry.origin': 'auto.middleware.nestjs', + 'sentry.origin': 'auto.middleware.nestjs.interceptor', }, description: 'AsyncInterceptor', parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), @@ -624,7 +624,7 @@ test('API route transaction includes nest async interceptor spans before route e timestamp: expect.any(Number), status: 'ok', op: 'middleware.nestjs', - origin: 'auto.middleware.nestjs', + origin: 'auto.middleware.nestjs.interceptor', }, ]), }), @@ -701,7 +701,7 @@ test('API route transaction includes exactly one nest async interceptor span aft trace_id: expect.stringMatching(/[a-f0-9]{32}/), data: { 'sentry.op': 'middleware.nestjs', - 'sentry.origin': 'auto.middleware.nestjs', + 'sentry.origin': 'auto.middleware.nestjs.interceptor', }, description: 'Interceptors - After Route', parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), @@ -709,7 +709,7 @@ test('API route transaction includes exactly one nest async interceptor span aft timestamp: expect.any(Number), status: 'ok', op: 'middleware.nestjs', - origin: 'auto.middleware.nestjs', + origin: 'auto.middleware.nestjs.interceptor', }, ]), }), diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/tests/transactions.test.ts b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/tests/transactions.test.ts index 77cb616450f9..380e5fdc018e 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/tests/transactions.test.ts +++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/tests/transactions.test.ts @@ -153,7 +153,7 @@ test('API route transaction includes exception filter span for global filter in trace_id: expect.stringMatching(/[a-f0-9]{32}/), data: { 'sentry.op': 'middleware.nestjs', - 'sentry.origin': 'auto.middleware.nestjs', + 'sentry.origin': 'auto.middleware.nestjs.exception_filter', }, description: 'ExampleExceptionFilter', parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), @@ -161,7 +161,7 @@ test('API route transaction includes exception filter span for global filter in timestamp: expect.any(Number), status: 'ok', op: 'middleware.nestjs', - origin: 'auto.middleware.nestjs', + origin: 'auto.middleware.nestjs.exception_filter', }, ]), }), @@ -192,7 +192,7 @@ test('API route transaction includes exception filter span for local filter in m trace_id: expect.stringMatching(/[a-f0-9]{32}/), data: { 'sentry.op': 'middleware.nestjs', - 'sentry.origin': 'auto.middleware.nestjs', + 'sentry.origin': 'auto.middleware.nestjs.exception_filter', }, description: 'LocalExampleExceptionFilter', parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), @@ -200,7 +200,7 @@ test('API route transaction includes exception filter span for local filter in m timestamp: expect.any(Number), status: 'ok', op: 'middleware.nestjs', - origin: 'auto.middleware.nestjs', + origin: 'auto.middleware.nestjs.exception_filter', }, ]), }), @@ -231,7 +231,7 @@ test('API route transaction includes exception filter span for global filter in trace_id: expect.stringMatching(/[a-f0-9]{32}/), data: { 'sentry.op': 'middleware.nestjs', - 'sentry.origin': 'auto.middleware.nestjs', + 'sentry.origin': 'auto.middleware.nestjs.exception_filter', }, description: 'ExampleExceptionFilterRegisteredFirst', parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), @@ -239,7 +239,7 @@ test('API route transaction includes exception filter span for global filter in timestamp: expect.any(Number), status: 'ok', op: 'middleware.nestjs', - origin: 'auto.middleware.nestjs', + origin: 'auto.middleware.nestjs.exception_filter', }, ]), }), diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/tests/transactions.test.ts b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/tests/transactions.test.ts index 63976a559898..416ff72e946b 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/tests/transactions.test.ts +++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/tests/transactions.test.ts @@ -153,7 +153,7 @@ test('API route transaction includes exception filter span for global filter in trace_id: expect.stringMatching(/[a-f0-9]{32}/), data: { 'sentry.op': 'middleware.nestjs', - 'sentry.origin': 'auto.middleware.nestjs', + 'sentry.origin': 'auto.middleware.nestjs.exception_filter', }, description: 'ExampleExceptionFilter', parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), @@ -161,7 +161,7 @@ test('API route transaction includes exception filter span for global filter in timestamp: expect.any(Number), status: 'ok', op: 'middleware.nestjs', - origin: 'auto.middleware.nestjs', + origin: 'auto.middleware.nestjs.exception_filter', }, ]), }), @@ -192,7 +192,7 @@ test('API route transaction includes exception filter span for local filter in m trace_id: expect.stringMatching(/[a-f0-9]{32}/), data: { 'sentry.op': 'middleware.nestjs', - 'sentry.origin': 'auto.middleware.nestjs', + 'sentry.origin': 'auto.middleware.nestjs.exception_filter', }, description: 'LocalExampleExceptionFilter', parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), @@ -200,7 +200,7 @@ test('API route transaction includes exception filter span for local filter in m timestamp: expect.any(Number), status: 'ok', op: 'middleware.nestjs', - origin: 'auto.middleware.nestjs', + origin: 'auto.middleware.nestjs.exception_filter', }, ]), }), @@ -231,7 +231,7 @@ test('API route transaction includes exception filter span for global filter in trace_id: expect.stringMatching(/[a-f0-9]{32}/), data: { 'sentry.op': 'middleware.nestjs', - 'sentry.origin': 'auto.middleware.nestjs', + 'sentry.origin': 'auto.middleware.nestjs.exception_filter', }, description: 'ExampleExceptionFilterRegisteredFirst', parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), @@ -239,7 +239,7 @@ test('API route transaction includes exception filter span for global filter in timestamp: expect.any(Number), status: 'ok', op: 'middleware.nestjs', - origin: 'auto.middleware.nestjs', + origin: 'auto.middleware.nestjs.exception_filter', }, ]), }), diff --git a/packages/nestjs/src/integrations/helpers.ts b/packages/nestjs/src/integrations/helpers.ts index 31c4e265f8f2..beb1ebb669dd 100644 --- a/packages/nestjs/src/integrations/helpers.ts +++ b/packages/nestjs/src/integrations/helpers.ts @@ -28,14 +28,19 @@ export function isPatched(target: InjectableTarget | CatchTarget): boolean { * Returns span options for nest middleware spans. */ // eslint-disable-next-line @typescript-eslint/explicit-function-return-type -export function getMiddlewareSpanOptions(target: InjectableTarget | CatchTarget, name: string | undefined = undefined) { +export function getMiddlewareSpanOptions( + target: InjectableTarget | CatchTarget, + name: string | undefined = undefined, + componentType: string | undefined = undefined, +) { const span_name = name ?? target.name; // fallback to class name if no name is provided + const origin = componentType ? `auto.middleware.nestjs.${componentType}` : 'auto.middleware.nestjs'; return { name: span_name, attributes: { [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'middleware.nestjs', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.middleware.nestjs', + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: origin, }, }; } diff --git a/packages/nestjs/src/integrations/sentry-nest-instrumentation.ts b/packages/nestjs/src/integrations/sentry-nest-instrumentation.ts index 04b20f5d4d6a..e1d7fa978020 100644 --- a/packages/nestjs/src/integrations/sentry-nest-instrumentation.ts +++ b/packages/nestjs/src/integrations/sentry-nest-instrumentation.ts @@ -140,7 +140,7 @@ export class SentryNestInstrumentation extends InstrumentationBase { return originalCanActivate.apply(thisArgCanActivate, argsCanActivate); } - return startSpan(getMiddlewareSpanOptions(target), () => { + return startSpan(getMiddlewareSpanOptions(target, undefined, 'guard'), () => { return originalCanActivate.apply(thisArgCanActivate, argsCanActivate); }); }, @@ -162,7 +162,7 @@ export class SentryNestInstrumentation extends InstrumentationBase { return originalTransform.apply(thisArgTransform, argsTransform); } - return startSpan(getMiddlewareSpanOptions(target), () => { + return startSpan(getMiddlewareSpanOptions(target, undefined, 'pipe'), () => { return originalTransform.apply(thisArgTransform, argsTransform); }); }, @@ -192,74 +192,82 @@ export class SentryNestInstrumentation extends InstrumentationBase { return originalIntercept.apply(thisArgIntercept, argsIntercept); } - return startSpanManual(getMiddlewareSpanOptions(target), (beforeSpan: Span) => { - // eslint-disable-next-line @typescript-eslint/unbound-method - next.handle = new Proxy(next.handle, { - apply: (originalHandle, thisArgHandle, argsHandle) => { - beforeSpan.end(); + return startSpanManual( + getMiddlewareSpanOptions(target, undefined, 'interceptor'), + (beforeSpan: Span) => { + // eslint-disable-next-line @typescript-eslint/unbound-method + next.handle = new Proxy(next.handle, { + apply: (originalHandle, thisArgHandle, argsHandle) => { + beforeSpan.end(); + + if (parentSpan) { + return withActiveSpan(parentSpan, () => { + const handleReturnObservable = Reflect.apply(originalHandle, thisArgHandle, argsHandle); + + if (!SeenNestjsContextSet.has(context)) { + SeenNestjsContextSet.add(context); + afterSpan = startInactiveSpan( + getMiddlewareSpanOptions(target, 'Interceptors - After Route', 'interceptor'), + ); + } - if (parentSpan) { - return withActiveSpan(parentSpan, () => { + return handleReturnObservable; + }); + } else { const handleReturnObservable = Reflect.apply(originalHandle, thisArgHandle, argsHandle); if (!SeenNestjsContextSet.has(context)) { SeenNestjsContextSet.add(context); afterSpan = startInactiveSpan( - getMiddlewareSpanOptions(target, 'Interceptors - After Route'), + getMiddlewareSpanOptions(target, 'Interceptors - After Route', 'interceptor'), ); } return handleReturnObservable; - }); - } else { - const handleReturnObservable = Reflect.apply(originalHandle, thisArgHandle, argsHandle); - - if (!SeenNestjsContextSet.has(context)) { - SeenNestjsContextSet.add(context); - afterSpan = startInactiveSpan(getMiddlewareSpanOptions(target, 'Interceptors - After Route')); } + }, + }); - return handleReturnObservable; - } - }, - }); - - let returnedObservableInterceptMaybePromise: Observable | Promise>; + let returnedObservableInterceptMaybePromise: Observable | Promise>; - try { - returnedObservableInterceptMaybePromise = originalIntercept.apply(thisArgIntercept, argsIntercept); - } catch (e) { - beforeSpan.end(); - afterSpan?.end(); - throw e; - } + try { + returnedObservableInterceptMaybePromise = originalIntercept.apply( + thisArgIntercept, + argsIntercept, + ); + } catch (e) { + beforeSpan.end(); + afterSpan?.end(); + throw e; + } + + if (!afterSpan) { + return returnedObservableInterceptMaybePromise; + } + + // handle async interceptor + if (isThenable(returnedObservableInterceptMaybePromise)) { + return returnedObservableInterceptMaybePromise.then( + observable => { + instrumentObservable(observable, afterSpan ?? parentSpan); + return observable; + }, + e => { + beforeSpan.end(); + afterSpan?.end(); + throw e; + }, + ); + } + + // handle sync interceptor + if (typeof returnedObservableInterceptMaybePromise.subscribe === 'function') { + instrumentObservable(returnedObservableInterceptMaybePromise, afterSpan); + } - if (!afterSpan) { return returnedObservableInterceptMaybePromise; - } - - // handle async interceptor - if (isThenable(returnedObservableInterceptMaybePromise)) { - return returnedObservableInterceptMaybePromise.then( - observable => { - instrumentObservable(observable, afterSpan ?? parentSpan); - return observable; - }, - e => { - beforeSpan.end(); - afterSpan?.end(); - throw e; - }, - ); - } - - // handle sync interceptor - if (typeof returnedObservableInterceptMaybePromise.subscribe === 'function') { - instrumentObservable(returnedObservableInterceptMaybePromise, afterSpan); - } - - return returnedObservableInterceptMaybePromise; - }); + }, + ); }, }); } @@ -293,7 +301,7 @@ export class SentryNestInstrumentation extends InstrumentationBase { return originalCatch.apply(thisArgCatch, argsCatch); } - return startSpan(getMiddlewareSpanOptions(target), () => { + return startSpan(getMiddlewareSpanOptions(target, undefined, 'exception_filter'), () => { return originalCatch.apply(thisArgCatch, argsCatch); }); },