Skip to content

Commit c00594a

Browse files
author
Luca Forstner
authored
ref: Disable incoming HTTP request instrumentation (#13918)
Disables the instrumentation for incoming HTTP requests to be consistent across Next.js versions for instrumentation. Next 13 and 15 work with the HTTP instrumentation. 14 doesn't.
1 parent 5c9566e commit c00594a

File tree

7 files changed

+82
-11
lines changed

7 files changed

+82
-11
lines changed

dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/client-app-routing-instrumentation.test.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,16 +42,18 @@ test('Creates a navigation transaction for app router routes', async ({ page })
4242
// It seems to differ between Next.js versions whether the route is parameterized or not
4343
(transactionEvent?.transaction === 'GET /server-component/parameter/foo/bar/baz' ||
4444
transactionEvent?.transaction === 'GET /server-component/parameter/[...parameters]') &&
45-
transactionEvent.contexts?.trace?.data?.['http.target'].startsWith('/server-component/parameter/foo/bar/baz') &&
46-
(await clientNavigationTransactionPromise).contexts?.trace?.trace_id ===
47-
transactionEvent.contexts?.trace?.trace_id
45+
transactionEvent.contexts?.trace?.data?.['http.target'].startsWith('/server-component/parameter/foo/bar/baz')
4846
);
4947
});
5048

5149
await page.getByText('/server-component/parameter/foo/bar/baz').click();
5250

5351
expect(await clientNavigationTransactionPromise).toBeDefined();
5452
expect(await serverComponentTransactionPromise).toBeDefined();
53+
54+
expect((await serverComponentTransactionPromise).contexts?.trace?.trace_id).toBe(
55+
(await clientNavigationTransactionPromise).contexts?.trace?.trace_id,
56+
);
5557
});
5658

5759
test('Creates a navigation transaction for `router.push()`', async ({ page }) => {

dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/route-handlers.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ test('Should record exceptions and transactions for faulty route handlers', asyn
5656

5757
expect(routehandlerTransaction.contexts?.trace?.status).toBe('internal_error');
5858
expect(routehandlerTransaction.contexts?.trace?.op).toBe('http.server');
59-
expect(routehandlerTransaction.contexts?.trace?.origin).toContain('auto.http.otel.http');
59+
expect(routehandlerTransaction.contexts?.trace?.origin).toContain('auto');
6060

6161
expect(routehandlerError.exception?.values?.[0].value).toBe('route-handler-error');
6262

dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/server-components.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ test('Sends a transaction for a request to app router', async ({ page }) => {
1616
expect(transactionEvent.contexts?.trace).toEqual({
1717
data: expect.objectContaining({
1818
'sentry.op': 'http.server',
19-
'sentry.origin': 'auto.http.otel.http',
19+
'sentry.origin': 'auto',
2020
'sentry.sample_rate': 1,
2121
'sentry.source': 'route',
2222
'http.method': 'GET',
@@ -27,7 +27,7 @@ test('Sends a transaction for a request to app router', async ({ page }) => {
2727
'otel.kind': 'SERVER',
2828
}),
2929
op: 'http.server',
30-
origin: 'auto.http.otel.http',
30+
origin: 'auto',
3131
span_id: expect.any(String),
3232
status: 'ok',
3333
trace_id: expect.any(String),

packages/nextjs/src/common/span-attributes-with-logic-attached.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,5 @@
22
* If this attribute is attached to a transaction, the Next.js SDK will drop that transaction.
33
*/
44
export const TRANSACTION_ATTR_SHOULD_DROP_TRANSACTION = 'sentry.drop_transaction';
5+
6+
export const TRANSACTION_ATTR_SENTRY_TRACE_BACKFILL = 'sentry.sentry_trace_backfill';

packages/nextjs/src/common/wrapGenerationFunctionWithSentry.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { propagationContextFromHeaders, uuid4, winterCGHeadersToDict } from '@se
2020
import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/core';
2121
import type { GenerationFunctionContext } from '../common/types';
2222
import { isNotFoundNavigationError, isRedirectNavigationError } from './nextNavigationErrorUtils';
23+
import { TRANSACTION_ATTR_SENTRY_TRACE_BACKFILL } from './span-attributes-with-logic-attached';
2324
import { commonObjectToIsolationScope, commonObjectToPropagationContext } from './utils/tracingUtils';
2425

2526
/**
@@ -75,6 +76,15 @@ export function wrapGenerationFunctionWithSentry<F extends (...args: any[]) => a
7576
},
7677
});
7778

79+
const activeSpan = getActiveSpan();
80+
if (activeSpan) {
81+
const rootSpan = getRootSpan(activeSpan);
82+
const sentryTrace = headersDict?.['sentry-trace'];
83+
if (sentryTrace) {
84+
rootSpan.setAttribute(TRANSACTION_ATTR_SENTRY_TRACE_BACKFILL, sentryTrace);
85+
}
86+
}
87+
7888
const propagationContext = commonObjectToPropagationContext(
7989
headers,
8090
headersDict?.['sentry-trace']

packages/nextjs/src/common/wrapServerComponentWithSentry.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { propagationContextFromHeaders, uuid4, vercelWaitUntil, winterCGHeadersT
1818
import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/core';
1919
import { isNotFoundNavigationError, isRedirectNavigationError } from '../common/nextNavigationErrorUtils';
2020
import type { ServerComponentContext } from '../common/types';
21+
import { TRANSACTION_ATTR_SENTRY_TRACE_BACKFILL } from './span-attributes-with-logic-attached';
2122
import { flushSafelyWithTimeout } from './utils/responseEnd';
2223
import { commonObjectToIsolationScope, commonObjectToPropagationContext } from './utils/tracingUtils';
2324

@@ -74,6 +75,15 @@ export function wrapServerComponentWithSentry<F extends (...args: any[]) => any>
7475
scope.setPropagationContext(propagationContext);
7576
}
7677

78+
const activeSpan = getActiveSpan();
79+
if (activeSpan) {
80+
const rootSpan = getRootSpan(activeSpan);
81+
const sentryTrace = headersDict?.['sentry-trace'];
82+
if (sentryTrace) {
83+
rootSpan.setAttribute(TRANSACTION_ATTR_SENTRY_TRACE_BACKFILL, sentryTrace);
84+
}
85+
}
86+
7787
return startSpanManual(
7888
{
7989
op: 'function.nextjs',

packages/nextjs/src/server/index.ts

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
import {
22
SEMANTIC_ATTRIBUTE_SENTRY_OP,
33
SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,
4+
SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,
45
applySdkMetadata,
56
getClient,
67
getGlobalScope,
78
getRootSpan,
89
spanToJSON,
910
} from '@sentry/core';
10-
import { getDefaultIntegrations, init as nodeInit } from '@sentry/node';
11+
import { getDefaultIntegrations, httpIntegration, init as nodeInit } from '@sentry/node';
1112
import type { NodeClient, NodeOptions } from '@sentry/node';
12-
import { GLOBAL_OBJ, logger } from '@sentry/utils';
13+
import { GLOBAL_OBJ, extractTraceparentData, logger, stripUrlQueryAndFragment } from '@sentry/utils';
1314

1415
import {
1516
ATTR_HTTP_REQUEST_METHOD,
@@ -22,7 +23,10 @@ import type { EventProcessor } from '@sentry/types';
2223
import { DEBUG_BUILD } from '../common/debug-build';
2324
import { devErrorSymbolicationEventProcessor } from '../common/devErrorSymbolicationEventProcessor';
2425
import { getVercelEnv } from '../common/getVercelEnv';
25-
import { TRANSACTION_ATTR_SHOULD_DROP_TRANSACTION } from '../common/span-attributes-with-logic-attached';
26+
import {
27+
TRANSACTION_ATTR_SENTRY_TRACE_BACKFILL,
28+
TRANSACTION_ATTR_SHOULD_DROP_TRANSACTION,
29+
} from '../common/span-attributes-with-logic-attached';
2630
import { isBuild } from '../common/utils/isBuild';
2731
import { distDirRewriteFramesIntegration } from './distDirRewriteFramesIntegration';
2832

@@ -99,7 +103,18 @@ export function init(options: NodeOptions): NodeClient | undefined {
99103
return;
100104
}
101105

102-
const customDefaultIntegrations = getDefaultIntegrations(options);
106+
const customDefaultIntegrations = getDefaultIntegrations(options)
107+
.filter(integration => integration.name !== 'Http')
108+
.concat(
109+
// We are using the HTTP integration without instrumenting incoming HTTP requests because Next.js does that by itself.
110+
httpIntegration({
111+
instrumentation: {
112+
_experimentalConfig: {
113+
disableIncomingRequestInstrumentation: true,
114+
},
115+
},
116+
}),
117+
);
103118

104119
// Turn off Next.js' own fetch instrumentation
105120
// https://github.com/lforst/nextjs-fork/blob/1994fd186defda77ad971c36dc3163db263c993f/packages/next/src/server/lib/patch-fetch.ts#L245
@@ -319,15 +334,47 @@ export function init(options: NodeOptions): NodeClient | undefined {
319334
}
320335

321336
// Enhance route handler transactions
322-
if (event.type === 'transaction' && event.contexts?.trace?.data?.['sentry.route_handler'] === true) {
337+
if (
338+
event.type === 'transaction' &&
339+
(event.contexts?.trace?.data?.['sentry.route_handler'] === true ||
340+
event.contexts?.trace?.data?.['sentry.rsc'] === true)
341+
) {
323342
event.contexts.trace.data = event.contexts.trace.data || {};
324343
event.contexts.trace.data[SEMANTIC_ATTRIBUTE_SENTRY_OP] = 'http.server';
325344
event.contexts.trace.op = 'http.server';
345+
346+
if (event.transaction) {
347+
event.transaction = stripUrlQueryAndFragment(event.transaction);
348+
}
349+
326350
if (typeof event.contexts.trace.data[ATTR_HTTP_ROUTE] === 'string') {
327351
// eslint-disable-next-line deprecation/deprecation
328352
event.transaction = `${event.contexts.trace.data[SEMATTRS_HTTP_METHOD]} ${event.contexts.trace.data[
329353
ATTR_HTTP_ROUTE
330354
].replace(/\/route$/, '')}`;
355+
event.contexts.trace.data[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE] = 'route';
356+
}
357+
}
358+
359+
// Next.js 13 is not correctly picking up tracing data for trace propagation so we use a back-fill strategy
360+
if (
361+
event.type === 'transaction' &&
362+
typeof event.contexts?.trace?.data?.[TRANSACTION_ATTR_SENTRY_TRACE_BACKFILL] === 'string'
363+
) {
364+
const traceparentData = extractTraceparentData(
365+
event.contexts.trace.data[TRANSACTION_ATTR_SENTRY_TRACE_BACKFILL],
366+
);
367+
368+
if (traceparentData?.parentSampled === false) {
369+
return null;
370+
}
371+
372+
if (traceparentData?.traceId) {
373+
event.contexts.trace.trace_id = traceparentData.traceId;
374+
}
375+
376+
if (traceparentData?.parentSpanId) {
377+
event.contexts.trace.parent_span_id = traceparentData.parentSpanId;
331378
}
332379
}
333380

0 commit comments

Comments
 (0)