From 946de2d3076375ce5b09fd88f75f8c917a213001 Mon Sep 17 00:00:00 2001 From: JPeer264 Date: Mon, 5 Oct 2026 11:33:52 +0200 Subject: [PATCH] feat(react-router): Export createSentryServerInstrumentation from the Cloudflare entry The docs set up React Router's instrumentation API with `export const instrumentations = [Sentry.createSentryServerInstrumentation()]`, but the Cloudflare entry did not export the function, so a Worker could not follow that step. Without the API, resource routes and errors in loaders and actions keep the name of the server below React Router: `GET /{*splat}` on Node and Deno, `GET` or none on Bun and Cloudflare. `react-router-8-framework` now exports `instrumentations` on every runtime, like the docs. Resource routes and server errors are named after the route everywhere, so the error tests lose their runtime branches and their todo. Co-Authored-By: Claude Opus 5.5 --- .../app/entry.server.cloudflare.tsx | 2 ++ .../app/entry.server.tsx | 2 ++ .../tests/errors/errors.server.test.ts | 18 ++++-------------- .../performance/outgoing-fetch.server.test.ts | 1 + .../performance/performance.server.test.ts | 4 ++-- packages/react-router/src/cloudflare/index.ts | 4 ++++ 6 files changed, 15 insertions(+), 16 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/react-router-8-framework/app/entry.server.cloudflare.tsx b/dev-packages/e2e-tests/test-applications/react-router-8-framework/app/entry.server.cloudflare.tsx index e04fe621e718..31a4fec981f6 100644 --- a/dev-packages/e2e-tests/test-applications/react-router-8-framework/app/entry.server.cloudflare.tsx +++ b/dev-packages/e2e-tests/test-applications/react-router-8-framework/app/entry.server.cloudflare.tsx @@ -48,3 +48,5 @@ export const handleError: HandleErrorFunction = (error, { request }) => { }; export default Sentry.wrapSentryHandleRequest(handleRequest); + +export const instrumentations = [Sentry.createSentryServerInstrumentation()]; diff --git a/dev-packages/e2e-tests/test-applications/react-router-8-framework/app/entry.server.tsx b/dev-packages/e2e-tests/test-applications/react-router-8-framework/app/entry.server.tsx index 01682288ab71..9f9a70db740a 100644 --- a/dev-packages/e2e-tests/test-applications/react-router-8-framework/app/entry.server.tsx +++ b/dev-packages/e2e-tests/test-applications/react-router-8-framework/app/entry.server.tsx @@ -18,3 +18,5 @@ const handleRequest = Sentry.createSentryHandleRequest({ export default handleRequest; export const handleError: HandleErrorFunction = Sentry.createSentryHandleError({ logErrors: true }); + +export const instrumentations = [Sentry.createSentryServerInstrumentation()]; diff --git a/dev-packages/e2e-tests/test-applications/react-router-8-framework/tests/errors/errors.server.test.ts b/dev-packages/e2e-tests/test-applications/react-router-8-framework/tests/errors/errors.server.test.ts index bf436aa3c866..35eb7a8854de 100644 --- a/dev-packages/e2e-tests/test-applications/react-router-8-framework/tests/errors/errors.server.test.ts +++ b/dev-packages/e2e-tests/test-applications/react-router-8-framework/tests/errors/errors.server.test.ts @@ -21,17 +21,12 @@ test.describe('server-side errors', () => { value: errorMessage, mechanism: { handled: false, - type: 'react-router', + type: 'react_router.loader', }, }, ], }, - // Express names the transaction on Node and Deno. On Bun, where Express is not instrumented under - // `bun run`, it stays the request path. On Cloudflare the error has no transaction. - // todo: should be 'GET /errors/server-loader' everywhere - ...(RUNTIME === 'cloudflare' - ? {} - : { transaction: RUNTIME === 'bun' ? 'GET /errors/server-loader' : 'GET /{*splat}' }), + transaction: 'GET /errors/server-loader', request: { url: expect.stringContaining('errors/server-loader'), headers: expect.any(Object), @@ -74,17 +69,12 @@ test.describe('server-side errors', () => { value: errorMessage, mechanism: { handled: false, - type: 'react-router', + type: 'react_router.action', }, }, ], }, - // Express names the transaction on Node and Deno. On Bun, where Express is not instrumented under - // `bun run`, it stays the request path. On Cloudflare the error has no transaction. - // todo: should be 'POST /errors/server-action' everywhere - ...(RUNTIME === 'cloudflare' - ? {} - : { transaction: RUNTIME === 'bun' ? 'POST /errors/server-action.data' : 'POST /{*splat}' }), + transaction: 'POST /errors/server-action', request: { url: expect.stringContaining('errors/server-action'), headers: expect.any(Object), diff --git a/dev-packages/e2e-tests/test-applications/react-router-8-framework/tests/performance/outgoing-fetch.server.test.ts b/dev-packages/e2e-tests/test-applications/react-router-8-framework/tests/performance/outgoing-fetch.server.test.ts index b24f0576f71c..dd8edc2d9da4 100644 --- a/dev-packages/e2e-tests/test-applications/react-router-8-framework/tests/performance/outgoing-fetch.server.test.ts +++ b/dev-packages/e2e-tests/test-applications/react-router-8-framework/tests/performance/outgoing-fetch.server.test.ts @@ -28,4 +28,5 @@ test('propagates the trace to an outgoing fetch', async ({ request }) => { .toBeDefined(); const serverSpan = streamedSpans.find(span => span.is_segment && span.trace_id === traceId && !span.parent_span_id)!; expect(getSpanOp(serverSpan)).toBe('http.server'); + expect(serverSpan.name).toBe('GET /performance/outgoing-fetch'); }); diff --git a/dev-packages/e2e-tests/test-applications/react-router-8-framework/tests/performance/performance.server.test.ts b/dev-packages/e2e-tests/test-applications/react-router-8-framework/tests/performance/performance.server.test.ts index 4488f1065229..c3f594ee2061 100644 --- a/dev-packages/e2e-tests/test-applications/react-router-8-framework/tests/performance/performance.server.test.ts +++ b/dev-packages/e2e-tests/test-applications/react-router-8-framework/tests/performance/performance.server.test.ts @@ -23,7 +23,7 @@ test.describe('server - performance', () => { expect(getSpanOp(span)).toBe('http.server'); expect(span.attributes).toMatchObject({ 'sentry.op': { value: 'http.server', type: 'string' }, - 'sentry.origin': { value: 'auto.http.react_router.request_handler', type: 'string' }, + 'sentry.origin': { value: 'auto.http.react_router.instrumentation_api', type: 'string' }, 'sentry.segment.name.source': { value: 'route', type: 'string' }, 'sentry.environment': { value: 'qa', type: 'string' }, 'sentry.sdk.name': { @@ -56,7 +56,7 @@ test.describe('server - performance', () => { expect(getSpanOp(span)).toBe('http.server'); expect(span.attributes).toMatchObject({ 'sentry.op': { value: 'http.server', type: 'string' }, - 'sentry.origin': { value: 'auto.http.react_router.request_handler', type: 'string' }, + 'sentry.origin': { value: 'auto.http.react_router.instrumentation_api', type: 'string' }, 'sentry.segment.name.source': { value: 'route', type: 'string' }, 'sentry.environment': { value: 'qa', type: 'string' }, 'sentry.sdk.name': { diff --git a/packages/react-router/src/cloudflare/index.ts b/packages/react-router/src/cloudflare/index.ts index 67db950b59fb..b94430728125 100644 --- a/packages/react-router/src/cloudflare/index.ts +++ b/packages/react-router/src/cloudflare/index.ts @@ -4,6 +4,10 @@ import { isPrerenderRequest } from '../server/serverBuild'; export * from '../client'; export { wrapSentryHandleRequest } from '../server/wrapSentryHandleRequest'; +export { + createSentryServerInstrumentation, + type CreateSentryServerInstrumentationOptions, +} from '../server/createServerInstrumentation'; /** * Injects Sentry trace meta tags into the HTML response by transforming the ReadableStream.