Skip to content

Commit 0aadc9b

Browse files
authored
ref: Set normalizedRequest instead of request in various places (#14305)
The request data integration prefers this over `request`, we want to get rid of `request` in v9. Updates the following: - [x] astro/src/server/middleware.ts - [x] bun/src/integrations/bunserver.ts - [x] cloudflare/src/scope-utils.ts - [ ] google-cloud-serverless/src/gcpfunction/http.ts - [x] nextjs/src/common/captureRequestError.ts - [x] nextjs/src/common/withServerActionInstrumentation.ts - [x] nextjs/src/common/wrapGenerationFunctionWithSentry.ts - [ ] nextjs/src/common/wrapMiddlewareWithSentry.ts - [x] nextjs/src/common/wrapRouteHandlerWithSentry.ts - [x] nextjs/src/common/wrapServerComponentWithSentry.ts - [ ] nextjs/src/common/pages-router-instrumentation/_error.ts - [ ] nextjs/src/common/pages-router-instrumentation/wrapApiHandlerWithSentry.ts - [ ] nextjs/src/common/utils/wrapperUtils.ts - [x] nextjs/src/edge/wrapApiHandlerWithSentry.ts - [ ] remix/src/utils/errors.ts - [ ] remix/src/utils/instrumentServer.ts - [x] sveltekit/src/server/handle.ts Part of #14298
1 parent a78a2d2 commit 0aadc9b

File tree

17 files changed

+94
-72
lines changed

17 files changed

+94
-72
lines changed

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

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -33,17 +33,12 @@ test('Sends a transaction for a request to app router', async ({ page }) => {
3333
trace_id: expect.any(String),
3434
});
3535

36-
expect(transactionEvent).toEqual(
37-
expect.objectContaining({
38-
request: {
39-
cookies: {},
40-
headers: expect.any(Object),
41-
url: expect.any(String),
42-
},
36+
expect(transactionEvent.request).toEqual({
37+
cookies: {},
38+
headers: expect.objectContaining({
39+
'user-agent': expect.any(String),
4340
}),
44-
);
45-
46-
expect(Object.keys(transactionEvent.request?.headers!).length).toBeGreaterThan(0);
41+
});
4742

4843
// The transaction should not contain any spans with the same name as the transaction
4944
// e.g. "GET /server-component/parameter/[...parameters]"

packages/astro/src/server/middleware.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,10 @@ import {
1212
startSpan,
1313
withIsolationScope,
1414
} from '@sentry/node';
15-
import type { Scope, SpanAttributes } from '@sentry/types';
15+
import type { RequestEventData, Scope, SpanAttributes } from '@sentry/types';
1616
import {
1717
addNonEnumerableProperty,
18+
extractQueryParamsFromUrl,
1819
logger,
1920
objectify,
2021
stripUrlQueryAndFragment,
@@ -111,7 +112,13 @@ async function instrumentRequest(
111112
getCurrentScope().setSDKProcessingMetadata({
112113
// We store the request on the current scope, not isolation scope,
113114
// because we may have multiple requests nested inside each other
114-
request: isDynamicPageRequest ? winterCGRequestToRequestData(request) : { method, url: request.url },
115+
normalizedRequest: (isDynamicPageRequest
116+
? winterCGRequestToRequestData(request)
117+
: {
118+
method,
119+
url: request.url,
120+
query_string: extractQueryParamsFromUrl(request.url),
121+
}) satisfies RequestEventData,
115122
});
116123

117124
if (options.trackClientIp && isDynamicPageRequest) {

packages/astro/test/server/middleware.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ describe('sentryMiddleware', () => {
221221
await middleware(ctx, next);
222222

223223
expect(setSDKProcessingMetadataMock).toHaveBeenCalledWith({
224-
request: {
224+
normalizedRequest: {
225225
method: 'GET',
226226
url: '/users',
227227
headers: {
@@ -254,7 +254,7 @@ describe('sentryMiddleware', () => {
254254
await middleware(ctx, next);
255255

256256
expect(setSDKProcessingMetadataMock).toHaveBeenCalledWith({
257-
request: {
257+
normalizedRequest: {
258258
method: 'GET',
259259
url: '/users',
260260
},

packages/bun/src/integrations/bunserver.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ import {
99
startSpan,
1010
withIsolationScope,
1111
} from '@sentry/core';
12-
import type { IntegrationFn, SpanAttributes } from '@sentry/types';
13-
import { getSanitizedUrlString, parseUrl } from '@sentry/utils';
12+
import type { IntegrationFn, RequestEventData, SpanAttributes } from '@sentry/types';
13+
import { extractQueryParamsFromUrl, getSanitizedUrlString, parseUrl } from '@sentry/utils';
1414

1515
const INTEGRATION_NAME = 'BunServer';
1616

@@ -76,11 +76,12 @@ function instrumentBunServeOptions(serveOptions: Parameters<typeof Bun.serve>[0]
7676
const url = getSanitizedUrlString(parsedUrl);
7777

7878
isolationScope.setSDKProcessingMetadata({
79-
request: {
79+
normalizedRequest: {
8080
url,
8181
method: request.method,
8282
headers: request.headers.toJSON(),
83-
},
83+
query_string: extractQueryParamsFromUrl(url),
84+
} satisfies RequestEventData,
8485
});
8586

8687
return continueTrace(

packages/cloudflare/src/scope-utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,5 @@ export function addCultureContext(scope: Scope, cf: IncomingRequestCfProperties)
2525
* Set request data on scope
2626
*/
2727
export function addRequest(scope: Scope, request: Request): void {
28-
scope.setSDKProcessingMetadata({ request: winterCGRequestToRequestData(request) });
28+
scope.setSDKProcessingMetadata({ normalizedRequest: winterCGRequestToRequestData(request) });
2929
}

packages/cloudflare/test/request.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ describe('withSentry', () => {
109109
},
110110
);
111111

112-
expect(sentryEvent.sdkProcessingMetadata?.request).toEqual({
112+
expect(sentryEvent.sdkProcessingMetadata?.normalizedRequest).toEqual({
113113
headers: {},
114114
url: 'https://example.com/',
115115
method: 'GET',

packages/nextjs/src/common/captureRequestError.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import { captureException, withScope } from '@sentry/core';
2+
import type { RequestEventData } from '@sentry/types';
3+
import { headersToDict } from '@sentry/utils';
24

35
type RequestInfo = {
46
path: string;
@@ -18,10 +20,10 @@ type ErrorContext = {
1820
export function captureRequestError(error: unknown, request: RequestInfo, errorContext: ErrorContext): void {
1921
withScope(scope => {
2022
scope.setSDKProcessingMetadata({
21-
request: {
22-
headers: request.headers,
23+
normalizedRequest: {
24+
headers: headersToDict(request.headers),
2325
method: request.method,
24-
},
26+
} satisfies RequestEventData,
2527
});
2628

2729
scope.setContext('nextjs', {

packages/nextjs/src/common/withServerActionInstrumentation.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
startSpan,
1010
withIsolationScope,
1111
} from '@sentry/core';
12+
import type { RequestEventData } from '@sentry/types';
1213
import { logger, vercelWaitUntil } from '@sentry/utils';
1314

1415
import { DEBUG_BUILD } from './debug-build';
@@ -89,9 +90,9 @@ async function withServerActionInstrumentationImplementation<A extends (...args:
8990

9091
isolationScope.setTransactionName(`serverAction/${serverActionName}`);
9192
isolationScope.setSDKProcessingMetadata({
92-
request: {
93+
normalizedRequest: {
9394
headers: fullHeadersObject,
94-
},
95+
} satisfies RequestEventData,
9596
});
9697

9798
return continueTrace(

packages/nextjs/src/common/wrapGenerationFunctionWithSentry.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {
1414
withIsolationScope,
1515
withScope,
1616
} from '@sentry/core';
17-
import type { WebFetchHeaders } from '@sentry/types';
17+
import type { RequestEventData, WebFetchHeaders } from '@sentry/types';
1818
import { propagationContextFromHeaders, uuid4, winterCGHeadersToDict } from '@sentry/utils';
1919

2020
import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/core';
@@ -68,9 +68,9 @@ export function wrapGenerationFunctionWithSentry<F extends (...args: any[]) => a
6868
scope.setTransactionName(`${componentType}.${generationFunctionIdentifier} (${componentRoute})`);
6969

7070
isolationScope.setSDKProcessingMetadata({
71-
request: {
71+
normalizedRequest: {
7272
headers: headersDict,
73-
},
73+
} satisfies RequestEventData,
7474
});
7575

7676
const activeSpan = getActiveSpan();

packages/nextjs/src/common/wrapMiddlewareWithSentry.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export function wrapMiddlewareWithSentry<H extends EdgeRouteHandler>(
3636

3737
if (req instanceof Request) {
3838
isolationScope.setSDKProcessingMetadata({
39-
request: winterCGRequestToRequestData(req),
39+
normalizedRequest: winterCGRequestToRequestData(req),
4040
});
4141
spanName = `middleware ${req.method} ${new URL(req.url).pathname}`;
4242
spanSource = 'url';

packages/nextjs/src/common/wrapRouteHandlerWithSentry.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {
1313
withIsolationScope,
1414
withScope,
1515
} from '@sentry/core';
16-
16+
import type { RequestEventData } from '@sentry/types';
1717
import type { RouteHandlerContext } from './types';
1818

1919
import { propagationContextFromHeaders, winterCGHeadersToDict } from '@sentry/utils';
@@ -64,10 +64,10 @@ export function wrapRouteHandlerWithSentry<F extends (...args: any[]) => any>(
6464
);
6565
scope.setPropagationContext(incomingPropagationContext);
6666
scope.setSDKProcessingMetadata({
67-
request: {
67+
normalizedRequest: {
6868
method,
6969
headers: completeHeadersDict,
70-
},
70+
} satisfies RequestEventData,
7171
});
7272
}
7373

packages/nextjs/src/common/wrapServerComponentWithSentry.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
withIsolationScope,
1414
withScope,
1515
} from '@sentry/core';
16+
import type { RequestEventData } from '@sentry/types';
1617
import { propagationContextFromHeaders, uuid4, vercelWaitUntil, winterCGHeadersToDict } from '@sentry/utils';
1718

1819
import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/core';
@@ -49,9 +50,9 @@ export function wrapServerComponentWithSentry<F extends (...args: any[]) => any>
4950
const headersDict = context.headers ? winterCGHeadersToDict(context.headers) : undefined;
5051

5152
isolationScope.setSDKProcessingMetadata({
52-
request: {
53+
normalizedRequest: {
5354
headers: headersDict,
54-
},
55+
} satisfies RequestEventData,
5556
});
5657

5758
return withIsolationScope(isolationScope, () => {

packages/nextjs/src/edge/wrapApiHandlerWithSentry.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export function wrapApiHandlerWithSentry<H extends EdgeRouteHandler>(
3232

3333
if (req instanceof Request) {
3434
isolationScope.setSDKProcessingMetadata({
35-
request: winterCGRequestToRequestData(req),
35+
normalizedRequest: winterCGRequestToRequestData(req),
3636
});
3737
currentScope.setTransactionName(`${req.method} ${parameterizedRoute}`);
3838
} else {

packages/node/src/integrations/http/SentryHttpInstrumentation.ts

Lines changed: 3 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@ import { getRequestInfo } from '@opentelemetry/instrumentation-http';
88
import { addBreadcrumb, getClient, getIsolationScope, withIsolationScope } from '@sentry/core';
99
import type { PolymorphicRequest, RequestEventData, SanitizedRequestData, Scope } from '@sentry/types';
1010
import {
11+
extractQueryParamsFromUrl,
1112
getBreadcrumbLogLevelFromHttpStatusCode,
1213
getSanitizedUrlString,
14+
headersToDict,
1315
logger,
1416
parseUrl,
1517
stripUrlQueryAndFragment,
@@ -145,7 +147,7 @@ export class SentryHttpInstrumentation extends InstrumentationBase<SentryHttpIns
145147
const normalizedRequest: RequestEventData = {
146148
url: absoluteUrl,
147149
method: request.method,
148-
query_string: extractQueryParams(request),
150+
query_string: extractQueryParamsFromUrl(request.url || ''),
149151
headers: headersToDict(request.headers),
150152
cookies,
151153
};
@@ -443,36 +445,3 @@ function patchRequestToCaptureBody(req: IncomingMessage, isolationScope: Scope):
443445
// ignore errors if we can't patch stuff
444446
}
445447
}
446-
447-
function extractQueryParams(req: IncomingMessage): string | undefined {
448-
// req.url is path and query string
449-
if (!req.url) {
450-
return;
451-
}
452-
453-
try {
454-
// The `URL` constructor can't handle internal URLs of the form `/some/path/here`, so stick a dummy protocol and
455-
// hostname as the base. Since the point here is just to grab the query string, it doesn't matter what we use.
456-
const queryParams = new URL(req.url, 'http://dogs.are.great').search.slice(1);
457-
return queryParams.length ? queryParams : undefined;
458-
} catch {
459-
return undefined;
460-
}
461-
}
462-
463-
function headersToDict(reqHeaders: Record<string, string | string[] | undefined>): Record<string, string> {
464-
const headers: Record<string, string> = Object.create(null);
465-
466-
try {
467-
Object.entries(reqHeaders).forEach(([key, value]) => {
468-
if (typeof value === 'string') {
469-
headers[key] = value;
470-
}
471-
});
472-
} catch (e) {
473-
DEBUG_BUILD &&
474-
logger.warn('Sentry failed extracting headers from a request object. If you see this, please file an issue.');
475-
}
476-
477-
return headers;
478-
}

packages/sveltekit/src/server/handle.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,9 @@ export function sentryHandle(handlerOptions?: SentryHandleOptions): Handle {
131131
return withIsolationScope(isolationScope => {
132132
// We only call continueTrace in the initial top level request to avoid
133133
// creating a new root span for the sub request.
134-
isolationScope.setSDKProcessingMetadata({ request: winterCGRequestToRequestData(input.event.request.clone()) });
134+
isolationScope.setSDKProcessingMetadata({
135+
normalizedRequest: winterCGRequestToRequestData(input.event.request.clone()),
136+
});
135137
return continueTrace(getTracePropagationData(input.event), () => instrumentHandle(input, options));
136138
});
137139
};
@@ -167,7 +169,9 @@ async function instrumentHandle(
167169
name: routeName,
168170
},
169171
async (span?: Span) => {
170-
getCurrentScope().setSDKProcessingMetadata({ request: winterCGRequestToRequestData(event.request.clone()) });
172+
getCurrentScope().setSDKProcessingMetadata({
173+
normalizedRequest: winterCGRequestToRequestData(event.request.clone()),
174+
});
171175
const res = await resolve(event, {
172176
transformPageChunk: addSentryCodeToPage(options),
173177
});

packages/utils/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ export {
7373
extractRequestData,
7474
winterCGHeadersToDict,
7575
winterCGRequestToRequestData,
76+
extractQueryParamsFromUrl,
77+
headersToDict,
7678
} from './requestdata';
7779
export type {
7880
AddRequestDataToEventOptions,

packages/utils/src/requestdata.ts

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -416,18 +416,58 @@ export function winterCGHeadersToDict(winterCGHeaders: WebFetchHeaders): Record<
416416
return headers;
417417
}
418418

419+
/**
420+
* Convert common request headers to a simple dictionary.
421+
*/
422+
export function headersToDict(reqHeaders: Record<string, string | string[] | undefined>): Record<string, string> {
423+
const headers: Record<string, string> = Object.create(null);
424+
425+
try {
426+
Object.entries(reqHeaders).forEach(([key, value]) => {
427+
if (typeof value === 'string') {
428+
headers[key] = value;
429+
}
430+
});
431+
} catch (e) {
432+
DEBUG_BUILD &&
433+
logger.warn('Sentry failed extracting headers from a request object. If you see this, please file an issue.');
434+
}
435+
436+
return headers;
437+
}
438+
419439
/**
420440
* Converts a `Request` object that implements the `Web Fetch API` (https://developer.mozilla.org/en-US/docs/Web/API/Headers) into the format that the `RequestData` integration understands.
421441
*/
422-
export function winterCGRequestToRequestData(req: WebFetchRequest): PolymorphicRequest {
442+
export function winterCGRequestToRequestData(req: WebFetchRequest): RequestEventData {
423443
const headers = winterCGHeadersToDict(req.headers);
444+
424445
return {
425446
method: req.method,
426447
url: req.url,
448+
query_string: extractQueryParamsFromUrl(req.url),
427449
headers,
450+
// TODO: Can we extract body data from the request?
428451
};
429452
}
430453

454+
/** Extract the query params from an URL. */
455+
export function extractQueryParamsFromUrl(url: string): string | undefined {
456+
// url is path and query string
457+
if (!url) {
458+
return;
459+
}
460+
461+
try {
462+
// The `URL` constructor can't handle internal URLs of the form `/some/path/here`, so stick a dummy protocol and
463+
// hostname as the base. Since the point here is just to grab the query string, it doesn't matter what we use.
464+
const queryParams = new URL(url, 'http://dogs.are.great').search.slice(1);
465+
return queryParams.length ? queryParams : undefined;
466+
} catch {
467+
return undefined;
468+
}
469+
}
470+
431471
function extractNormalizedRequestData(
432472
normalizedRequest: RequestEventData,
433473
{ include }: { include: string[] },

0 commit comments

Comments
 (0)