From 9f29bfc3df6c7b6b06db658b243a5da1bc4faa5f Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Wed, 17 Dec 2025 14:41:40 +0100 Subject: [PATCH 1/4] add failing e2e test --- .../src/routes/api.hello.ts | 11 ++++++ .../tests/transaction.test.ts | 37 +++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 dev-packages/e2e-tests/test-applications/tanstackstart-react/src/routes/api.hello.ts diff --git a/dev-packages/e2e-tests/test-applications/tanstackstart-react/src/routes/api.hello.ts b/dev-packages/e2e-tests/test-applications/tanstackstart-react/src/routes/api.hello.ts new file mode 100644 index 000000000000..6508439a9b33 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/tanstackstart-react/src/routes/api.hello.ts @@ -0,0 +1,11 @@ +import { createFileRoute } from '@tanstack/react-router'; + +export const Route = createFileRoute('/api/hello')({ + server: { + handlers: { + GET: async () => { + return new Response('Hello, world!'); + }, + }, + }, +}); diff --git a/dev-packages/e2e-tests/test-applications/tanstackstart-react/tests/transaction.test.ts b/dev-packages/e2e-tests/test-applications/tanstackstart-react/tests/transaction.test.ts index d2ebbffb0ec0..1d9cb2581018 100644 --- a/dev-packages/e2e-tests/test-applications/tanstackstart-react/tests/transaction.test.ts +++ b/dev-packages/e2e-tests/test-applications/tanstackstart-react/tests/transaction.test.ts @@ -98,3 +98,40 @@ test('Sends a server function transaction for a nested server function only if i expect(nestedSpan).toBeDefined(); expect(nestedSpan?.parent_span_id).toBe(autoSpan?.span_id); }); + +test('Sends an API route transaction with auto-instrumentation', async ({ page }) => { + const transactionEventPromise = waitForTransaction('tanstackstart-react', transactionEvent => { + return ( + transactionEvent?.contexts?.trace?.op === 'http.server' && + transactionEvent?.transaction === 'GET /api/hello' + ); + }); + + await page.goto('/api/hello'); + + const transactionEvent = await transactionEventPromise; + + console.log('transactionEvent: ', transactionEvent); + + expect(transactionEvent).toEqual( + expect.objectContaining({ + transaction: 'GET /api/hello', + }), + ); + + expect(transactionEvent?.spans).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + description: 'GET /api/hello', + op: 'http.server', + origin: 'auto.http.tanstackstart.server', + source: 'route', + data: { + 'sentry.op': 'http.server', + 'sentry.origin': 'auto.http.tanstackstart.server', + 'http.route': '/api/hello', + }, + }), + ]), + ); +}); From 915e6b62e6788f7f807828d6dca13945fbc77981 Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Wed, 17 Dec 2025 15:11:15 +0100 Subject: [PATCH 2/4] start span for other server requests --- .../tests/transaction.test.ts | 7 ++--- .../src/server/wrapFetchWithSentry.ts | 31 +++++++++++++++++-- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/tanstackstart-react/tests/transaction.test.ts b/dev-packages/e2e-tests/test-applications/tanstackstart-react/tests/transaction.test.ts index 1d9cb2581018..07c04464d527 100644 --- a/dev-packages/e2e-tests/test-applications/tanstackstart-react/tests/transaction.test.ts +++ b/dev-packages/e2e-tests/test-applications/tanstackstart-react/tests/transaction.test.ts @@ -102,8 +102,7 @@ test('Sends a server function transaction for a nested server function only if i test('Sends an API route transaction with auto-instrumentation', async ({ page }) => { const transactionEventPromise = waitForTransaction('tanstackstart-react', transactionEvent => { return ( - transactionEvent?.contexts?.trace?.op === 'http.server' && - transactionEvent?.transaction === 'GET /api/hello' + transactionEvent?.contexts?.trace?.op === 'http.server' && transactionEvent?.transaction === 'GET /api/hello' ); }); @@ -125,11 +124,11 @@ test('Sends an API route transaction with auto-instrumentation', async ({ page } description: 'GET /api/hello', op: 'http.server', origin: 'auto.http.tanstackstart.server', - source: 'route', data: { 'sentry.op': 'http.server', 'sentry.origin': 'auto.http.tanstackstart.server', - 'http.route': '/api/hello', + 'sentry.source': 'route', + 'http.request.method': 'GET', }, }), ]), diff --git a/packages/tanstackstart-react/src/server/wrapFetchWithSentry.ts b/packages/tanstackstart-react/src/server/wrapFetchWithSentry.ts index 22d218ef0b48..e4c9b3b0e0cf 100644 --- a/packages/tanstackstart-react/src/server/wrapFetchWithSentry.ts +++ b/packages/tanstackstart-react/src/server/wrapFetchWithSentry.ts @@ -1,4 +1,11 @@ -import { SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, startSpan } from '@sentry/node'; +import type { SpanAttributes } from '@sentry/core'; +import { SEMANTIC_ATTRIBUTE_HTTP_REQUEST_METHOD } from '@sentry/core'; +import { + SEMANTIC_ATTRIBUTE_SENTRY_OP, + SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, + SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, + startSpan, +} from '@sentry/node'; import { extractServerFunctionSha256 } from './utils'; export type ServerEntry = { @@ -42,7 +49,7 @@ export function wrapFetchWithSentry(serverEntry: ServerEntry): ServerEntry { const functionSha256 = extractServerFunctionSha256(url.pathname); const op = 'function.tanstackstart'; - const serverFunctionSpanAttributes = { + const serverFunctionSpanAttributes: SpanAttributes = { [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.tanstackstart.server', [SEMANTIC_ATTRIBUTE_SENTRY_OP]: op, 'tanstackstart.function.hash.sha256': functionSha256, @@ -60,7 +67,25 @@ export function wrapFetchWithSentry(serverEntry: ServerEntry): ServerEntry { ); } - return target.apply(thisArg, args); + // instrument other server requests including API routes + const op = 'http.server'; + const httpServerSpanAttributes: SpanAttributes = { + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.tanstackstart.server', + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: op, + [SEMANTIC_ATTRIBUTE_HTTP_REQUEST_METHOD]: method, + [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route', + }; + + return startSpan( + { + op: op, + name: `${method} ${url.pathname}`, + attributes: httpServerSpanAttributes, + }, + () => { + return target.apply(thisArg, args); + }, + ); }, }); } From 549d0b155775c67c143f5407fde577dddbd31153 Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Wed, 17 Dec 2025 15:35:09 +0100 Subject: [PATCH 3/4] refactor --- .../tests/transaction.test.ts | 2 - .../src/server/wrapFetchWithSentry.ts | 43 ++++++++----------- 2 files changed, 17 insertions(+), 28 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/tanstackstart-react/tests/transaction.test.ts b/dev-packages/e2e-tests/test-applications/tanstackstart-react/tests/transaction.test.ts index 07c04464d527..39ecfd4fc8df 100644 --- a/dev-packages/e2e-tests/test-applications/tanstackstart-react/tests/transaction.test.ts +++ b/dev-packages/e2e-tests/test-applications/tanstackstart-react/tests/transaction.test.ts @@ -110,8 +110,6 @@ test('Sends an API route transaction with auto-instrumentation', async ({ page } const transactionEvent = await transactionEventPromise; - console.log('transactionEvent: ', transactionEvent); - expect(transactionEvent).toEqual( expect.objectContaining({ transaction: 'GET /api/hello', diff --git a/packages/tanstackstart-react/src/server/wrapFetchWithSentry.ts b/packages/tanstackstart-react/src/server/wrapFetchWithSentry.ts index e4c9b3b0e0cf..c095344b9f66 100644 --- a/packages/tanstackstart-react/src/server/wrapFetchWithSentry.ts +++ b/packages/tanstackstart-react/src/server/wrapFetchWithSentry.ts @@ -44,43 +44,34 @@ export function wrapFetchWithSentry(serverEntry: ServerEntry): ServerEntry { const url = new URL(request.url); const method = request.method || 'GET'; - // instrument server functions + let op: string; + let spanAttributes: SpanAttributes; + if (url.pathname.includes('_serverFn') || url.pathname.includes('createServerFn')) { + // server function call + op = 'function.tanstackstart'; const functionSha256 = extractServerFunctionSha256(url.pathname); - const op = 'function.tanstackstart'; - - const serverFunctionSpanAttributes: SpanAttributes = { + spanAttributes = { [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.tanstackstart.server', [SEMANTIC_ATTRIBUTE_SENTRY_OP]: op, 'tanstackstart.function.hash.sha256': functionSha256, }; - - return startSpan( - { - op: op, - name: `${method} ${url.pathname}`, - attributes: serverFunctionSpanAttributes, - }, - () => { - return target.apply(thisArg, args); - }, - ); + } else { + // API route or other server request + op = 'http.server'; + spanAttributes = { + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.tanstackstart.server', + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: op, + [SEMANTIC_ATTRIBUTE_HTTP_REQUEST_METHOD]: method, + [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route', + }; } - // instrument other server requests including API routes - const op = 'http.server'; - const httpServerSpanAttributes: SpanAttributes = { - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.tanstackstart.server', - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: op, - [SEMANTIC_ATTRIBUTE_HTTP_REQUEST_METHOD]: method, - [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route', - }; - return startSpan( { - op: op, + op, name: `${method} ${url.pathname}`, - attributes: httpServerSpanAttributes, + attributes: spanAttributes, }, () => { return target.apply(thisArg, args); From 0427cf8177a2311b539196f8de25dc8ca1ae3d71 Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Wed, 17 Dec 2025 16:32:33 +0100 Subject: [PATCH 4/4] Switch sentry source to url --- .../tanstackstart-react/tests/transaction.test.ts | 2 +- packages/tanstackstart-react/src/server/wrapFetchWithSentry.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/tanstackstart-react/tests/transaction.test.ts b/dev-packages/e2e-tests/test-applications/tanstackstart-react/tests/transaction.test.ts index 39ecfd4fc8df..78e860e309b3 100644 --- a/dev-packages/e2e-tests/test-applications/tanstackstart-react/tests/transaction.test.ts +++ b/dev-packages/e2e-tests/test-applications/tanstackstart-react/tests/transaction.test.ts @@ -125,7 +125,7 @@ test('Sends an API route transaction with auto-instrumentation', async ({ page } data: { 'sentry.op': 'http.server', 'sentry.origin': 'auto.http.tanstackstart.server', - 'sentry.source': 'route', + 'sentry.source': 'url', 'http.request.method': 'GET', }, }), diff --git a/packages/tanstackstart-react/src/server/wrapFetchWithSentry.ts b/packages/tanstackstart-react/src/server/wrapFetchWithSentry.ts index c095344b9f66..788873db9970 100644 --- a/packages/tanstackstart-react/src/server/wrapFetchWithSentry.ts +++ b/packages/tanstackstart-react/src/server/wrapFetchWithSentry.ts @@ -63,7 +63,7 @@ export function wrapFetchWithSentry(serverEntry: ServerEntry): ServerEntry { [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.tanstackstart.server', [SEMANTIC_ATTRIBUTE_SENTRY_OP]: op, [SEMANTIC_ATTRIBUTE_HTTP_REQUEST_METHOD]: method, - [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route', + [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url', }; }