From 5d889aadffecda992286b3c164a7ca495a7bde76 Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Thu, 17 Oct 2024 09:01:34 +0000 Subject: [PATCH 1/2] fix(nextjs): Use `preprocessEvent` hook to improve span data --- packages/nextjs/src/server/index.ts | 102 ++++++++++++++-------------- 1 file changed, 50 insertions(+), 52 deletions(-) diff --git a/packages/nextjs/src/server/index.ts b/packages/nextjs/src/server/index.ts index 168288e081fe..b67f7278a9f3 100644 --- a/packages/nextjs/src/server/index.ts +++ b/packages/nextjs/src/server/index.ts @@ -241,6 +241,22 @@ export function init(options: NodeOptions): NodeClient | undefined { return null; } + // Next.js 13 sometimes names the root transactions like this containing useless tracing. + if (event.transaction === 'NextServer.getRequestHandler') { + return null; + } + + // Next.js 13 is not correctly picking up tracing data for trace propagation so we use a back-fill strategy + if (typeof event.contexts?.trace?.data?.[TRANSACTION_ATTR_SENTRY_TRACE_BACKFILL] === 'string') { + const traceparentData = extractTraceparentData( + event.contexts.trace.data[TRANSACTION_ATTR_SENTRY_TRACE_BACKFILL], + ); + + if (traceparentData?.parentSampled === false) { + return null; + } + } + return event; } else { return event; @@ -285,63 +301,45 @@ export function init(options: NodeOptions): NodeClient | undefined { ), ); - getGlobalScope().addEventProcessor( - Object.assign( - (event => { - // Enhance route handler transactions - if ( - event.type === 'transaction' && - event.contexts?.trace?.data?.['next.span_type'] === 'BaseServer.handleRequest' - ) { - event.contexts.trace.data = event.contexts.trace.data || {}; - event.contexts.trace.data[SEMANTIC_ATTRIBUTE_SENTRY_OP] = 'http.server'; - event.contexts.trace.op = 'http.server'; - - if (event.transaction) { - event.transaction = stripUrlQueryAndFragment(event.transaction); - } - - // eslint-disable-next-line deprecation/deprecation - const method = event.contexts.trace.data[SEMATTRS_HTTP_METHOD]; - const route = event.contexts.trace.data[ATTR_HTTP_ROUTE]; - if (typeof method === 'string' && typeof route === 'string') { - event.transaction = `${method} ${route.replace(/\/route$/, '')}`; - event.contexts.trace.data[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE] = 'route'; - } - } - - // Next.js 13 is not correctly picking up tracing data for trace propagation so we use a back-fill strategy - if ( - event.type === 'transaction' && - typeof event.contexts?.trace?.data?.[TRANSACTION_ATTR_SENTRY_TRACE_BACKFILL] === 'string' - ) { - const traceparentData = extractTraceparentData( - event.contexts.trace.data[TRANSACTION_ATTR_SENTRY_TRACE_BACKFILL], - ); + client?.on('preprocessEvent', event => { + // Enhance route handler transactions + if ( + event.type === 'transaction' && + event.contexts?.trace?.data?.['next.span_type'] === 'BaseServer.handleRequest' + ) { + event.contexts.trace.data = event.contexts.trace.data || {}; + event.contexts.trace.data[SEMANTIC_ATTRIBUTE_SENTRY_OP] = 'http.server'; + event.contexts.trace.op = 'http.server'; - if (traceparentData?.parentSampled === false) { - return null; - } + if (event.transaction) { + event.transaction = stripUrlQueryAndFragment(event.transaction); + } - if (traceparentData?.traceId) { - event.contexts.trace.trace_id = traceparentData.traceId; - } + // eslint-disable-next-line deprecation/deprecation + const method = event.contexts.trace.data[SEMATTRS_HTTP_METHOD]; + const route = event.contexts.trace.data[ATTR_HTTP_ROUTE]; + if (typeof method === 'string' && typeof route === 'string') { + event.transaction = `${method} ${route.replace(/\/route$/, '')}`; + event.contexts.trace.data[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE] = 'route'; + } + } - if (traceparentData?.parentSpanId) { - event.contexts.trace.parent_span_id = traceparentData.parentSpanId; - } - } + // Next.js 13 is not correctly picking up tracing data for trace propagation so we use a back-fill strategy + if ( + event.type === 'transaction' && + typeof event.contexts?.trace?.data?.[TRANSACTION_ATTR_SENTRY_TRACE_BACKFILL] === 'string' + ) { + const traceparentData = extractTraceparentData(event.contexts.trace.data[TRANSACTION_ATTR_SENTRY_TRACE_BACKFILL]); - // Next.js 13 sometimes names the root transactions like this containing useless tracing. - if (event.type === 'transaction' && event.transaction === 'NextServer.getRequestHandler') { - return null; - } + if (traceparentData?.traceId) { + event.contexts.trace.trace_id = traceparentData.traceId; + } - return event; - }) satisfies EventProcessor, - { id: 'NextjsTransactionEnhancer' }, - ), - ); + if (traceparentData?.parentSpanId) { + event.contexts.trace.parent_span_id = traceparentData.parentSpanId; + } + } + }); if (process.env.NODE_ENV === 'development') { getGlobalScope().addEventProcessor(devErrorSymbolicationEventProcessor); From 9c9b9b5a085b22e35f0d67a3edf4f055f346c254 Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Thu, 17 Oct 2024 09:29:34 +0000 Subject: [PATCH 2/2] Add comment --- packages/nextjs/src/server/index.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/nextjs/src/server/index.ts b/packages/nextjs/src/server/index.ts index b67f7278a9f3..892154b3b910 100644 --- a/packages/nextjs/src/server/index.ts +++ b/packages/nextjs/src/server/index.ts @@ -301,6 +301,9 @@ export function init(options: NodeOptions): NodeClient | undefined { ), ); + // Use the preprocessEvent hook instead of an event processor, so that the users event processors receive the most + // up-to-date value, but also so that the logic that detects changes to the transaction names to set the source to + // "custom", doesn't trigger. client?.on('preprocessEvent', event => { // Enhance route handler transactions if (