I am struggling getting distributed tracing to work with React Router Framework.
I definitely have the spans for everything that's client side (asets etc), however I don't see any spans for my loaders.
I only see browser.request and browser.fetch spans and nothing inside.
I tried asking the AI but it did not solve my issue so far:
https://discord.com/channels/621778831602221064/1396502446037143622
Any idea what I could be missing ?
Here are my attempts so far:
// shared\sentry\initClient.ts
Sentry.init({
dsn: sentryConfig.dsn,
environment: appConfig.environment,
sendDefaultPii: true,
skipOpenTelemetrySetup: true,
integrations: [Sentry.reactRouterTracingIntegration()],
_experiments: { enableLogs: true },
tracesSampleRate: 1.0,
profilesSampleRate: 1.0,
});
// shared\sentry\initClient.server.ts
Sentry.init({
// ...
integrations: [nodeProfilingIntegration()],
// ...
});
// shared\sentry\initTracerProvider.server.ts
const sentryClient = Sentry.getClient();
if (!sentryClient) {
throw new Error("Sentry client not found");
}
const provider = new NodeTracerProvider({
sampler: new SentryOpenTelemetry.SentrySampler(sentryClient),
spanProcessors: [new SentryOpenTelemetry.SentrySpanProcessor()],
});
provider.register({
propagator: new SentryOpenTelemetry.SentryPropagator(),
contextManager: new Sentry.SentryContextManager(),
});
Sentry.validateOpenTelemetrySetup();
// web\entry.client.tsx
import "@local/shared/sentry/initClient";
// ...
// web\entry.server.tsx
const handleRequest = Sentry.createSentryHandleRequest({
ServerRouter,
renderToPipeableStream,
createReadableStreamFromReadable,
});
export { handleRequest };
export default handleRequest;
export const handleError: HandleErrorFunction = (error, { request }) => {
// React Router may abort some interrupted requests, don't log those
if (!request.signal.aborted) {
Sentry.captureException(error);
// optionally log the error so you can see it
console.error(error);
}
};
// web\root.tsx
// ...
export const loader = Sentry.wrapServerLoader(
{
name: "root",
},
async ({ context }: Route.LoaderArgs) => {
// ...
const sentryTraceData = Sentry.getTraceData();
return {
// ...
};
},
);
function RootLayout({ children }: RootLayoutProps) {
// ...
<meta name="sentry-trace" content={sentryTraceData["sentry-trace"]} />
<meta name="baggage" content={sentryTraceData.baggage} />
// ...
}
// ...
Then in a route loader
export const loader = Sentry.wrapServerLoader(
{ name: "app/_layout" },
async ({ context, request }: Route.LoaderArgs) => {
// ...
const shop = await Sentry.startSpan({ name: "app/_layout" }, async () => {
return await queryClient.fetchQuery(ShopQueryServer(shopId));
});
// ...
},
);