Skip to content

Commit eb1c1cd

Browse files
authored
fix(nextjs): Use preprocessEvent hook to improve span data (#14007)
We use the preprocessEvent hook to pass the most up to date data to users event processors and also not to trigger the logic that sets the transaction source to custom when the transaction name is updated in event processors.
1 parent a4bb383 commit eb1c1cd

File tree

1 file changed

+64
-64
lines changed

1 file changed

+64
-64
lines changed

packages/nextjs/src/server/index.ts

+64-64
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,22 @@ export function init(options: NodeOptions): NodeClient | undefined {
242242
return null;
243243
}
244244

245+
// Next.js 13 sometimes names the root transactions like this containing useless tracing.
246+
if (event.transaction === 'NextServer.getRequestHandler') {
247+
return null;
248+
}
249+
250+
// Next.js 13 is not correctly picking up tracing data for trace propagation so we use a back-fill strategy
251+
if (typeof event.contexts?.trace?.data?.[TRANSACTION_ATTR_SENTRY_TRACE_BACKFILL] === 'string') {
252+
const traceparentData = extractTraceparentData(
253+
event.contexts.trace.data[TRANSACTION_ATTR_SENTRY_TRACE_BACKFILL],
254+
);
255+
256+
if (traceparentData?.parentSampled === false) {
257+
return null;
258+
}
259+
}
260+
245261
return event;
246262
} else {
247263
return event;
@@ -286,78 +302,62 @@ export function init(options: NodeOptions): NodeClient | undefined {
286302
),
287303
);
288304

289-
// TODO: move this into pre-processing hook
290-
getGlobalScope().addEventProcessor(
291-
Object.assign(
292-
(event => {
293-
// Enhance route handler transactions
294-
if (
295-
event.type === 'transaction' &&
296-
event.contexts?.trace?.data?.['next.span_type'] === 'BaseServer.handleRequest'
297-
) {
298-
event.contexts.trace.data = event.contexts.trace.data || {};
299-
event.contexts.trace.data[SEMANTIC_ATTRIBUTE_SENTRY_OP] = 'http.server';
300-
event.contexts.trace.op = 'http.server';
301-
302-
if (event.transaction) {
303-
event.transaction = stripUrlQueryAndFragment(event.transaction);
304-
}
305-
306-
// eslint-disable-next-line deprecation/deprecation
307-
const method = event.contexts.trace.data[SEMATTRS_HTTP_METHOD];
308-
// eslint-disable-next-line deprecation/deprecation
309-
const target = event.contexts?.trace?.data?.[SEMATTRS_HTTP_TARGET];
310-
const route = event.contexts.trace.data[ATTR_HTTP_ROUTE];
311-
312-
if (typeof method === 'string' && typeof route === 'string') {
313-
event.transaction = `${method} ${route.replace(/\/route$/, '')}`;
314-
event.contexts.trace.data[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE] = 'route';
315-
}
305+
// Use the preprocessEvent hook instead of an event processor, so that the users event processors receive the most
306+
// up-to-date value, but also so that the logic that detects changes to the transaction names to set the source to
307+
// "custom", doesn't trigger.
308+
client?.on('preprocessEvent', event => {
309+
// Enhance route handler transactions
310+
if (
311+
event.type === 'transaction' &&
312+
event.contexts?.trace?.data?.['next.span_type'] === 'BaseServer.handleRequest'
313+
) {
314+
event.contexts.trace.data = event.contexts.trace.data || {};
315+
event.contexts.trace.data[SEMANTIC_ATTRIBUTE_SENTRY_OP] = 'http.server';
316+
event.contexts.trace.op = 'http.server';
316317

317-
// backfill transaction name for pages that would otherwise contain unparameterized routes
318-
if (event.contexts.trace.data['sentry.route_backfill'] && event.transaction !== 'GET /_app') {
319-
event.transaction = `${method} ${event.contexts.trace.data[TRANSACTION_ATTR_SENTRY_ROUTE_BACKFILL]}`;
320-
}
318+
if (event.transaction) {
319+
event.transaction = stripUrlQueryAndFragment(event.transaction);
320+
}
321321

322-
// Next.js overrides transaction names for page loads that throw an error
323-
// but we want to keep the original target name
324-
if (event.transaction === 'GET /_error' && target) {
325-
event.transaction = `${method ? `${method} ` : ''}${target}`;
326-
}
327-
}
322+
// eslint-disable-next-line deprecation/deprecation
323+
const method = event.contexts.trace.data[SEMATTRS_HTTP_METHOD];
324+
// eslint-disable-next-line deprecation/deprecation
325+
const target = event.contexts?.trace?.data?.[SEMATTRS_HTTP_TARGET];
326+
const route = event.contexts.trace.data[ATTR_HTTP_ROUTE];
328327

329-
// Next.js 13 is not correctly picking up tracing data for trace propagation so we use a back-fill strategy
330-
if (
331-
event.type === 'transaction' &&
332-
typeof event.contexts?.trace?.data?.[TRANSACTION_ATTR_SENTRY_TRACE_BACKFILL] === 'string'
333-
) {
334-
const traceparentData = extractTraceparentData(
335-
event.contexts.trace.data[TRANSACTION_ATTR_SENTRY_TRACE_BACKFILL],
336-
);
328+
if (typeof method === 'string' && typeof route === 'string') {
329+
event.transaction = `${method} ${route.replace(/\/route$/, '')}`;
330+
event.contexts.trace.data[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE] = 'route';
331+
}
337332

338-
if (traceparentData?.parentSampled === false) {
339-
return null;
340-
}
333+
// backfill transaction name for pages that would otherwise contain unparameterized routes
334+
if (event.contexts.trace.data['sentry.route_backfill'] && event.transaction !== 'GET /_app') {
335+
event.transaction = `${method} ${event.contexts.trace.data[TRANSACTION_ATTR_SENTRY_ROUTE_BACKFILL]}`;
336+
}
341337

342-
if (traceparentData?.traceId) {
343-
event.contexts.trace.trace_id = traceparentData.traceId;
344-
}
338+
// Next.js overrides transaction names for page loads that throw an error
339+
// but we want to keep the original target name
340+
if (event.transaction === 'GET /_error' && target) {
341+
event.transaction = `${method ? `${method} ` : ''}${target}`;
342+
}
343+
}
345344

346-
if (traceparentData?.parentSpanId) {
347-
event.contexts.trace.parent_span_id = traceparentData.parentSpanId;
348-
}
349-
}
345+
// Next.js 13 is not correctly picking up tracing data for trace propagation so we use a back-fill strategy
346+
if (
347+
event.type === 'transaction' &&
348+
typeof event.contexts?.trace?.data?.[TRANSACTION_ATTR_SENTRY_TRACE_BACKFILL] === 'string'
349+
) {
350+
const traceparentData = extractTraceparentData(event.contexts.trace.data[TRANSACTION_ATTR_SENTRY_TRACE_BACKFILL]);
350351

351-
// Next.js 13 sometimes names the root transactions like this containing useless tracing.
352-
if (event.type === 'transaction' && event.transaction === 'NextServer.getRequestHandler') {
353-
return null;
354-
}
352+
if (traceparentData?.traceId) {
353+
event.contexts.trace.trace_id = traceparentData.traceId;
354+
}
355355

356-
return event;
357-
}) satisfies EventProcessor,
358-
{ id: 'NextjsTransactionEnhancer' },
359-
),
360-
);
356+
if (traceparentData?.parentSpanId) {
357+
event.contexts.trace.parent_span_id = traceparentData.parentSpanId;
358+
}
359+
}
360+
});
361361

362362
if (process.env.NODE_ENV === 'development') {
363363
getGlobalScope().addEventProcessor(devErrorSymbolicationEventProcessor);

0 commit comments

Comments
 (0)