Skip to content

Commit b37e88e

Browse files
authored
feat(remix): Set sentry-trace and baggage <meta> tags on server-side (#5440)
Add a meta function to each route to set `sentry-trace` and `baggage` <meta> tags using the http.server (root) span. This enables distributed tracing where the head of trace is starting on the server side.
1 parent 8a06b16 commit b37e88e

File tree

1 file changed

+56
-1
lines changed

1 file changed

+56
-1
lines changed

packages/remix/src/utils/instrumentServer.ts

+56-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
1+
/* eslint-disable max-lines */
12
import { captureException, getCurrentHub } from '@sentry/node';
23
import { getActiveTransaction } from '@sentry/tracing';
3-
import { addExceptionMechanism, fill, loadModule, logger, stripUrlQueryAndFragment } from '@sentry/utils';
4+
import {
5+
addExceptionMechanism,
6+
fill,
7+
loadModule,
8+
logger,
9+
serializeBaggage,
10+
stripUrlQueryAndFragment,
11+
} from '@sentry/utils';
412

513
// Types vendored from @remix-run/[email protected]:
614
// https://github.com/remix-run/remix/blob/f3691d51027b93caa3fd2cdfe146d7b62a6eb8f2/packages/remix-server-runtime/server.ts
@@ -20,11 +28,26 @@ interface Route {
2028
parentId?: string;
2129
path?: string;
2230
}
31+
interface RouteData {
32+
[routeId: string]: AppData;
33+
}
34+
35+
interface MetaFunction {
36+
(args: { data: AppData; parentsData: RouteData; params: Params; location: Location }): HtmlMetaDescriptor;
37+
}
38+
39+
interface HtmlMetaDescriptor {
40+
[name: string]: null | string | undefined | Record<string, string> | Array<Record<string, string> | string>;
41+
charset?: 'utf-8';
42+
charSet?: 'utf-8';
43+
title?: string;
44+
}
2345

2446
interface ServerRouteModule {
2547
action?: DataFunction;
2648
headers?: unknown;
2749
loader?: DataFunction;
50+
meta?: MetaFunction | HtmlMetaDescriptor;
2851
}
2952

3053
interface ServerRoute extends Route {
@@ -209,6 +232,36 @@ function makeWrappedLoader(origAction: DataFunction): DataFunction {
209232
return makeWrappedDataFunction(origAction, 'loader');
210233
}
211234

235+
function makeWrappedMeta(origMeta: MetaFunction | HtmlMetaDescriptor = {}): MetaFunction {
236+
return function (
237+
this: unknown,
238+
args: { data: AppData; parentsData: RouteData; params: Params; location: Location },
239+
): HtmlMetaDescriptor {
240+
let origMetaResult;
241+
if (origMeta instanceof Function) {
242+
origMetaResult = origMeta.call(this, args);
243+
} else {
244+
origMetaResult = origMeta;
245+
}
246+
247+
const scope = getCurrentHub().getScope();
248+
if (scope) {
249+
const span = scope.getSpan();
250+
const transaction = getActiveTransaction();
251+
252+
if (span && transaction) {
253+
return {
254+
...origMetaResult,
255+
'sentry-trace': span.toTraceparent(),
256+
baggage: serializeBaggage(transaction.getBaggage()),
257+
};
258+
}
259+
}
260+
261+
return origMetaResult;
262+
};
263+
}
264+
212265
function wrapRequestHandler(origRequestHandler: RequestHandler): RequestHandler {
213266
return async function (this: unknown, request: Request, loadContext?: unknown): Promise<Response> {
214267
const hub = getCurrentHub();
@@ -250,6 +303,8 @@ function makeWrappedCreateRequestHandler(
250303
for (const [id, route] of Object.entries(build.routes)) {
251304
const wrappedRoute = { ...route, module: { ...route.module } };
252305

306+
fill(wrappedRoute.module, 'meta', makeWrappedMeta);
307+
253308
if (wrappedRoute.module.action) {
254309
fill(wrappedRoute.module, 'action', makeWrappedAction);
255310
}

0 commit comments

Comments
 (0)