Skip to content

Commit 1db7f93

Browse files
committed
add headers in nextjs and node-core
1 parent bbcec87 commit 1db7f93

File tree

10 files changed

+85
-10
lines changed

10 files changed

+85
-10
lines changed

packages/nextjs/src/common/pages-router-instrumentation/wrapApiHandlerWithSentry.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
} from '@sentry/core';
1616
import type { NextApiRequest } from 'next';
1717
import type { AugmentedNextApiResponse, NextApiHandler } from '../types';
18+
import { addHeadersAsAttributes } from '../utils/headersToAttributes';
1819
import { flushSafelyWithTimeout } from '../utils/responseEnd';
1920
import { dropNextjsRootContext, escapeNextjsTracing } from '../utils/tracingUtils';
2021

@@ -87,6 +88,7 @@ export function wrapApiHandlerWithSentry(apiHandler: NextApiHandler, parameteriz
8788
attributes: {
8889
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route',
8990
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.nextjs',
91+
...addHeadersAsAttributes(normalizedRequest.headers || {}),
9092
},
9193
},
9294
async span => {
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import type { Span, WebFetchHeaders } from '@sentry/core';
2+
import { getClient, httpHeadersToSpanAttributes, winterCGHeadersToDict } from '@sentry/core';
3+
4+
/**
5+
* Extracts HTTP request headers as span attributes and optionally applies them to a span.
6+
*/
7+
export function addHeadersAsAttributes(
8+
headers: WebFetchHeaders | Headers | Record<string, string | string[] | undefined> | undefined,
9+
span?: Span,
10+
): Record<string, string[]> {
11+
if (!headers) {
12+
return {};
13+
}
14+
15+
const client = getClient();
16+
const sendDefaultPii = client?.getOptions().sendDefaultPii ?? false;
17+
18+
const headersDict: Record<string, string | string[] | undefined> =
19+
headers instanceof Headers || (typeof headers === 'object' && 'get' in headers)
20+
? winterCGHeadersToDict(headers as Headers)
21+
: headers;
22+
23+
const headerAttributes = httpHeadersToSpanAttributes(headersDict, sendDefaultPii);
24+
25+
if (span) {
26+
span.setAttributes(headerAttributes);
27+
}
28+
29+
return headerAttributes;
30+
}

packages/nextjs/src/common/wrapGenerationFunctionWithSentry.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {
2222
import type { GenerationFunctionContext } from '../common/types';
2323
import { isNotFoundNavigationError, isRedirectNavigationError } from './nextNavigationErrorUtils';
2424
import { TRANSACTION_ATTR_SENTRY_TRACE_BACKFILL } from './span-attributes-with-logic-attached';
25+
import { addHeadersAsAttributes } from './utils/headersToAttributes';
2526
import { commonObjectToIsolationScope, commonObjectToPropagationContext } from './utils/tracingUtils';
2627
import { getSanitizedRequestUrl } from './utils/urls';
2728
import { maybeExtractSynchronousParamsAndSearchParams } from './utils/wrapperUtils';
@@ -63,6 +64,11 @@ export function wrapGenerationFunctionWithSentry<F extends (...args: any[]) => a
6364

6465
const headersDict = headers ? winterCGHeadersToDict(headers) : undefined;
6566

67+
if (activeSpan) {
68+
const rootSpan = getRootSpan(activeSpan);
69+
addHeadersAsAttributes(headers, rootSpan);
70+
}
71+
6672
let data: Record<string, unknown> | undefined = undefined;
6773
if (getClient()?.getOptions().sendDefaultPii) {
6874
const props: unknown = args[0];

packages/nextjs/src/common/wrapMiddlewareWithSentry.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
winterCGRequestToRequestData,
1414
withIsolationScope,
1515
} from '@sentry/core';
16+
import { addHeadersAsAttributes } from '../common/utils/headersToAttributes';
1617
import { flushSafelyWithTimeout } from '../common/utils/responseEnd';
1718
import type { EdgeRouteHandler } from '../edge/types';
1819

@@ -59,13 +60,16 @@ export function wrapMiddlewareWithSentry<H extends EdgeRouteHandler>(
5960

6061
let spanName: string;
6162
let spanSource: TransactionSource;
63+
let headerAttributes: Record<string, string[]> = {};
6264

6365
if (req instanceof Request) {
6466
isolationScope.setSDKProcessingMetadata({
6567
normalizedRequest: winterCGRequestToRequestData(req),
6668
});
6769
spanName = `middleware ${req.method} ${new URL(req.url).pathname}`;
6870
spanSource = 'url';
71+
72+
headerAttributes = addHeadersAsAttributes(req.headers);
6973
} else {
7074
spanName = 'middleware';
7175
spanSource = 'component';
@@ -84,6 +88,7 @@ export function wrapMiddlewareWithSentry<H extends EdgeRouteHandler>(
8488
const rootSpan = getRootSpan(activeSpan);
8589
if (rootSpan) {
8690
setCapturedScopesOnSpan(rootSpan, currentScope, isolationScope);
91+
rootSpan.setAttributes(headerAttributes);
8792
}
8893
}
8994

@@ -94,6 +99,7 @@ export function wrapMiddlewareWithSentry<H extends EdgeRouteHandler>(
9499
attributes: {
95100
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: spanSource,
96101
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.nextjs.wrapMiddlewareWithSentry',
102+
...headerAttributes,
97103
},
98104
},
99105
() => {

packages/nextjs/src/common/wrapRouteHandlerWithSentry.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
} from '@sentry/core';
2020
import { isNotFoundNavigationError, isRedirectNavigationError } from './nextNavigationErrorUtils';
2121
import type { RouteHandlerContext } from './types';
22+
import { addHeadersAsAttributes } from './utils/headersToAttributes';
2223
import { flushSafelyWithTimeout } from './utils/responseEnd';
2324
import { commonObjectToIsolationScope } from './utils/tracingUtils';
2425

@@ -39,6 +40,10 @@ export function wrapRouteHandlerWithSentry<F extends (...args: any[]) => any>(
3940
const activeSpan = getActiveSpan();
4041
const rootSpan = activeSpan ? getRootSpan(activeSpan) : undefined;
4142

43+
if (rootSpan && process.env.NEXT_RUNTIME !== 'edge') {
44+
addHeadersAsAttributes(headers, rootSpan);
45+
}
46+
4247
let edgeRuntimeIsolationScopeOverride: Scope | undefined;
4348
if (rootSpan && process.env.NEXT_RUNTIME === 'edge') {
4449
const isolationScope = commonObjectToIsolationScope(headers);
@@ -50,6 +55,7 @@ export function wrapRouteHandlerWithSentry<F extends (...args: any[]) => any>(
5055
rootSpan.updateName(`${method} ${parameterizedRoute}`);
5156
rootSpan.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, 'route');
5257
rootSpan.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'http.server');
58+
addHeadersAsAttributes(headers, rootSpan);
5359
}
5460

5561
return withIsolationScope(

packages/nextjs/src/common/wrapServerComponentWithSentry.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import { isNotFoundNavigationError, isRedirectNavigationError } from '../common/
2424
import type { ServerComponentContext } from '../common/types';
2525
import { flushSafelyWithTimeout } from '../common/utils/responseEnd';
2626
import { TRANSACTION_ATTR_SENTRY_TRACE_BACKFILL } from './span-attributes-with-logic-attached';
27+
import { addHeadersAsAttributes } from './utils/headersToAttributes';
2728
import { commonObjectToIsolationScope, commonObjectToPropagationContext } from './utils/tracingUtils';
2829
import { getSanitizedRequestUrl } from './utils/urls';
2930
import { maybeExtractSynchronousParamsAndSearchParams } from './utils/wrapperUtils';
@@ -61,6 +62,11 @@ export function wrapServerComponentWithSentry<F extends (...args: any[]) => any>
6162

6263
const headersDict = context.headers ? winterCGHeadersToDict(context.headers) : undefined;
6364

65+
if (activeSpan) {
66+
const rootSpan = getRootSpan(activeSpan);
67+
addHeadersAsAttributes(context.headers, rootSpan);
68+
}
69+
6470
let params: Record<string, string> | undefined = undefined;
6571

6672
if (getClient()?.getOptions().sendDefaultPii) {

packages/nextjs/src/edge/wrapApiHandlerWithSentry.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
winterCGRequestToRequestData,
1414
withIsolationScope,
1515
} from '@sentry/core';
16+
import { addHeadersAsAttributes } from '../common/utils/headersToAttributes';
1617
import { flushSafelyWithTimeout } from '../common/utils/responseEnd';
1718
import type { EdgeRouteHandler } from './types';
1819

@@ -31,11 +32,15 @@ export function wrapApiHandlerWithSentry<H extends EdgeRouteHandler>(
3132
const req: unknown = args[0];
3233
const currentScope = getCurrentScope();
3334

35+
let headerAttributes: Record<string, string[]> = {};
36+
3437
if (req instanceof Request) {
3538
isolationScope.setSDKProcessingMetadata({
3639
normalizedRequest: winterCGRequestToRequestData(req),
3740
});
3841
currentScope.setTransactionName(`${req.method} ${parameterizedRoute}`);
42+
43+
headerAttributes = addHeadersAsAttributes(req.headers);
3944
} else {
4045
currentScope.setTransactionName(`handler (${parameterizedRoute})`);
4146
}
@@ -58,6 +63,7 @@ export function wrapApiHandlerWithSentry<H extends EdgeRouteHandler>(
5863
rootSpan.setAttributes({
5964
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'http.server',
6065
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route',
66+
...headerAttributes,
6167
});
6268
setCapturedScopesOnSpan(rootSpan, currentScope, isolationScope);
6369
}
@@ -74,6 +80,7 @@ export function wrapApiHandlerWithSentry<H extends EdgeRouteHandler>(
7480
attributes: {
7581
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route',
7682
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.nextjs.wrapApiHandlerWithSentry',
83+
...headerAttributes,
7784
},
7885
},
7986
() => {

packages/nextjs/src/server/index.ts

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -201,15 +201,6 @@ export function init(options: NodeOptions): NodeClient | undefined {
201201
}
202202

203203
setCapturedScopesOnSpan(span, scope, isolationScope);
204-
205-
const sendDefaultPii = opts.sendDefaultPii ?? false;
206-
const sdkProcessingMetadata = isolationScope.getScopeData().sdkProcessingMetadata;
207-
const normalizedRequest = sdkProcessingMetadata?.normalizedRequest;
208-
209-
if (normalizedRequest?.headers) {
210-
const headerAttributes = httpHeadersToSpanAttributes(normalizedRequest.headers, sendDefaultPii);
211-
span.setAttributes(headerAttributes);
212-
}
213204
}
214205
});
215206

packages/node-core/src/integrations/http/incoming-requests.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
getClient,
88
getCurrentScope,
99
getIsolationScope,
10+
httpHeadersToSpanAttributes,
1011
httpRequestToRequestData,
1112
stripUrlQueryAndFragment,
1213
withIsolationScope,
@@ -70,8 +71,13 @@ export function instrumentServer(
7071
patchRequestToCaptureBody(request, isolationScope, maxIncomingRequestBodySize);
7172
}
7273

74+
// Extract HTTP request headers as span attributes
75+
const client = getClient();
76+
const sendDefaultPii = client?.getOptions().sendDefaultPii ?? false;
77+
const httpHeaderAttributes = httpHeadersToSpanAttributes(normalizedRequest.headers || {}, sendDefaultPii);
78+
7379
// Update the isolation scope, isolate this request
74-
isolationScope.setSDKProcessingMetadata({ normalizedRequest, ipAddress });
80+
isolationScope.setSDKProcessingMetadata({ normalizedRequest, ipAddress, httpHeaderAttributes });
7581

7682
// attempt to update the scope's `transactionName` based on the request URL
7783
// Ideally, framework instrumentations coming after the HttpInstrumentation

packages/opentelemetry/src/spanProcessor.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
logSpanEnd,
1010
logSpanStart,
1111
setCapturedScopesOnSpan,
12+
spanToJSON,
1213
} from '@sentry/core';
1314
import { SEMANTIC_ATTRIBUTE_SENTRY_PARENT_IS_REMOTE } from './semanticAttributes';
1415
import { SentrySpanExporter } from './spanExporter';
@@ -47,6 +48,20 @@ function onSpanStart(span: Span, parentContext: Context): void {
4748

4849
logSpanStart(span);
4950

51+
// Add HTTP request headers as span attributes for HTTP server spans
52+
if (scopes?.isolationScope) {
53+
const spanData = spanToJSON(span);
54+
// Check if this is an HTTP server span
55+
if (spanData.op === 'http.server' || (spanData.data && 'http.method' in spanData.data)) {
56+
const sdkProcessingMetadata = scopes.isolationScope.getScopeData().sdkProcessingMetadata;
57+
const httpHeaderAttributes = sdkProcessingMetadata?.httpHeaderAttributes as Record<string, string[]>;
58+
59+
if (httpHeaderAttributes && typeof httpHeaderAttributes === 'object') {
60+
span.setAttributes(httpHeaderAttributes);
61+
}
62+
}
63+
}
64+
5065
const client = getClient();
5166
client?.emit('spanStart', span);
5267
}

0 commit comments

Comments
 (0)