Skip to content

Commit 6c8d6c3

Browse files
authored
feat(remix): Enable RequestData integration for server-side requests (#6007)
This enables the new `RequestData` integration in the remix SDK in order to add request data to server-side events. The integration itself is already installed automatically when the SDK initializes, because it's one of the underlying node SDK's default integrations, but without access to the request, it currently no-ops. This enables it to function as intended by storing the request object in the scope's `sdkProcessingMetadata`.
1 parent 18b29d2 commit 6c8d6c3

File tree

5 files changed

+62
-4
lines changed

5 files changed

+62
-4
lines changed

packages/node/src/requestdata.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ export function extractRequestData(
169169
// koa, nextjs: req.url
170170
const originalUrl = req.originalUrl || req.url || '';
171171
// absolute url
172-
const absoluteUrl = `${protocol}://${host}${originalUrl}`;
172+
const absoluteUrl = originalUrl.startsWith(protocol) ? originalUrl : `${protocol}://${host}${originalUrl}`;
173173
include.forEach(key => {
174174
switch (key) {
175175
case 'headers': {

packages/remix/src/utils/instrumentServer.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,14 +70,15 @@ function extractData(response: Response): Promise<unknown> {
7070
return responseClone.text();
7171
}
7272

73-
function captureRemixServerException(err: Error, name: string): void {
73+
function captureRemixServerException(err: Error, name: string, request: Request): void {
7474
// Skip capturing if the thrown error is not a 5xx response
7575
// https://remix.run/docs/en/v1/api/conventions#throwing-responses-in-loaders
7676
if (isResponse(err) && err.status < 500) {
7777
return;
7878
}
7979

8080
captureException(isResponse(err) ? extractData(err) : err, scope => {
81+
scope.setSDKProcessingMetadata({ request });
8182
scope.addEventProcessor(event => {
8283
addExceptionMechanism(event, {
8384
type: 'instrument',
@@ -127,7 +128,7 @@ function makeWrappedDocumentRequestFunction(
127128

128129
span?.finish();
129130
} catch (err) {
130-
captureRemixServerException(err, 'documentRequest');
131+
captureRemixServerException(err, 'documentRequest', request);
131132
throw err;
132133
}
133134

@@ -164,7 +165,7 @@ function makeWrappedDataFunction(origFn: DataFunction, id: string, name: 'action
164165
currentScope.setSpan(activeTransaction);
165166
span?.finish();
166167
} catch (err) {
167-
captureRemixServerException(err, name);
168+
captureRemixServerException(err, name, args.request);
168169
throw err;
169170
}
170171

@@ -353,6 +354,11 @@ function wrapRequestHandler(origRequestHandler: RequestHandler, build: ServerBui
353354
return local.bind(async () => {
354355
const hub = getCurrentHub();
355356
const options = hub.getClient()?.getOptions();
357+
const scope = hub.getScope();
358+
359+
if (scope) {
360+
scope.setSDKProcessingMetadata({ request });
361+
}
356362

357363
if (!options || !hasTracingEnabled(options)) {
358364
return origRequestHandler.call(this, request, loadContext);

packages/remix/src/utils/serverAdapters/express.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,11 @@ function wrapExpressRequestHandler(
6060
const request = extractRequestData(req);
6161
const hub = getCurrentHub();
6262
const options = hub.getClient()?.getOptions();
63+
const scope = hub.getScope();
64+
65+
if (scope) {
66+
scope.setSDKProcessingMetadata({ request });
67+
}
6368

6469
if (!options || !hasTracingEnabled(options) || !request.url || !request.method) {
6570
return origRequestHandler.call(this, req, res, next);

packages/remix/test/integration/test/server/action.test.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ describe.each(['builtin', 'express'])('Remix API Actions with adapter = %s', ada
3030
op: 'function.remix.document_request',
3131
},
3232
],
33+
request: {
34+
method: 'POST',
35+
url,
36+
},
3337
});
3438
});
3539

@@ -78,6 +82,44 @@ describe.each(['builtin', 'express'])('Remix API Actions with adapter = %s', ada
7882
});
7983
});
8084

85+
it('includes request data in transaction and error events', async () => {
86+
const env = await RemixTestEnv.init(adapter);
87+
const url = `${env.url}/action-json-response/-1`;
88+
89+
const envelopes = await env.getMultipleEnvelopeRequest({
90+
url,
91+
count: 2,
92+
method: 'post',
93+
envelopeType: ['transaction', 'event'],
94+
});
95+
96+
const [transaction] = envelopes.filter(envelope => envelope[1].type === 'transaction');
97+
const [event] = envelopes.filter(envelope => envelope[1].type === 'event');
98+
99+
assertSentryTransaction(transaction[2], {
100+
transaction: 'routes/action-json-response/$id',
101+
request: {
102+
method: 'POST',
103+
url,
104+
},
105+
});
106+
107+
assertSentryEvent(event[2], {
108+
exception: {
109+
values: [
110+
{
111+
type: 'Error',
112+
value: 'Unexpected Server Error',
113+
},
114+
],
115+
},
116+
request: {
117+
method: 'POST',
118+
url,
119+
},
120+
});
121+
});
122+
81123
it('handles a thrown 500 response', async () => {
82124
const env = await RemixTestEnv.init(adapter);
83125
const url = `${env.url}/action-json-response/-2`;

packages/types/src/scope.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,4 +175,9 @@ export interface Scope {
175175
* Clears attachments from the scope
176176
*/
177177
clearAttachments(): this;
178+
179+
/**
180+
* Add data which will be accessible during event processing but won't get sent to Sentry
181+
*/
182+
setSDKProcessingMetadata(newData: { [key: string]: unknown }): this;
178183
}

0 commit comments

Comments
 (0)