From 502d908d2e9105000fea545504fd1a7c39eb8df9 Mon Sep 17 00:00:00 2001 From: Martin Sonnberger Date: Fri, 31 Jul 2026 15:39:58 +0200 Subject: [PATCH 1/5] feat!: Use `function` op for framework functions --- .../tests/server-transactions.test.ts | 8 +-- .../tests/server-components.test.ts | 4 +- .../tests/errors/errors.server.test.ts | 7 +-- .../tests/performance/fetcher.client.test.ts | 14 ++--- .../tests/performance/lazy.server.test.ts | 18 ++++--- .../performance/middleware.server.test.ts | 13 ++--- .../performance/performance.server.test.ts | 14 ++--- .../performance/performance.server.test.ts | 8 +-- .../performance/performance.server.test.ts | 8 +-- .../tests/performance.server.test.ts | 4 +- .../tests/performance.server.test.ts | 4 +- .../tests/performance.server.test.ts | 4 +- .../tests/performance.server.test.ts | 4 +- .../tests/performance.server.test.ts | 4 +- .../tests/tracing.server.test.ts | 26 ++++----- .../tests/tracing.test.ts | 2 +- .../tests/performance.server.test.ts | 2 +- .../tests/performance.server.test.ts | 2 +- .../sveltekit-3/tests/tracing.server.test.ts | 26 ++++----- .../sveltekit-3/tests/tracing.test.ts | 2 +- .../tests/transaction.test.ts | 8 +-- .../tests/transaction.test.ts | 8 +-- .../nextjs/src/common/utils/tracingUtils.ts | 3 +- .../common/withServerActionInstrumentation.ts | 5 +- .../src/client/createClientInstrumentation.ts | 18 ++++--- .../src/server/createServerInstrumentation.ts | 22 ++++++-- .../createClientInstrumentation.test.ts | 15 ++++-- .../createServerInstrumentation.test.ts | 19 ++++--- packages/remix/src/server/instrumentServer.ts | 9 ++-- .../server/integrations/tracing-channel.ts | 6 ++- .../tracing-channel-no-form-data.test.ts | 2 +- .../remix/test/server/tracing-channel.test.ts | 4 +- .../server/withServerActionInstrumentation.ts | 5 +- .../withServerActionInstrumentation.test.ts | 4 +- packages/sveltekit/src/client/load.ts | 5 +- .../integrations/svelteKitSpans.ts | 54 +++++++++---------- packages/sveltekit/src/server-common/load.ts | 8 ++- .../src/server-common/serverRoute.ts | 6 ++- packages/sveltekit/test/client/load.test.ts | 12 +++-- .../integrations/svelteKitSpans.test.ts | 28 +++++----- .../sveltekit/test/server-common/load.test.ts | 15 ++++-- .../test/server-common/serverRoute.test.ts | 9 +++- .../src/server/globalMiddleware.ts | 2 +- .../src/server/wrapFetchWithSentry.ts | 14 ++--- .../test/server/wrapFetchWithSentry.test.ts | 6 ++- 45 files changed, 261 insertions(+), 200 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/create-remix-app-express/tests/server-transactions.test.ts b/dev-packages/e2e-tests/test-applications/create-remix-app-express/tests/server-transactions.test.ts index 0b25be4abb34..3627d641a323 100644 --- a/dev-packages/e2e-tests/test-applications/create-remix-app-express/tests/server-transactions.test.ts +++ b/dev-packages/e2e-tests/test-applications/create-remix-app-express/tests/server-transactions.test.ts @@ -38,7 +38,7 @@ test('Sends form data with action span', async ({ page }) => { ); expect(actionSpan).toBeDefined(); - expect(actionSpan?.op).toBe('action.remix'); + expect(actionSpan?.op).toBe('function'); expect(actionSpan?.data).toMatchObject({ 'formData.text': 'test', 'formData.file': 'file.txt', @@ -57,7 +57,7 @@ test('Sends a loader span to Sentry', async ({ page }) => { ); expect(loaderSpan).toBeDefined(); - expect(loaderSpan?.op).toBe('loader.remix'); + expect(loaderSpan?.op).toBe('function'); }); test('Propagates trace when ErrorBoundary is triggered', async ({ page }) => { @@ -111,7 +111,7 @@ test('Parameterizes a 2-level nested route on the server', async ({ page }) => { const transaction = await transactionPromise; expect(transaction.contexts?.trace?.data?.['sentry.source']).toBe('route'); - expect(transaction.spans?.some(s => s.data?.['code.function'] === 'loader' && s.op === 'loader.remix')).toBe(true); + expect(transaction.spans?.some(s => s.data?.['code.function'] === 'loader' && s.op === 'function')).toBe(true); }); test('Parameterizes a 3-level nested API route on the server', async ({ page }) => { @@ -163,7 +163,7 @@ test('Records action and loader spans on a parameterized action route', async ({ s => s.data?.['code.function'] === 'action' && s.data?.['match.route.id'] === 'routes/action-json-response.$id', ); expect(actionSpan).toBeDefined(); - expect(actionSpan?.op).toBe('action.remix'); + expect(actionSpan?.op).toBe('function'); expect(actionSpan?.data?.['match.params.id']).toBe('123123'); const rootLoaderSpan = transaction.spans?.find( diff --git a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/server-components.test.ts b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/server-components.test.ts index 12199c8d5085..f30fc3de97d6 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/server-components.test.ts +++ b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/server-components.test.ts @@ -83,7 +83,7 @@ test('Should set a "not_found" status on a server component span when notFound() expect(transactionEvent.spans).toContainEqual( expect.objectContaining({ description: 'resolve page server component "/server-component/not-found"', - op: 'function.nextjs', + op: 'function', data: expect.objectContaining({ 'sentry.nextjs.ssr.function.type': 'Page', 'sentry.nextjs.ssr.function.route': '/server-component/not-found', @@ -122,7 +122,7 @@ test('Should capture an error and transaction for a app router page', async ({ p expect(transactionEvent.spans).toContainEqual( expect.objectContaining({ description: 'resolve page server component "/server-component/faulty"', - op: 'function.nextjs', + op: 'function', data: expect.objectContaining({ 'sentry.nextjs.ssr.function.type': 'Page', 'sentry.nextjs.ssr.function.route': '/server-component/faulty', diff --git a/dev-packages/e2e-tests/test-applications/react-router-7-framework-instrumentation/tests/errors/errors.server.test.ts b/dev-packages/e2e-tests/test-applications/react-router-7-framework-instrumentation/tests/errors/errors.server.test.ts index 397ddec159ca..c84634582677 100644 --- a/dev-packages/e2e-tests/test-applications/react-router-7-framework-instrumentation/tests/errors/errors.server.test.ts +++ b/dev-packages/e2e-tests/test-applications/react-router-7-framework-instrumentation/tests/errors/errors.server.test.ts @@ -60,15 +60,16 @@ test.describe('server - instrumentation API error capture', () => { // Find the loader span const loaderSpan = transaction?.spans?.find( - (span: { data?: { 'sentry.op'?: string } }) => span.data?.['sentry.op'] === 'function.react_router.loader', + (span: { data?: { 'code.function.name'?: string } }) => span.data?.['code.function.name'] === 'loader', ); expect(loaderSpan).toMatchObject({ data: { 'sentry.origin': 'auto.function.react_router.instrumentation_api', - 'sentry.op': 'function.react_router.loader', + 'sentry.op': 'function', + 'code.function.name': 'loader', }, - op: 'function.react_router.loader', + op: 'function', }); }); diff --git a/dev-packages/e2e-tests/test-applications/react-router-7-framework-instrumentation/tests/performance/fetcher.client.test.ts b/dev-packages/e2e-tests/test-applications/react-router-7-framework-instrumentation/tests/performance/fetcher.client.test.ts index cbffc20c4ae0..28a3fb042145 100644 --- a/dev-packages/e2e-tests/test-applications/react-router-7-framework-instrumentation/tests/performance/fetcher.client.test.ts +++ b/dev-packages/e2e-tests/test-applications/react-router-7-framework-instrumentation/tests/performance/fetcher.client.test.ts @@ -3,9 +3,9 @@ import { waitForTransaction } from '@sentry-internal/test-utils'; import { APP_NAME } from '../constants'; // As of React Router 7.15+, HydratedRouter invokes the client `fetch` hook in Framework Mode. -// A fetcher submission produces a `function.react_router.fetcher` transaction -// (origin `auto.function.react_router.instrumentation_api`) that nests the client action/loader -// spans and the `http.client` spans for the underlying `.data` requests. +// A fetcher submission produces a `function` transaction (origin +// `auto.function.react_router.instrumentation_api`, `code.function.name` `fetcher`) that nests the +// client action/loader spans and the `http.client` spans for the underlying `.data` requests. // See: https://github.com/remix-run/react-router/discussions/13749 test.describe('client - instrumentation API fetcher', () => { @@ -20,7 +20,7 @@ test.describe('client - instrumentation API fetcher', () => { }); const fetcherTxPromise = waitForTransaction(APP_NAME, async transactionEvent => { - return transactionEvent.contexts?.trace?.op === 'function.react_router.fetcher'; + return transactionEvent.contexts?.trace?.data?.['code.function.name'] === 'fetcher'; }); await page.goto(`/performance/fetcher-test`); @@ -35,9 +35,9 @@ test.describe('client - instrumentation API fetcher', () => { // The fetcher transaction nests the client action span and the http.client span(s) for the // underlying `.data` request(s) - i.e. the OTel/browser fetch span is parented by the fetcher // span, not emitted standalone. - const spanOps = (fetcherTx.spans ?? []).map(span => span.op); - expect(spanOps).toContain('function.react_router.client_action'); - expect(spanOps).toContain('http.client'); + const spans = fetcherTx.spans ?? []; + expect(spans.some(span => span.data?.['code.function.name'] === 'clientAction')).toBe(true); + expect(spans.map(span => span.op)).toContain('http.client'); }); test('should still send server action transaction when fetcher submits', async ({ page }) => { diff --git a/dev-packages/e2e-tests/test-applications/react-router-7-framework-instrumentation/tests/performance/lazy.server.test.ts b/dev-packages/e2e-tests/test-applications/react-router-7-framework-instrumentation/tests/performance/lazy.server.test.ts index 85c35e75e8a5..3262deda8611 100644 --- a/dev-packages/e2e-tests/test-applications/react-router-7-framework-instrumentation/tests/performance/lazy.server.test.ts +++ b/dev-packages/e2e-tests/test-applications/react-router-7-framework-instrumentation/tests/performance/lazy.server.test.ts @@ -42,7 +42,7 @@ test.describe('server - instrumentation API lazy loading', () => { // Find the lazy span const lazySpan = transaction?.spans?.find( - (span: { data?: { 'sentry.op'?: string } }) => span.data?.['sentry.op'] === 'function.react_router.lazy', + (span: { data?: { 'code.function.name'?: string } }) => span.data?.['code.function.name'] === 'lazy', ); expect(lazySpan).toMatchObject({ @@ -50,13 +50,14 @@ test.describe('server - instrumentation API lazy loading', () => { trace_id: expect.any(String), data: { 'sentry.origin': 'auto.function.react_router.instrumentation_api', - 'sentry.op': 'function.react_router.lazy', + 'sentry.op': 'function', + 'code.function.name': 'lazy', }, description: 'Lazy Route Load', parent_span_id: expect.any(String), start_timestamp: expect.any(Number), timestamp: expect.any(Number), - op: 'function.react_router.lazy', + op: 'function', origin: 'auto.function.react_router.instrumentation_api', }); }); @@ -72,7 +73,7 @@ test.describe('server - instrumentation API lazy loading', () => { // Find the loader span that runs after lazy loading const loaderSpan = transaction?.spans?.find( - (span: { data?: { 'sentry.op'?: string } }) => span.data?.['sentry.op'] === 'function.react_router.loader', + (span: { data?: { 'code.function.name'?: string } }) => span.data?.['code.function.name'] === 'loader', ); expect(loaderSpan).toMatchObject({ @@ -80,10 +81,11 @@ test.describe('server - instrumentation API lazy loading', () => { trace_id: expect.any(String), data: { 'sentry.origin': 'auto.function.react_router.instrumentation_api', - 'sentry.op': 'function.react_router.loader', + 'sentry.op': 'function', + 'code.function.name': 'loader', }, description: '/performance/lazy-route', - op: 'function.react_router.loader', + op: 'function', origin: 'auto.function.react_router.instrumentation_api', }); }); @@ -98,11 +100,11 @@ test.describe('server - instrumentation API lazy loading', () => { const transaction = await txPromise; const lazySpan = transaction?.spans?.find( - (span: { data?: { 'sentry.op'?: string } }) => span.data?.['sentry.op'] === 'function.react_router.lazy', + (span: { data?: { 'code.function.name'?: string } }) => span.data?.['code.function.name'] === 'lazy', ); const loaderSpan = transaction?.spans?.find( - (span: { data?: { 'sentry.op'?: string } }) => span.data?.['sentry.op'] === 'function.react_router.loader', + (span: { data?: { 'code.function.name'?: string } }) => span.data?.['code.function.name'] === 'loader', ); expect(lazySpan).toBeDefined(); diff --git a/dev-packages/e2e-tests/test-applications/react-router-7-framework-instrumentation/tests/performance/middleware.server.test.ts b/dev-packages/e2e-tests/test-applications/react-router-7-framework-instrumentation/tests/performance/middleware.server.test.ts index ccc3fff2cd3e..ca03e157cb6e 100644 --- a/dev-packages/e2e-tests/test-applications/react-router-7-framework-instrumentation/tests/performance/middleware.server.test.ts +++ b/dev-packages/e2e-tests/test-applications/react-router-7-framework-instrumentation/tests/performance/middleware.server.test.ts @@ -38,7 +38,7 @@ test.describe('server - instrumentation API middleware', () => { // Find the middleware span const middlewareSpan = transaction?.spans?.find( - (span: { data?: { 'sentry.op'?: string } }) => span.data?.['sentry.op'] === 'function.react_router.middleware', + (span: { data?: { 'code.function.name'?: string } }) => span.data?.['code.function.name'] === 'middleware', ); expect(middlewareSpan).toBeDefined(); @@ -47,7 +47,8 @@ test.describe('server - instrumentation API middleware', () => { trace_id: expect.any(String), data: expect.objectContaining({ 'sentry.origin': 'auto.function.react_router.instrumentation_api', - 'sentry.op': 'function.react_router.middleware', + 'sentry.op': 'function', + 'code.function.name': 'middleware', 'react_router.route.id': 'routes/performance/with-middleware', 'http.route': '/performance/with-middleware', 'react_router.middleware.index': 0, @@ -55,7 +56,7 @@ test.describe('server - instrumentation API middleware', () => { parent_span_id: expect.any(String), start_timestamp: expect.any(Number), timestamp: expect.any(Number), - op: 'function.react_router.middleware', + op: 'function', origin: 'auto.function.react_router.instrumentation_api', }); @@ -74,11 +75,11 @@ test.describe('server - instrumentation API middleware', () => { const transaction = await txPromise; const middlewareSpan = transaction?.spans?.find( - (span: { data?: { 'sentry.op'?: string } }) => span.data?.['sentry.op'] === 'function.react_router.middleware', + (span: { data?: { 'code.function.name'?: string } }) => span.data?.['code.function.name'] === 'middleware', ); const loaderSpan = transaction?.spans?.find( - (span: { data?: { 'sentry.op'?: string } }) => span.data?.['sentry.op'] === 'function.react_router.loader', + (span: { data?: { 'code.function.name'?: string } }) => span.data?.['code.function.name'] === 'loader', ); expect(middlewareSpan).toBeDefined(); @@ -101,7 +102,7 @@ test.describe('server - instrumentation API middleware', () => { await expect(page.locator('#multi-middleware-content')).toHaveText('This route has 3 middlewares'); const middlewareSpans = transaction?.spans?.filter( - (span: { data?: { 'sentry.op'?: string } }) => span.data?.['sentry.op'] === 'function.react_router.middleware', + (span: { data?: { 'code.function.name'?: string } }) => span.data?.['code.function.name'] === 'middleware', ); expect(middlewareSpans).toHaveLength(3); diff --git a/dev-packages/e2e-tests/test-applications/react-router-7-framework-instrumentation/tests/performance/performance.server.test.ts b/dev-packages/e2e-tests/test-applications/react-router-7-framework-instrumentation/tests/performance/performance.server.test.ts index df37db2dd8e5..d9a8b0b8f2ac 100644 --- a/dev-packages/e2e-tests/test-applications/react-router-7-framework-instrumentation/tests/performance/performance.server.test.ts +++ b/dev-packages/e2e-tests/test-applications/react-router-7-framework-instrumentation/tests/performance/performance.server.test.ts @@ -103,21 +103,22 @@ test.describe('server - instrumentation API performance', () => { const transaction = await txPromise; // Find the loader span - const loaderSpan = transaction?.spans?.find(span => span.data?.['sentry.op'] === 'function.react_router.loader'); + const loaderSpan = transaction?.spans?.find(span => span.data?.['code.function.name'] === 'loader'); expect(loaderSpan).toMatchObject({ span_id: expect.any(String), trace_id: expect.any(String), data: { 'sentry.origin': 'auto.function.react_router.instrumentation_api', - 'sentry.op': 'function.react_router.loader', + 'sentry.op': 'function', + 'code.function.name': 'loader', }, description: '/performance/server-loader', parent_span_id: expect.any(String), start_timestamp: expect.any(Number), timestamp: expect.any(Number), status: 'ok', - op: 'function.react_router.loader', + op: 'function', origin: 'auto.function.react_router.instrumentation_api', }); }); @@ -133,21 +134,22 @@ test.describe('server - instrumentation API performance', () => { const transaction = await txPromise; // Find the action span - const actionSpan = transaction?.spans?.find(span => span.data?.['sentry.op'] === 'function.react_router.action'); + const actionSpan = transaction?.spans?.find(span => span.data?.['code.function.name'] === 'action'); expect(actionSpan).toMatchObject({ span_id: expect.any(String), trace_id: expect.any(String), data: { 'sentry.origin': 'auto.function.react_router.instrumentation_api', - 'sentry.op': 'function.react_router.action', + 'sentry.op': 'function', + 'code.function.name': 'action', }, description: '/performance/server-action', parent_span_id: expect.any(String), start_timestamp: expect.any(Number), timestamp: expect.any(Number), status: 'ok', - op: 'function.react_router.action', + op: 'function', origin: 'auto.function.react_router.instrumentation_api', }); }); diff --git a/dev-packages/e2e-tests/test-applications/react-router-7-framework/tests/performance/performance.server.test.ts b/dev-packages/e2e-tests/test-applications/react-router-7-framework/tests/performance/performance.server.test.ts index 8cbc4c46a460..db7e8f752350 100644 --- a/dev-packages/e2e-tests/test-applications/react-router-7-framework/tests/performance/performance.server.test.ts +++ b/dev-packages/e2e-tests/test-applications/react-router-7-framework/tests/performance/performance.server.test.ts @@ -122,14 +122,14 @@ test.describe('server - performance', () => { trace_id: expect.any(String), data: { 'sentry.origin': 'auto.http.react_router', - 'sentry.op': 'function.react_router.loader', + 'sentry.op': 'function', }, description: 'Executing Server Loader', parent_span_id: expect.any(String), start_timestamp: expect.any(Number), timestamp: expect.any(Number), status: 'ok', - op: 'function.react_router.loader', + op: 'function', origin: 'auto.http.react_router', }); }); @@ -150,14 +150,14 @@ test.describe('server - performance', () => { trace_id: expect.any(String), data: { 'sentry.origin': 'auto.http.react_router', - 'sentry.op': 'function.react_router.action', + 'sentry.op': 'function', }, description: 'Executing Server Action', parent_span_id: expect.any(String), start_timestamp: expect.any(Number), timestamp: expect.any(Number), status: 'ok', - op: 'function.react_router.action', + op: 'function', origin: 'auto.http.react_router', }); }); 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 8cbc4c46a460..db7e8f752350 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 @@ -122,14 +122,14 @@ test.describe('server - performance', () => { trace_id: expect.any(String), data: { 'sentry.origin': 'auto.http.react_router', - 'sentry.op': 'function.react_router.loader', + 'sentry.op': 'function', }, description: 'Executing Server Loader', parent_span_id: expect.any(String), start_timestamp: expect.any(Number), timestamp: expect.any(Number), status: 'ok', - op: 'function.react_router.loader', + op: 'function', origin: 'auto.http.react_router', }); }); @@ -150,14 +150,14 @@ test.describe('server - performance', () => { trace_id: expect.any(String), data: { 'sentry.origin': 'auto.http.react_router', - 'sentry.op': 'function.react_router.action', + 'sentry.op': 'function', }, description: 'Executing Server Action', parent_span_id: expect.any(String), start_timestamp: expect.any(Number), timestamp: expect.any(Number), status: 'ok', - op: 'function.react_router.action', + op: 'function', origin: 'auto.http.react_router', }); }); diff --git a/dev-packages/e2e-tests/test-applications/solidstart-2/tests/performance.server.test.ts b/dev-packages/e2e-tests/test-applications/solidstart-2/tests/performance.server.test.ts index 1a4fd8c80e30..877620c48ebd 100644 --- a/dev-packages/e2e-tests/test-applications/solidstart-2/tests/performance.server.test.ts +++ b/dev-packages/e2e-tests/test-applications/solidstart-2/tests/performance.server.test.ts @@ -20,7 +20,7 @@ test('sends a server action transaction on pageload', async ({ page }) => { expect.objectContaining({ description: 'getPrefecture', data: { - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function.server_action', + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function', [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.solidstart', [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'component', }, @@ -45,7 +45,7 @@ test('sends a server action transaction on client navigation', async ({ page }) expect.objectContaining({ description: 'getPrefecture', data: { - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function.server_action', + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function', [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.solidstart', [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'component', }, diff --git a/dev-packages/e2e-tests/test-applications/solidstart-dynamic-import/tests/performance.server.test.ts b/dev-packages/e2e-tests/test-applications/solidstart-dynamic-import/tests/performance.server.test.ts index 1722970cba76..932254b4c38d 100644 --- a/dev-packages/e2e-tests/test-applications/solidstart-dynamic-import/tests/performance.server.test.ts +++ b/dev-packages/e2e-tests/test-applications/solidstart-dynamic-import/tests/performance.server.test.ts @@ -20,7 +20,7 @@ test('sends a server action transaction on pageload', async ({ page }) => { expect.objectContaining({ description: 'getPrefecture', data: { - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function.server_action', + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function', [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.solidstart', [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'component', }, @@ -45,7 +45,7 @@ test('sends a server action transaction on client navigation', async ({ page }) expect.objectContaining({ description: 'getPrefecture', data: { - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function.server_action', + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function', [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.solidstart', [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'component', }, diff --git a/dev-packages/e2e-tests/test-applications/solidstart-spa/tests/performance.server.test.ts b/dev-packages/e2e-tests/test-applications/solidstart-spa/tests/performance.server.test.ts index 9406c4d977bb..810a5d4a8adb 100644 --- a/dev-packages/e2e-tests/test-applications/solidstart-spa/tests/performance.server.test.ts +++ b/dev-packages/e2e-tests/test-applications/solidstart-spa/tests/performance.server.test.ts @@ -20,7 +20,7 @@ test('sends a server action transaction on pageload', async ({ page }) => { expect.objectContaining({ description: 'getPrefecture', data: { - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function.server_action', + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function', [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.solidstart', [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'component', }, @@ -45,7 +45,7 @@ test('sends a server action transaction on client navigation', async ({ page }) expect.objectContaining({ description: 'getPrefecture', data: { - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function.server_action', + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function', [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.solidstart', [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'component', }, diff --git a/dev-packages/e2e-tests/test-applications/solidstart-top-level-import/tests/performance.server.test.ts b/dev-packages/e2e-tests/test-applications/solidstart-top-level-import/tests/performance.server.test.ts index 018e3bdcedc7..1c747470f7f6 100644 --- a/dev-packages/e2e-tests/test-applications/solidstart-top-level-import/tests/performance.server.test.ts +++ b/dev-packages/e2e-tests/test-applications/solidstart-top-level-import/tests/performance.server.test.ts @@ -20,7 +20,7 @@ test('sends a server action transaction on pageload', async ({ page }) => { expect.objectContaining({ description: 'getPrefecture', data: { - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function.server_action', + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function', [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.solidstart', [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'component', }, @@ -45,7 +45,7 @@ test('sends a server action transaction on client navigation', async ({ page }) expect.objectContaining({ description: 'getPrefecture', data: { - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function.server_action', + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function', [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.solidstart', [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'component', }, diff --git a/dev-packages/e2e-tests/test-applications/solidstart/tests/performance.server.test.ts b/dev-packages/e2e-tests/test-applications/solidstart/tests/performance.server.test.ts index a86f5b5af68c..956c78a08fcc 100644 --- a/dev-packages/e2e-tests/test-applications/solidstart/tests/performance.server.test.ts +++ b/dev-packages/e2e-tests/test-applications/solidstart/tests/performance.server.test.ts @@ -20,7 +20,7 @@ test('sends a server action transaction on pageload', async ({ page }) => { expect.objectContaining({ description: 'getPrefecture', data: { - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function.server_action', + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function', [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.solidstart', [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'component', }, @@ -45,7 +45,7 @@ test('sends a server action transaction on client navigation', async ({ page }) expect.objectContaining({ description: 'getPrefecture', data: { - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function.server_action', + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function', [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.solidstart', [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'component', }, diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-kit-tracing/tests/tracing.server.test.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2-kit-tracing/tests/tracing.server.test.ts index c5b0f7c98543..90f60315bddb 100644 --- a/dev-packages/e2e-tests/test-applications/sveltekit-2-kit-tracing/tests/tracing.server.test.ts +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-kit-tracing/tests/tracing.server.test.ts @@ -38,11 +38,11 @@ test('server pageload request span has nested request span for sub request', asy // initial resolve span: expect.objectContaining({ data: expect.objectContaining({ - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function.sveltekit.resolve', + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function', [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.sveltekit', 'http.route': '/server-load-fetch', }), - op: 'function.sveltekit.resolve', + op: 'function', description: 'sveltekit.resolve', origin: 'auto.http.sveltekit', status: 'ok', @@ -52,10 +52,10 @@ test('server pageload request span has nested request span for sub request', asy expect.objectContaining({ data: expect.objectContaining({ 'sentry.origin': 'auto.function.sveltekit.handle', - 'sentry.op': 'function.sveltekit.handle', + 'sentry.op': 'function', }), description: 'sveltekit.handle.sequenced.sentryRequestHandler', - op: 'function.sveltekit.handle', + op: 'function', origin: 'auto.function.sveltekit.handle', status: 'ok', }), @@ -64,14 +64,14 @@ test('server pageload request span has nested request span for sub request', asy expect.objectContaining({ data: expect.objectContaining({ 'http.route': '/server-load-fetch', - 'sentry.op': 'function.sveltekit.load', + 'sentry.op': 'function', 'sentry.origin': 'auto.function.sveltekit.load', 'sveltekit.load.environment': 'server', 'sveltekit.load.node_id': 'src/routes/server-load-fetch/+page.server.ts', 'sveltekit.load.node_type': '+page.server', }), description: 'sveltekit.load', - op: 'function.sveltekit.load', + op: 'function', origin: 'auto.function.sveltekit.load', status: 'ok', }), @@ -100,10 +100,10 @@ test('server pageload request span has nested request span for sub request', asy expect.objectContaining({ data: expect.objectContaining({ 'sentry.origin': 'auto.function.sveltekit.handle', - 'sentry.op': 'function.sveltekit.handle', + 'sentry.op': 'function', }), description: 'sveltekit.handle.sequenced.sentryRequestHandler', - op: 'function.sveltekit.handle', + op: 'function', origin: 'auto.function.sveltekit.handle', status: 'ok', }), @@ -112,11 +112,11 @@ test('server pageload request span has nested request span for sub request', asy expect.objectContaining({ data: expect.objectContaining({ 'http.route': '/api/users', - 'sentry.op': 'function.sveltekit.resolve', + 'sentry.op': 'function', 'sentry.origin': 'auto.http.sveltekit', }), description: 'sveltekit.resolve', - op: 'function.sveltekit.resolve', + op: 'function', origin: 'auto.http.sveltekit', status: 'ok', }), @@ -165,21 +165,21 @@ test('server trace includes form action span', async ({ page }) => { // sequenced handler span expect.objectContaining({ description: 'sveltekit.handle.sequenced.sentryRequestHandler', - op: 'function.sveltekit.handle', + op: 'function', origin: 'auto.function.sveltekit.handle', }), // resolve span expect.objectContaining({ description: 'sveltekit.resolve', - op: 'function.sveltekit.resolve', + op: 'function', origin: 'auto.http.sveltekit', }), // form action span expect.objectContaining({ description: 'sveltekit.form_action', - op: 'function.sveltekit.form_action', + op: 'function', origin: 'auto.function.sveltekit.action', data: expect.objectContaining({ 'sveltekit.form_action.name': 'default', diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-kit-tracing/tests/tracing.test.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2-kit-tracing/tests/tracing.test.ts index f4aafa58f9b3..32246281b534 100644 --- a/dev-packages/e2e-tests/test-applications/sveltekit-2-kit-tracing/tests/tracing.test.ts +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-kit-tracing/tests/tracing.test.ts @@ -52,7 +52,7 @@ test('capture a distributed pageload trace', async ({ page }) => { const serverKitResolveSpan = serverTxnEvent.spans?.find(s => s.description === 'sveltekit.resolve'); expect(serverKitResolveSpan).toMatchObject({ description: 'sveltekit.resolve', - op: 'function.sveltekit.resolve', + op: 'function', origin: 'auto.http.sveltekit', status: 'ok', }); diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/tests/performance.server.test.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/tests/performance.server.test.ts index f065f5148411..e98324cd0071 100644 --- a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/tests/performance.server.test.ts +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/tests/performance.server.test.ts @@ -26,7 +26,7 @@ test('server pageload request span has nested request span for sub request', asy expect(spans).toEqual( expect.arrayContaining([ // load span where the server load function initiates the sub request: - expect.objectContaining({ op: 'function.sveltekit.server.load', description: '/server-load-fetch' }), + expect.objectContaining({ op: 'function', description: '/server-load-fetch' }), // sub request span: expect.objectContaining({ op: 'http.server', description: 'GET /api/users' }), ]), diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2/tests/performance.server.test.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2/tests/performance.server.test.ts index 9fd87b052374..1195899aa42c 100644 --- a/dev-packages/e2e-tests/test-applications/sveltekit-2/tests/performance.server.test.ts +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2/tests/performance.server.test.ts @@ -26,7 +26,7 @@ test('server pageload request span has nested request span for sub request', asy expect(spans).toEqual( expect.arrayContaining([ // load span where the server load function initiates the sub request: - expect.objectContaining({ op: 'function.sveltekit.server.load', description: '/server-load-fetch' }), + expect.objectContaining({ op: 'function', description: '/server-load-fetch' }), // sub request span: expect.objectContaining({ op: 'http.server', description: 'GET /api/users' }), ]), diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-3/tests/tracing.server.test.ts b/dev-packages/e2e-tests/test-applications/sveltekit-3/tests/tracing.server.test.ts index f988ed0ae4b1..7fc7e18f8e1d 100644 --- a/dev-packages/e2e-tests/test-applications/sveltekit-3/tests/tracing.server.test.ts +++ b/dev-packages/e2e-tests/test-applications/sveltekit-3/tests/tracing.server.test.ts @@ -38,11 +38,11 @@ test('server pageload request span has nested request span for sub request', asy // initial resolve span: expect.objectContaining({ data: expect.objectContaining({ - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function.sveltekit.resolve', + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function', [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.sveltekit', 'http.route': '/server-load-fetch', }), - op: 'function.sveltekit.resolve', + op: 'function', description: 'sveltekit.resolve', origin: 'auto.http.sveltekit', status: 'ok', @@ -52,10 +52,10 @@ test('server pageload request span has nested request span for sub request', asy expect.objectContaining({ data: expect.objectContaining({ 'sentry.origin': 'auto.function.sveltekit.handle', - 'sentry.op': 'function.sveltekit.handle', + 'sentry.op': 'function', }), description: 'sveltekit.handle.sequenced.sentryRequestHandler', - op: 'function.sveltekit.handle', + op: 'function', origin: 'auto.function.sveltekit.handle', status: 'ok', }), @@ -64,14 +64,14 @@ test('server pageload request span has nested request span for sub request', asy expect.objectContaining({ data: expect.objectContaining({ 'http.route': '/server-load-fetch', - 'sentry.op': 'function.sveltekit.load', + 'sentry.op': 'function', 'sentry.origin': 'auto.function.sveltekit.load', 'sveltekit.load.environment': 'server', 'sveltekit.load.node_id': 'src/routes/server-load-fetch/+page.server.ts', 'sveltekit.load.node_type': '+page.server', }), description: 'sveltekit.load', - op: 'function.sveltekit.load', + op: 'function', origin: 'auto.function.sveltekit.load', status: 'ok', }), @@ -99,10 +99,10 @@ test('server pageload request span has nested request span for sub request', asy expect.objectContaining({ data: expect.objectContaining({ 'sentry.origin': 'auto.function.sveltekit.handle', - 'sentry.op': 'function.sveltekit.handle', + 'sentry.op': 'function', }), description: 'sveltekit.handle.sequenced.sentryRequestHandler', - op: 'function.sveltekit.handle', + op: 'function', origin: 'auto.function.sveltekit.handle', status: 'ok', }), @@ -111,11 +111,11 @@ test('server pageload request span has nested request span for sub request', asy expect.objectContaining({ data: expect.objectContaining({ 'http.route': '/api/users', - 'sentry.op': 'function.sveltekit.resolve', + 'sentry.op': 'function', 'sentry.origin': 'auto.http.sveltekit', }), description: 'sveltekit.resolve', - op: 'function.sveltekit.resolve', + op: 'function', origin: 'auto.http.sveltekit', status: 'ok', }), @@ -168,21 +168,21 @@ test.skip('server trace includes form action span', async ({ page }) => { // sequenced handler span expect.objectContaining({ description: 'sveltekit.handle.sequenced.sentryRequestHandler', - op: 'function.sveltekit.handle', + op: 'function', origin: 'auto.function.sveltekit.handle', }), // resolve span expect.objectContaining({ description: 'sveltekit.resolve', - op: 'function.sveltekit.resolve', + op: 'function', origin: 'auto.http.sveltekit', }), // form action span expect.objectContaining({ description: 'sveltekit.form_action', - op: 'function.sveltekit.form_action', + op: 'function', origin: 'auto.function.sveltekit.action', data: expect.objectContaining({ 'sveltekit.form_action.name': 'default', diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-3/tests/tracing.test.ts b/dev-packages/e2e-tests/test-applications/sveltekit-3/tests/tracing.test.ts index d57704d69412..d731661dfcb2 100644 --- a/dev-packages/e2e-tests/test-applications/sveltekit-3/tests/tracing.test.ts +++ b/dev-packages/e2e-tests/test-applications/sveltekit-3/tests/tracing.test.ts @@ -47,7 +47,7 @@ test('capture a distributed pageload trace', async ({ page }) => { const serverKitResolveSpan = serverTxnEvent.spans?.find(s => s.description === 'sveltekit.resolve'); expect(serverKitResolveSpan).toMatchObject({ description: 'sveltekit.resolve', - op: 'function.sveltekit.resolve', + op: 'function', origin: 'auto.http.sveltekit', status: 'ok', }); diff --git a/dev-packages/e2e-tests/test-applications/tanstackstart-react-cloudflare/tests/transaction.test.ts b/dev-packages/e2e-tests/test-applications/tanstackstart-react-cloudflare/tests/transaction.test.ts index e884ac474b3a..91b3130ff0dc 100644 --- a/dev-packages/e2e-tests/test-applications/tanstackstart-react-cloudflare/tests/transaction.test.ts +++ b/dev-packages/e2e-tests/test-applications/tanstackstart-react-cloudflare/tests/transaction.test.ts @@ -26,10 +26,10 @@ test('Sends a server function transaction with span from wrapFetchWithSentry', a expect(transactionEvent?.spans).toEqual([ expect.objectContaining({ description: 'GET /_serverFn/testLog', - op: 'function.tanstackstart', + op: 'function', origin: 'auto.function.tanstackstart.server', data: { - 'sentry.op': 'function.tanstackstart', + 'sentry.op': 'function', 'sentry.origin': 'auto.function.tanstackstart.server', 'sentry.source': 'route', 'tanstackstart.function.id': expect.any(String), @@ -65,10 +65,10 @@ test('Sends a server function transaction for a nested server function with manu expect.arrayContaining([ expect.objectContaining({ description: 'GET /_serverFn/testNestedLog', - op: 'function.tanstackstart', + op: 'function', origin: 'auto.function.tanstackstart.server', data: { - 'sentry.op': 'function.tanstackstart', + 'sentry.op': 'function', 'sentry.origin': 'auto.function.tanstackstart.server', 'sentry.source': 'route', 'tanstackstart.function.id': expect.any(String), 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 621ee3f88e02..87dfcf45edd8 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 @@ -28,10 +28,10 @@ test('Sends a server function transaction with auto-instrumentation', async ({ p expect.arrayContaining([ expect.objectContaining({ description: 'GET /_serverFn/testLog', - op: 'function.tanstackstart', + op: 'function', origin: 'auto.function.tanstackstart.server', data: { - 'sentry.op': 'function.tanstackstart', + 'sentry.op': 'function', 'sentry.origin': 'auto.function.tanstackstart.server', 'sentry.source': 'route', 'tanstackstart.function.id': expect.any(String), @@ -68,10 +68,10 @@ test('Sends a server function transaction for a nested server function only if i expect.arrayContaining([ expect.objectContaining({ description: 'GET /_serverFn/testNestedLog', - op: 'function.tanstackstart', + op: 'function', origin: 'auto.function.tanstackstart.server', data: { - 'sentry.op': 'function.tanstackstart', + 'sentry.op': 'function', 'sentry.origin': 'auto.function.tanstackstart.server', 'sentry.source': 'route', 'tanstackstart.function.id': expect.any(String), diff --git a/packages/nextjs/src/common/utils/tracingUtils.ts b/packages/nextjs/src/common/utils/tracingUtils.ts index 03f95bba17e3..7c6ab23eb220 100644 --- a/packages/nextjs/src/common/utils/tracingUtils.ts +++ b/packages/nextjs/src/common/utils/tracingUtils.ts @@ -1,4 +1,5 @@ import { HTTP_ROUTE, SENTRY_OP } from '@sentry/conventions/attributes'; +import { WEB_SERVER_FUNCTION_SPAN_OP } from '@sentry/conventions/op'; import type { PropagationContext, Span, SpanAttributes } from '@sentry/core'; import { isObjectLike, Scope, SEMANTIC_ATTRIBUTE_SENTRY_SOURCE } from '@sentry/core'; import { ATTR_NEXT_SEGMENT, ATTR_NEXT_SPAN_NAME, ATTR_NEXT_SPAN_TYPE } from '../nextSpanAttributes'; @@ -109,7 +110,7 @@ export function maybeEnhanceServerComponentSpanName( activeSpan.setAttributes({ 'sentry.nextjs.ssr.function.type': segment === PAGE_SEGMENT ? 'Page' : 'Layout', 'sentry.nextjs.ssr.function.route': route, - [SENTRY_OP]: 'function.nextjs', + [SENTRY_OP]: WEB_SERVER_FUNCTION_SPAN_OP, [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route', }); } diff --git a/packages/nextjs/src/common/withServerActionInstrumentation.ts b/packages/nextjs/src/common/withServerActionInstrumentation.ts index 5aedfa66558f..a0551f5c0256 100644 --- a/packages/nextjs/src/common/withServerActionInstrumentation.ts +++ b/packages/nextjs/src/common/withServerActionInstrumentation.ts @@ -17,7 +17,8 @@ import { import { flushSafelyWithTimeout, waitUntil } from '../common/utils/responseEnd'; import { DEBUG_BUILD } from './debug-build'; import { isNotFoundNavigationError, isRedirectNavigationError } from './nextNavigationErrorUtils'; -import { SENTRY_KIND } from '@sentry/conventions/attributes'; +import { SENTRY_KIND, SENTRY_OP } from '@sentry/conventions/attributes'; +import { WEB_SERVER_FUNCTION_SPAN_OP } from '@sentry/conventions/op'; interface Options { formData?: FormData; @@ -113,11 +114,11 @@ async function withServerActionInstrumentationImplementation { expect.objectContaining({ name: 'Fetcher fetcher-1', attributes: expect.objectContaining({ - 'sentry.op': 'function.react_router.fetcher', + 'sentry.op': 'function', + 'code.function.name': 'fetcher', 'sentry.origin': 'auto.function.react_router.instrumentation_api', }), }), @@ -252,7 +253,8 @@ describe('createSentryClientInstrumentation', () => { expect.objectContaining({ name: '/users/:id', attributes: expect.objectContaining({ - 'sentry.op': 'function.react_router.client_loader', + 'sentry.op': 'function', + 'code.function.name': 'clientLoader', 'sentry.origin': 'auto.function.react_router.instrumentation_api', }), }), @@ -289,7 +291,8 @@ describe('createSentryClientInstrumentation', () => { expect.objectContaining({ name: '/users/:id', attributes: expect.objectContaining({ - 'sentry.op': 'function.react_router.client_action', + 'sentry.op': 'function', + 'code.function.name': 'clientAction', 'sentry.origin': 'auto.function.react_router.instrumentation_api', }), }), @@ -605,7 +608,8 @@ describe('createSentryClientInstrumentation', () => { expect.objectContaining({ name: 'middleware test-route', attributes: expect.objectContaining({ - 'sentry.op': 'function.react_router.client_middleware', + 'sentry.op': 'function', + 'code.function.name': 'clientMiddleware', 'sentry.origin': 'auto.function.react_router.instrumentation_api', 'react_router.route.id': 'test-route', 'http.route': '/users/:id', @@ -638,7 +642,8 @@ describe('createSentryClientInstrumentation', () => { expect.objectContaining({ name: 'Lazy Route Load', attributes: expect.objectContaining({ - 'sentry.op': 'function.react_router.client_lazy', + 'sentry.op': 'function', + 'code.function.name': 'lazy', 'sentry.origin': 'auto.function.react_router.instrumentation_api', }), }), diff --git a/packages/react-router/test/server/createServerInstrumentation.test.ts b/packages/react-router/test/server/createServerInstrumentation.test.ts index 12d01c86d15c..0928edec39a9 100644 --- a/packages/react-router/test/server/createServerInstrumentation.test.ts +++ b/packages/react-router/test/server/createServerInstrumentation.test.ts @@ -267,7 +267,8 @@ describe('createSentryServerInstrumentation', () => { expect.objectContaining({ name: '/users/:id', attributes: expect.objectContaining({ - 'sentry.op': 'function.react_router.loader', + 'sentry.op': 'function', + 'code.function.name': 'loader', 'sentry.origin': 'auto.function.react_router.instrumentation_api', }), }), @@ -307,7 +308,8 @@ describe('createSentryServerInstrumentation', () => { expect.objectContaining({ name: '/users/:id', attributes: expect.objectContaining({ - 'sentry.op': 'function.react_router.action', + 'sentry.op': 'function', + 'code.function.name': 'action', 'sentry.origin': 'auto.function.react_router.instrumentation_api', }), }), @@ -364,7 +366,8 @@ describe('createSentryServerInstrumentation', () => { expect.objectContaining({ name: 'middleware test-route', attributes: expect.objectContaining({ - 'sentry.op': 'function.react_router.middleware', + 'sentry.op': 'function', + 'code.function.name': 'middleware', 'sentry.origin': 'auto.function.react_router.instrumentation_api', 'react_router.route.id': 'test-route', 'http.route': '/users/:id', @@ -395,7 +398,8 @@ describe('createSentryServerInstrumentation', () => { expect.objectContaining({ name: 'middleware authMiddleware', attributes: expect.objectContaining({ - 'sentry.op': 'function.react_router.middleware', + 'sentry.op': 'function', + 'code.function.name': 'middleware', 'react_router.route.id': 'routes/protected', 'http.route': '/protected', 'react_router.middleware.name': 'authMiddleware', @@ -444,9 +448,7 @@ describe('createSentryServerInstrumentation', () => { await hooks.middleware(mockCallMiddleware, requestInfo); // Filter to only middleware spans - const middlewareSpans = startSpanCalls.filter( - opts => opts.attributes?.['sentry.op'] === 'function.react_router.middleware', - ); + const middlewareSpans = startSpanCalls.filter(opts => opts.attributes?.['code.function.name'] === 'middleware'); expect(middlewareSpans).toHaveLength(3); expect(middlewareSpans[0].attributes['react_router.middleware.index']).toBe(0); @@ -477,7 +479,8 @@ describe('createSentryServerInstrumentation', () => { expect.objectContaining({ name: 'Lazy Route Load', attributes: expect.objectContaining({ - 'sentry.op': 'function.react_router.lazy', + 'sentry.op': 'function', + 'code.function.name': 'lazy', 'sentry.origin': 'auto.function.react_router.instrumentation_api', }), }), diff --git a/packages/remix/src/server/instrumentServer.ts b/packages/remix/src/server/instrumentServer.ts index 53a7fd98307a..f7b98d100028 100644 --- a/packages/remix/src/server/instrumentServer.ts +++ b/packages/remix/src/server/instrumentServer.ts @@ -40,7 +40,8 @@ import { createRoutes, getTransactionName, isCloudflareEnv } from '../utils/util import { extractData, isResponse, json } from '../utils/vendor/response'; import { captureRemixServerException, errorHandleDataFunction } from './errors'; import { generateSentryServerTimingHeader, injectServerTimingHeaderValue } from './serverTimingTracePropagation'; -import { HTTP_ROUTE, URL_FULL, URL_PATH } from '@sentry/conventions/attributes'; +import { CODE_FUNCTION_NAME, HTTP_ROUTE, SENTRY_OP, URL_FULL, URL_PATH } from '@sentry/conventions/attributes'; +import { WEB_SERVER_FUNCTION_SPAN_OP } from '@sentry/conventions/op'; type AppData = unknown; type RemixRequest = Parameters[0]; @@ -135,7 +136,7 @@ function makeWrappedDocumentRequestFunction(instrumentTracing?: boolean) { method: request.method, [URL_FULL]: request.url, [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.remix', - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function.remix.document_request', + [SENTRY_OP]: WEB_SERVER_FUNCTION_SPAN_OP, }, }, () => { @@ -206,11 +207,11 @@ function makeWrappedDataFunction( res = await startSpan( { - op: `function.remix.${name}`, name: id, attributes: { [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.remix', - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: `function.remix.${name}`, + [SENTRY_OP]: WEB_SERVER_FUNCTION_SPAN_OP, + [CODE_FUNCTION_NAME]: name, name, }, }, diff --git a/packages/remix/src/server/integrations/tracing-channel.ts b/packages/remix/src/server/integrations/tracing-channel.ts index d5cc0bfeadfc..234a7a0c30d3 100644 --- a/packages/remix/src/server/integrations/tracing-channel.ts +++ b/packages/remix/src/server/integrations/tracing-channel.ts @@ -21,7 +21,9 @@ import { URL_FULL, URL_PATH, SENTRY_KIND, + SENTRY_OP, } from '@sentry/conventions/attributes'; +import { WEB_SERVER_FUNCTION_SPAN_OP } from '@sentry/conventions/op'; import { remixChannels } from '@sentry/server-utils/orchestrion'; import type { FormDataCapture } from '../../utils/formData'; import { applyFormDataAttributes } from '../../utils/formData'; @@ -179,7 +181,7 @@ function subscribeCallRouteLoader(): void { name: `LOADER ${params.routeId}`, attributes: { [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN, - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'loader.remix', + [SENTRY_OP]: WEB_SERVER_FUNCTION_SPAN_OP, [CODE_FUNCTION]: 'loader', ...getRequestAttributes(params.request), ...getMatchAttributes(params), @@ -213,7 +215,7 @@ function subscribeCallRouteAction(formDataCapture: FormDataCapture | undefined): name: `ACTION ${params.routeId}`, attributes: { [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN, - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'action.remix', + [SENTRY_OP]: WEB_SERVER_FUNCTION_SPAN_OP, [CODE_FUNCTION]: 'action', ...getRequestAttributes(params.request), ...getMatchAttributes(params), diff --git a/packages/remix/test/server/tracing-channel-no-form-data.test.ts b/packages/remix/test/server/tracing-channel-no-form-data.test.ts index a8dbd64bfd1f..421e05bb2791 100644 --- a/packages/remix/test/server/tracing-channel-no-form-data.test.ts +++ b/packages/remix/test/server/tracing-channel-no-form-data.test.ts @@ -54,7 +54,7 @@ describe('remixIntegration with orchestrion (no form-data capture configured)', expect.objectContaining({ name: 'ACTION routes/submit', attributes: expect.objectContaining({ - 'sentry.op': 'action.remix', + 'sentry.op': 'function', 'code.function': 'action', 'http.method': 'POST', }), diff --git a/packages/remix/test/server/tracing-channel.test.ts b/packages/remix/test/server/tracing-channel.test.ts index bbd663e5ed78..bd2e2d6f9ecc 100644 --- a/packages/remix/test/server/tracing-channel.test.ts +++ b/packages/remix/test/server/tracing-channel.test.ts @@ -102,7 +102,7 @@ describe('remixIntegration (Orchestrion-based)', () => { name: 'LOADER routes/users.$userId', attributes: expect.objectContaining({ 'sentry.origin': 'auto.http.remix', - 'sentry.op': 'loader.remix', + 'sentry.op': 'function', 'code.function': 'loader', 'http.method': 'GET', 'url.full': 'http://localhost/users/123', @@ -141,7 +141,7 @@ describe('remixIntegration (Orchestrion-based)', () => { expect.objectContaining({ name: 'ACTION routes/submit', attributes: expect.objectContaining({ - 'sentry.op': 'action.remix', + 'sentry.op': 'function', 'code.function': 'action', 'http.method': 'POST', }), diff --git a/packages/solidstart/src/server/withServerActionInstrumentation.ts b/packages/solidstart/src/server/withServerActionInstrumentation.ts index 985a44dcc6d0..cb7079b62b35 100644 --- a/packages/solidstart/src/server/withServerActionInstrumentation.ts +++ b/packages/solidstart/src/server/withServerActionInstrumentation.ts @@ -6,7 +6,8 @@ import { } from '@sentry/core'; import { captureException, getActiveSpan, SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, spanToJSON, startSpan } from '@sentry/node'; import { isRedirect } from './utils'; -import { HTTP_ROUTE, HTTP_TARGET } from '@sentry/conventions/attributes'; +import { HTTP_ROUTE, HTTP_TARGET, SENTRY_OP } from '@sentry/conventions/attributes'; +import { WEB_SERVER_FUNCTION_SPAN_OP } from '@sentry/conventions/op'; import { setHttpServerSpanRouteAttribute } from '@sentry/server-utils'; /** @@ -36,9 +37,9 @@ export async function withServerActionInstrumentation { await serverActionGetPrefecture(); expect(spanStartMock).toHaveBeenCalledWith( expect.objectContaining({ - op: 'function.server_action', + op: 'function', description: 'getPrefecture', data: expect.objectContaining({ - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function.server_action', + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function', [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'component', [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.solidstart', }), diff --git a/packages/sveltekit/src/client/load.ts b/packages/sveltekit/src/client/load.ts index 79b4b54ba01d..bc95867e718a 100644 --- a/packages/sveltekit/src/client/load.ts +++ b/packages/sveltekit/src/client/load.ts @@ -6,6 +6,8 @@ import { SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, startSpan, } from '@sentry/core'; +import { CODE_FUNCTION_NAME, SENTRY_OP } from '@sentry/conventions/attributes'; +import { GENERAL_FUNCTION_SPAN_OP } from '@sentry/conventions/op'; import { captureException } from '@sentry/svelte'; import type { LoadEvent } from '@sveltejs/kit'; import type { SentryWrappedFlag } from '../common/utils'; @@ -76,8 +78,9 @@ export function wrapLoadWithSentry any>(origLoad: T) return startSpan( { - op: 'function.sveltekit.load', attributes: { + [SENTRY_OP]: GENERAL_FUNCTION_SPAN_OP, + [CODE_FUNCTION_NAME]: 'load', [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.sveltekit', [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: routeId ? 'route' : 'url', }, diff --git a/packages/sveltekit/src/server-common/integrations/svelteKitSpans.ts b/packages/sveltekit/src/server-common/integrations/svelteKitSpans.ts index dd8912f54a65..c59f7dc66509 100644 --- a/packages/sveltekit/src/server-common/integrations/svelteKitSpans.ts +++ b/packages/sveltekit/src/server-common/integrations/svelteKitSpans.ts @@ -1,9 +1,7 @@ import type { Integration, SpanJSON, SpanOrigin, StreamedSpanJSON } from '@sentry/core'; -import { - safeSetSpanJSONAttributes, - SEMANTIC_ATTRIBUTE_SENTRY_OP, - SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, -} from '@sentry/core'; +import { safeSetSpanJSONAttributes, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/core'; +import { SENTRY_OP } from '@sentry/conventions/attributes'; +import { WEB_SERVER_FUNCTION_SPAN_OP } from '@sentry/conventions/op'; /** * A small integration that preprocesses spans so that SvelteKit-generated spans @@ -35,17 +33,20 @@ export function svelteKitSpansIntegration(): Integration { * @exported for testing */ export function _enhanceKitSpan(span: SpanJSON): void { - const { op, origin } = _getKitSpanEnhancement(span.description); + const origin = _getKitSpanOrigin(span.description); + if (!origin) { + return; + } - const previousOp = span.op || span.data[SEMANTIC_ATTRIBUTE_SENTRY_OP]; + const previousOp = span.op || span.data[SENTRY_OP]; const previousOrigin = span.origin || span.data[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]; - if (!previousOp && op) { - span.op = op; - span.data[SEMANTIC_ATTRIBUTE_SENTRY_OP] = op; + if (!previousOp) { + span.op = WEB_SERVER_FUNCTION_SPAN_OP; + span.data[SENTRY_OP] = WEB_SERVER_FUNCTION_SPAN_OP; } - if ((!previousOrigin || previousOrigin === 'manual') && origin) { + if (!previousOrigin || previousOrigin === 'manual') { span.origin = origin; span.data[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN] = origin; } @@ -56,14 +57,16 @@ export function _enhanceKitSpan(span: SpanJSON): void { * @exported for testing */ export function _enhanceKitSpanStreamed(span: StreamedSpanJSON): void { - const { op, origin } = _getKitSpanEnhancement(span.name); + const origin = _getKitSpanOrigin(span.name); + if (!origin) { + return; + } + const previousOrigin = span.attributes?.[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN] as SpanOrigin | undefined; - if (op) { - safeSetSpanJSONAttributes(span, { [SEMANTIC_ATTRIBUTE_SENTRY_OP]: op }); - } + safeSetSpanJSONAttributes(span, { [SENTRY_OP]: WEB_SERVER_FUNCTION_SPAN_OP }); - if (previousOrigin === 'manual' && origin) { + if (previousOrigin === 'manual') { // `safeSetSpanJSONAttributes` skips existing keys, so overwrite the 'manual' sentinel directly. span.attributes![SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN] = origin; } else { @@ -71,27 +74,24 @@ export function _enhanceKitSpanStreamed(span: StreamedSpanJSON): void { } } -function _getKitSpanEnhancement(spanName: string | undefined): { - op?: string; - origin?: SpanOrigin; -} { +function _getKitSpanOrigin(spanName: string | undefined): SpanOrigin | undefined { switch (spanName) { case 'sveltekit.resolve': - return { op: 'function.sveltekit.resolve', origin: 'auto.http.sveltekit' }; + return 'auto.http.sveltekit'; case 'sveltekit.load': - return { op: 'function.sveltekit.load', origin: 'auto.function.sveltekit.load' }; + return 'auto.function.sveltekit.load'; case 'sveltekit.form_action': - return { op: 'function.sveltekit.form_action', origin: 'auto.function.sveltekit.action' }; + return 'auto.function.sveltekit.action'; case 'sveltekit.remote.call': - return { op: 'function.sveltekit.remote', origin: 'auto.rpc.sveltekit.remote' }; + return 'auto.rpc.sveltekit.remote'; case 'sveltekit.handle.root': // We don't want to overwrite the root handle span at this point since // we already enhance the root span in our `sentryHandle` hook. - return {}; + return undefined; default: if (spanName?.startsWith('sveltekit.handle.sequenced.')) { - return { op: 'function.sveltekit.handle', origin: 'auto.function.sveltekit.handle' }; + return 'auto.function.sveltekit.handle'; } - return {}; + return undefined; } } diff --git a/packages/sveltekit/src/server-common/load.ts b/packages/sveltekit/src/server-common/load.ts index 5e74e23b08aa..b76475052aae 100644 --- a/packages/sveltekit/src/server-common/load.ts +++ b/packages/sveltekit/src/server-common/load.ts @@ -5,6 +5,8 @@ import { SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, startSpan, } from '@sentry/core'; +import { CODE_FUNCTION_NAME, SENTRY_OP } from '@sentry/conventions/attributes'; +import { WEB_SERVER_FUNCTION_SPAN_OP } from '@sentry/conventions/op'; import type { LoadEvent, ServerLoadEvent } from '@sveltejs/kit'; import type { SentryWrappedFlag } from '../common/utils'; import { getRouteId } from '../common/utils'; @@ -40,8 +42,9 @@ export function wrapLoadWithSentry any>(origLoad: T) // We need to await before returning, otherwise we won't catch any errors thrown by the load function return await startSpan( { - op: 'function.sveltekit.load', attributes: { + [SENTRY_OP]: WEB_SERVER_FUNCTION_SPAN_OP, + [CODE_FUNCTION_NAME]: 'load', [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.sveltekit', [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: routeId ? 'route' : 'url', }, @@ -103,8 +106,9 @@ export function wrapServerLoadWithSentry any>(origSe // We need to await before returning, otherwise we won't catch any errors thrown by the load function return await startSpan( { - op: 'function.sveltekit.server.load', attributes: { + [SENTRY_OP]: WEB_SERVER_FUNCTION_SPAN_OP, + [CODE_FUNCTION_NAME]: 'load', [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.sveltekit', [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: routeId ? 'route' : 'url', 'http.method': event.request.method, diff --git a/packages/sveltekit/src/server-common/serverRoute.ts b/packages/sveltekit/src/server-common/serverRoute.ts index dea2029fe144..e3dbae9dc12c 100644 --- a/packages/sveltekit/src/server-common/serverRoute.ts +++ b/packages/sveltekit/src/server-common/serverRoute.ts @@ -5,6 +5,8 @@ import { SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, startSpan, } from '@sentry/core'; +import { CODE_FUNCTION_NAME, SENTRY_OP } from '@sentry/conventions/attributes'; +import { WEB_SERVER_FUNCTION_SPAN_OP } from '@sentry/conventions/op'; import type { RequestEvent } from '@sveltejs/kit'; import { sendErrorToSentry } from './utils'; @@ -52,10 +54,12 @@ export function wrapServerRouteWithSentry( return await startSpan( { name: `${httpMethod} ${routeId || 'Server Route'}`, - op: `function.sveltekit.server.${httpMethod.toLowerCase()}`, attributes: { + [SENTRY_OP]: WEB_SERVER_FUNCTION_SPAN_OP, + [CODE_FUNCTION_NAME]: httpMethod, [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.sveltekit', [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route', + 'http.method': httpMethod, }, onlyIfParent: true, }, diff --git a/packages/sveltekit/test/client/load.test.ts b/packages/sveltekit/test/client/load.test.ts index f91926661375..4cd67149ca05 100644 --- a/packages/sveltekit/test/client/load.test.ts +++ b/packages/sveltekit/test/client/load.test.ts @@ -1,4 +1,8 @@ -import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SEMANTIC_ATTRIBUTE_SENTRY_SOURCE } from '@sentry/core'; +import { + SEMANTIC_ATTRIBUTE_SENTRY_OP, + SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, + SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, +} from '@sentry/core'; import * as SentrySvelte from '@sentry/svelte'; import type { Load } from '@sveltejs/kit'; import { redirect } from '@sveltejs/kit'; @@ -103,10 +107,11 @@ describe('wrapLoadWithSentry', () => { expect(mockStartSpan).toHaveBeenCalledWith( { attributes: { + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function', + 'code.function.name': 'load', [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.sveltekit', [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route', }, - op: 'function.sveltekit.load', name: '/users/[id]', }, expect.any(Function), @@ -130,10 +135,11 @@ describe('wrapLoadWithSentry', () => { expect(mockStartSpan).toHaveBeenCalledWith( { attributes: { + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function', + 'code.function.name': 'load', [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.sveltekit', [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url', }, - op: 'function.sveltekit.load', name: '/users/123', }, expect.any(Function), diff --git a/packages/sveltekit/test/server-common/integrations/svelteKitSpans.test.ts b/packages/sveltekit/test/server-common/integrations/svelteKitSpans.test.ts index b051d613aad1..347c9f8fcf53 100644 --- a/packages/sveltekit/test/server-common/integrations/svelteKitSpans.test.ts +++ b/packages/sveltekit/test/server-common/integrations/svelteKitSpans.test.ts @@ -45,20 +45,20 @@ describe('svelteKitSpansIntegration', () => { svelteKitSpansIntegration().preprocessEvent?.(event, {}, {}); expect(event.spans).toHaveLength(1); - expect(event.spans?.[0]?.op).toBe('function.sveltekit.resolve'); + expect(event.spans?.[0]?.op).toBe('function'); expect(event.spans?.[0]?.origin).toBe('auto.http.sveltekit'); - expect(event.spans?.[0]?.data[SEMANTIC_ATTRIBUTE_SENTRY_OP]).toBe('function.sveltekit.resolve'); + expect(event.spans?.[0]?.data[SEMANTIC_ATTRIBUTE_SENTRY_OP]).toBe('function'); expect(event.spans?.[0]?.data[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]).toBe('auto.http.sveltekit'); }); describe('_enhanceKitSpan', () => { it.each([ - ['sveltekit.resolve', 'function.sveltekit.resolve', 'auto.http.sveltekit'], - ['sveltekit.load', 'function.sveltekit.load', 'auto.function.sveltekit.load'], - ['sveltekit.form_action', 'function.sveltekit.form_action', 'auto.function.sveltekit.action'], - ['sveltekit.remote.call', 'function.sveltekit.remote', 'auto.rpc.sveltekit.remote'], - ['sveltekit.handle.sequenced.0', 'function.sveltekit.handle', 'auto.function.sveltekit.handle'], - ['sveltekit.handle.sequenced.myHandler', 'function.sveltekit.handle', 'auto.function.sveltekit.handle'], + ['sveltekit.resolve', 'function', 'auto.http.sveltekit'], + ['sveltekit.load', 'function', 'auto.function.sveltekit.load'], + ['sveltekit.form_action', 'function', 'auto.function.sveltekit.action'], + ['sveltekit.remote.call', 'function', 'auto.rpc.sveltekit.remote'], + ['sveltekit.handle.sequenced.0', 'function', 'auto.function.sveltekit.handle'], + ['sveltekit.handle.sequenced.myHandler', 'function', 'auto.function.sveltekit.handle'], ])('enhances %s span with the correct op and origin', (spanName, op, origin) => { const span = { description: spanName, @@ -191,12 +191,12 @@ describe('svelteKitSpansIntegration', () => { } it.each([ - ['sveltekit.resolve', 'function.sveltekit.resolve', 'auto.http.sveltekit'], - ['sveltekit.load', 'function.sveltekit.load', 'auto.function.sveltekit.load'], - ['sveltekit.form_action', 'function.sveltekit.form_action', 'auto.function.sveltekit.action'], - ['sveltekit.remote.call', 'function.sveltekit.remote', 'auto.rpc.sveltekit.remote'], - ['sveltekit.handle.sequenced.0', 'function.sveltekit.handle', 'auto.function.sveltekit.handle'], - ['sveltekit.handle.sequenced.myHandler', 'function.sveltekit.handle', 'auto.function.sveltekit.handle'], + ['sveltekit.resolve', 'function', 'auto.http.sveltekit'], + ['sveltekit.load', 'function', 'auto.function.sveltekit.load'], + ['sveltekit.form_action', 'function', 'auto.function.sveltekit.action'], + ['sveltekit.remote.call', 'function', 'auto.rpc.sveltekit.remote'], + ['sveltekit.handle.sequenced.0', 'function', 'auto.function.sveltekit.handle'], + ['sveltekit.handle.sequenced.myHandler', 'function', 'auto.function.sveltekit.handle'], ])('enhances %s span with the correct op and origin', (spanName, op, origin) => { const span = makeStreamedSpan({ name: spanName, attributes: { someAttribute: 'someValue' } }); diff --git a/packages/sveltekit/test/server-common/load.test.ts b/packages/sveltekit/test/server-common/load.test.ts index 4ca7436bb8b2..14a10009e6f7 100644 --- a/packages/sveltekit/test/server-common/load.test.ts +++ b/packages/sveltekit/test/server-common/load.test.ts @@ -169,10 +169,11 @@ describe('wrapLoadWithSentry calls `startSpan`', () => { expect(mockStartSpan).toHaveBeenCalledWith( { attributes: { + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function', + 'code.function.name': 'load', [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.sveltekit', [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route', }, - op: 'function.sveltekit.load', name: '/users/[id]', }, expect.any(Function), @@ -187,10 +188,11 @@ describe('wrapLoadWithSentry calls `startSpan`', () => { expect(mockStartSpan).toHaveBeenCalledWith( { attributes: { + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function', + 'code.function.name': 'load', [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.sveltekit', [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url', }, - op: 'function.sveltekit.load', name: '/users/123', }, expect.any(Function), @@ -258,11 +260,12 @@ describe('wrapServerLoadWithSentry calls `startSpan`', () => { data: { [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.sveltekit', [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url', - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function.sveltekit.server.load', + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function', + 'code.function.name': 'load', 'http.method': 'GET', 'sentry.sample_rate': 1, }, - op: 'function.sveltekit.server.load', + op: 'function', span_id: expect.stringMatching(/[a-f0-9]{16}/), trace_id: expect.stringMatching(/[a-f0-9]{32}/), origin: 'auto.function.sveltekit', @@ -297,7 +300,9 @@ describe('wrapServerLoadWithSentry calls `startSpan`', () => { expect(mockStartSpan).toHaveBeenCalledTimes(1); expect(mockStartSpan).toHaveBeenCalledWith( expect.objectContaining({ - op: 'function.sveltekit.server.load', + attributes: expect.objectContaining({ + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function', + }), name: '/users/[id]', // <-- this shows that the route was still accessed }), expect.any(Function), diff --git a/packages/sveltekit/test/server-common/serverRoute.test.ts b/packages/sveltekit/test/server-common/serverRoute.test.ts index d98f75bf34f7..ac6dd6b03383 100644 --- a/packages/sveltekit/test/server-common/serverRoute.test.ts +++ b/packages/sveltekit/test/server-common/serverRoute.test.ts @@ -3,6 +3,7 @@ import type { NumericRange, RequestEvent } from '@sveltejs/kit'; import { error, redirect } from '@sveltejs/kit'; import { beforeEach, describe, expect, it, vi } from 'vitest'; import { + SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, wrapServerRouteWithSentry, @@ -36,12 +37,14 @@ describe('wrapServerRouteWithSentry', () => { expect(startSpanSpy).toHaveBeenCalledWith( { attributes: { + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function', + 'code.function.name': 'GET', [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.sveltekit', [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route', + 'http.method': 'GET', }, name: 'GET /api/users/:id', onlyIfParent: true, - op: 'function.sveltekit.server.get', }, expect.any(Function), ); @@ -57,12 +60,14 @@ describe('wrapServerRouteWithSentry', () => { expect(startSpanSpy).toHaveBeenCalledWith( { attributes: { + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function', + 'code.function.name': 'GET', [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.sveltekit', [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route', + 'http.method': 'GET', }, name: 'GET Server Route', onlyIfParent: true, - op: 'function.sveltekit.server.get', }, expect.any(Function), ); diff --git a/packages/tanstackstart-react/src/server/globalMiddleware.ts b/packages/tanstackstart-react/src/server/globalMiddleware.ts index 7a730bed37b0..9e89eafa5dde 100644 --- a/packages/tanstackstart-react/src/server/globalMiddleware.ts +++ b/packages/tanstackstart-react/src/server/globalMiddleware.ts @@ -38,7 +38,7 @@ function createSentryFunctionMiddlewareHandler(mechanismType: string) { }): Promise { const activeSpan = getActiveSpan(); const spanData = activeSpan ? spanToJSON(activeSpan) : undefined; - if (activeSpan && spanData?.op === 'function.tanstackstart') { + if (activeSpan && spanData?.origin === 'auto.function.tanstackstart.server') { if (serverFnMeta?.name) { const method = spanData.description?.split(' ')[0] || 'GET'; updateSpanName(activeSpan, `${method} /_serverFn/${serverFnMeta.name}`); diff --git a/packages/tanstackstart-react/src/server/wrapFetchWithSentry.ts b/packages/tanstackstart-react/src/server/wrapFetchWithSentry.ts index fab2788cd234..3704b43ba34f 100644 --- a/packages/tanstackstart-react/src/server/wrapFetchWithSentry.ts +++ b/packages/tanstackstart-react/src/server/wrapFetchWithSentry.ts @@ -1,10 +1,7 @@ import { flushIfServerless, getTraceMetaTags } from '@sentry/core'; -import { - captureException, - SEMANTIC_ATTRIBUTE_SENTRY_OP, - SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, - startSpan, -} from '@sentry/node'; +import { captureException, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, startSpan } from '@sentry/node'; +import { SENTRY_OP } from '@sentry/conventions/attributes'; +import { WEB_SERVER_FUNCTION_SPAN_OP } from '@sentry/conventions/op'; import { updateSpanWithRouteParametrization } from './routeParametrization'; declare const __SENTRY_ROUTE_PATTERNS__: string[] | undefined; @@ -142,15 +139,12 @@ export function wrapFetchWithSentry(serverEntry: ServerEntry): ServerEntry { // instrument server functions if (url.pathname.includes('_serverFn') || url.pathname.includes('createServerFn')) { - const op = 'function.tanstackstart'; - return await startSpan( { - op, name: `${method} ${url.pathname}`, attributes: { [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.tanstackstart.server', - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: op, + [SENTRY_OP]: WEB_SERVER_FUNCTION_SPAN_OP, }, }, async () => { diff --git a/packages/tanstackstart-react/test/server/wrapFetchWithSentry.test.ts b/packages/tanstackstart-react/test/server/wrapFetchWithSentry.test.ts index 80d5690af231..5928cefeb9b7 100644 --- a/packages/tanstackstart-react/test/server/wrapFetchWithSentry.test.ts +++ b/packages/tanstackstart-react/test/server/wrapFetchWithSentry.test.ts @@ -50,7 +50,7 @@ describe('wrapFetchWithSentry', () => { expect(flushIfServerlessSpy).toHaveBeenCalledTimes(1); }); - it('creates a function.tanstackstart span for server function requests', async () => { + it('creates a function span for server function requests', async () => { const mockResponse = new Response('ok'); const fetchFn = vi.fn().mockResolvedValue(mockResponse); @@ -61,8 +61,10 @@ describe('wrapFetchWithSentry', () => { expect(startSpanSpy).toHaveBeenCalledWith( expect.objectContaining({ - op: 'function.tanstackstart', name: 'GET /_serverFn/abc123', + attributes: expect.objectContaining({ + 'sentry.op': 'function', + }), }), expect.any(Function), ); From 12939ec197bde7e6ddd7f433b53658a87b06c877 Mon Sep 17 00:00:00 2001 From: Martin Sonnberger Date: Wed, 5 Aug 2026 11:38:39 +0200 Subject: [PATCH 2/5] fix e2e tests --- .../tests/errors/errors.server.test.ts | 2 +- .../tests/performance/lazy.server.test.ts | 8 ++++---- .../tests/performance/middleware.server.test.ts | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/react-router-7-framework-instrumentation/tests/errors/errors.server.test.ts b/dev-packages/e2e-tests/test-applications/react-router-7-framework-instrumentation/tests/errors/errors.server.test.ts index c84634582677..0cd30a5ad0c7 100644 --- a/dev-packages/e2e-tests/test-applications/react-router-7-framework-instrumentation/tests/errors/errors.server.test.ts +++ b/dev-packages/e2e-tests/test-applications/react-router-7-framework-instrumentation/tests/errors/errors.server.test.ts @@ -60,7 +60,7 @@ test.describe('server - instrumentation API error capture', () => { // Find the loader span const loaderSpan = transaction?.spans?.find( - (span: { data?: { 'code.function.name'?: string } }) => span.data?.['code.function.name'] === 'loader', + span => span.data?.['code.function.name'] === 'loader', ); expect(loaderSpan).toMatchObject({ diff --git a/dev-packages/e2e-tests/test-applications/react-router-7-framework-instrumentation/tests/performance/lazy.server.test.ts b/dev-packages/e2e-tests/test-applications/react-router-7-framework-instrumentation/tests/performance/lazy.server.test.ts index 3262deda8611..1719a9b43756 100644 --- a/dev-packages/e2e-tests/test-applications/react-router-7-framework-instrumentation/tests/performance/lazy.server.test.ts +++ b/dev-packages/e2e-tests/test-applications/react-router-7-framework-instrumentation/tests/performance/lazy.server.test.ts @@ -42,7 +42,7 @@ test.describe('server - instrumentation API lazy loading', () => { // Find the lazy span const lazySpan = transaction?.spans?.find( - (span: { data?: { 'code.function.name'?: string } }) => span.data?.['code.function.name'] === 'lazy', + span => span.data?.['code.function.name'] === 'lazy', ); expect(lazySpan).toMatchObject({ @@ -73,7 +73,7 @@ test.describe('server - instrumentation API lazy loading', () => { // Find the loader span that runs after lazy loading const loaderSpan = transaction?.spans?.find( - (span: { data?: { 'code.function.name'?: string } }) => span.data?.['code.function.name'] === 'loader', + span => span.data?.['code.function.name'] === 'loader', ); expect(loaderSpan).toMatchObject({ @@ -100,11 +100,11 @@ test.describe('server - instrumentation API lazy loading', () => { const transaction = await txPromise; const lazySpan = transaction?.spans?.find( - (span: { data?: { 'code.function.name'?: string } }) => span.data?.['code.function.name'] === 'lazy', + span => span.data?.['code.function.name'] === 'lazy', ); const loaderSpan = transaction?.spans?.find( - (span: { data?: { 'code.function.name'?: string } }) => span.data?.['code.function.name'] === 'loader', + span => span.data?.['code.function.name'] === 'loader', ); expect(lazySpan).toBeDefined(); diff --git a/dev-packages/e2e-tests/test-applications/react-router-7-framework-instrumentation/tests/performance/middleware.server.test.ts b/dev-packages/e2e-tests/test-applications/react-router-7-framework-instrumentation/tests/performance/middleware.server.test.ts index ca03e157cb6e..b25093a215e1 100644 --- a/dev-packages/e2e-tests/test-applications/react-router-7-framework-instrumentation/tests/performance/middleware.server.test.ts +++ b/dev-packages/e2e-tests/test-applications/react-router-7-framework-instrumentation/tests/performance/middleware.server.test.ts @@ -38,7 +38,7 @@ test.describe('server - instrumentation API middleware', () => { // Find the middleware span const middlewareSpan = transaction?.spans?.find( - (span: { data?: { 'code.function.name'?: string } }) => span.data?.['code.function.name'] === 'middleware', + span => span.data?.['code.function.name'] === 'middleware', ); expect(middlewareSpan).toBeDefined(); @@ -75,11 +75,11 @@ test.describe('server - instrumentation API middleware', () => { const transaction = await txPromise; const middlewareSpan = transaction?.spans?.find( - (span: { data?: { 'code.function.name'?: string } }) => span.data?.['code.function.name'] === 'middleware', + span => span.data?.['code.function.name'] === 'middleware', ); const loaderSpan = transaction?.spans?.find( - (span: { data?: { 'code.function.name'?: string } }) => span.data?.['code.function.name'] === 'loader', + span => span.data?.['code.function.name'] === 'loader', ); expect(middlewareSpan).toBeDefined(); @@ -102,7 +102,7 @@ test.describe('server - instrumentation API middleware', () => { await expect(page.locator('#multi-middleware-content')).toHaveText('This route has 3 middlewares'); const middlewareSpans = transaction?.spans?.filter( - (span: { data?: { 'code.function.name'?: string } }) => span.data?.['code.function.name'] === 'middleware', + span => span.data?.['code.function.name'] === 'middleware', ); expect(middlewareSpans).toHaveLength(3); From 09aace5538f5e9736bb2241a434687aa9f88b643 Mon Sep 17 00:00:00 2001 From: Martin Sonnberger Date: Wed, 5 Aug 2026 11:45:26 +0200 Subject: [PATCH 3/5] format --- .../tests/errors/errors.server.test.ts | 4 +--- .../tests/performance/lazy.server.test.ts | 16 ++++------------ .../tests/performance/middleware.server.test.ts | 16 ++++------------ 3 files changed, 9 insertions(+), 27 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/react-router-7-framework-instrumentation/tests/errors/errors.server.test.ts b/dev-packages/e2e-tests/test-applications/react-router-7-framework-instrumentation/tests/errors/errors.server.test.ts index 0cd30a5ad0c7..d4eadfdf0797 100644 --- a/dev-packages/e2e-tests/test-applications/react-router-7-framework-instrumentation/tests/errors/errors.server.test.ts +++ b/dev-packages/e2e-tests/test-applications/react-router-7-framework-instrumentation/tests/errors/errors.server.test.ts @@ -59,9 +59,7 @@ test.describe('server - instrumentation API error capture', () => { const transaction = await txPromise; // Find the loader span - const loaderSpan = transaction?.spans?.find( - span => span.data?.['code.function.name'] === 'loader', - ); + const loaderSpan = transaction?.spans?.find(span => span.data?.['code.function.name'] === 'loader'); expect(loaderSpan).toMatchObject({ data: { diff --git a/dev-packages/e2e-tests/test-applications/react-router-7-framework-instrumentation/tests/performance/lazy.server.test.ts b/dev-packages/e2e-tests/test-applications/react-router-7-framework-instrumentation/tests/performance/lazy.server.test.ts index 1719a9b43756..a0198e13b2fb 100644 --- a/dev-packages/e2e-tests/test-applications/react-router-7-framework-instrumentation/tests/performance/lazy.server.test.ts +++ b/dev-packages/e2e-tests/test-applications/react-router-7-framework-instrumentation/tests/performance/lazy.server.test.ts @@ -41,9 +41,7 @@ test.describe('server - instrumentation API lazy loading', () => { }); // Find the lazy span - const lazySpan = transaction?.spans?.find( - span => span.data?.['code.function.name'] === 'lazy', - ); + const lazySpan = transaction?.spans?.find(span => span.data?.['code.function.name'] === 'lazy'); expect(lazySpan).toMatchObject({ span_id: expect.any(String), @@ -72,9 +70,7 @@ test.describe('server - instrumentation API lazy loading', () => { const transaction = await txPromise; // Find the loader span that runs after lazy loading - const loaderSpan = transaction?.spans?.find( - span => span.data?.['code.function.name'] === 'loader', - ); + const loaderSpan = transaction?.spans?.find(span => span.data?.['code.function.name'] === 'loader'); expect(loaderSpan).toMatchObject({ span_id: expect.any(String), @@ -99,13 +95,9 @@ test.describe('server - instrumentation API lazy loading', () => { const transaction = await txPromise; - const lazySpan = transaction?.spans?.find( - span => span.data?.['code.function.name'] === 'lazy', - ); + const lazySpan = transaction?.spans?.find(span => span.data?.['code.function.name'] === 'lazy'); - const loaderSpan = transaction?.spans?.find( - span => span.data?.['code.function.name'] === 'loader', - ); + const loaderSpan = transaction?.spans?.find(span => span.data?.['code.function.name'] === 'loader'); expect(lazySpan).toBeDefined(); expect(loaderSpan).toBeDefined(); diff --git a/dev-packages/e2e-tests/test-applications/react-router-7-framework-instrumentation/tests/performance/middleware.server.test.ts b/dev-packages/e2e-tests/test-applications/react-router-7-framework-instrumentation/tests/performance/middleware.server.test.ts index b25093a215e1..11dc019761dd 100644 --- a/dev-packages/e2e-tests/test-applications/react-router-7-framework-instrumentation/tests/performance/middleware.server.test.ts +++ b/dev-packages/e2e-tests/test-applications/react-router-7-framework-instrumentation/tests/performance/middleware.server.test.ts @@ -37,9 +37,7 @@ test.describe('server - instrumentation API middleware', () => { }); // Find the middleware span - const middlewareSpan = transaction?.spans?.find( - span => span.data?.['code.function.name'] === 'middleware', - ); + const middlewareSpan = transaction?.spans?.find(span => span.data?.['code.function.name'] === 'middleware'); expect(middlewareSpan).toBeDefined(); expect(middlewareSpan).toMatchObject({ @@ -74,13 +72,9 @@ test.describe('server - instrumentation API middleware', () => { const transaction = await txPromise; - const middlewareSpan = transaction?.spans?.find( - span => span.data?.['code.function.name'] === 'middleware', - ); + const middlewareSpan = transaction?.spans?.find(span => span.data?.['code.function.name'] === 'middleware'); - const loaderSpan = transaction?.spans?.find( - span => span.data?.['code.function.name'] === 'loader', - ); + const loaderSpan = transaction?.spans?.find(span => span.data?.['code.function.name'] === 'loader'); expect(middlewareSpan).toBeDefined(); expect(loaderSpan).toBeDefined(); @@ -101,9 +95,7 @@ test.describe('server - instrumentation API middleware', () => { await expect(page.locator('#multi-middleware-title')).toBeVisible(); await expect(page.locator('#multi-middleware-content')).toHaveText('This route has 3 middlewares'); - const middlewareSpans = transaction?.spans?.filter( - span => span.data?.['code.function.name'] === 'middleware', - ); + const middlewareSpans = transaction?.spans?.filter(span => span.data?.['code.function.name'] === 'middleware'); expect(middlewareSpans).toHaveLength(3); From 380cc8885764b8e368c0284d8087436c8718dca4 Mon Sep 17 00:00:00 2001 From: Martin Sonnberger Date: Thu, 6 Aug 2026 16:24:55 +0200 Subject: [PATCH 4/5] code review --- .../tests/server-transactions.test.ts | 2 +- .../tests/server-transactions.test.ts | 24 ++++---- .../tests/server-transactions.test.ts | 2 +- .../tests/server-transactions.test.ts | 2 +- .../performance/middleware.server.test.ts | 4 +- .../performance/performance.server.test.ts | 57 ------------------- .../src/client/createClientInstrumentation.ts | 4 +- .../src/server/createServerInstrumentation.ts | 4 +- .../createClientInstrumentation.test.ts | 2 +- .../createServerInstrumentation.test.ts | 4 +- .../server/integrations/tracing-channel.ts | 8 +-- .../tracing-channel-no-form-data.test.ts | 2 +- .../remix/test/server/tracing-channel.test.ts | 6 +- 13 files changed, 33 insertions(+), 88 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/create-remix-app-express-vite-dev/tests/server-transactions.test.ts b/dev-packages/e2e-tests/test-applications/create-remix-app-express-vite-dev/tests/server-transactions.test.ts index 7edb459f380d..1b839666a325 100644 --- a/dev-packages/e2e-tests/test-applications/create-remix-app-express-vite-dev/tests/server-transactions.test.ts +++ b/dev-packages/e2e-tests/test-applications/create-remix-app-express-vite-dev/tests/server-transactions.test.ts @@ -40,7 +40,7 @@ test('Sends two linked transactions (server & client) to Sentry', async ({ page const httpServerTraceId = httpServerTransaction.contexts?.trace?.trace_id; const httpServerSpanId = httpServerTransaction.contexts?.trace?.span_id; const loaderSpanId = httpServerTransaction?.spans?.find( - span => span.data && span.data['code.function'] === 'loader', + span => span.data && span.data['code.function.name'] === 'loader', )?.span_id; const pageLoadTraceId = pageloadTransaction.contexts?.trace?.trace_id; diff --git a/dev-packages/e2e-tests/test-applications/create-remix-app-express/tests/server-transactions.test.ts b/dev-packages/e2e-tests/test-applications/create-remix-app-express/tests/server-transactions.test.ts index 3627d641a323..d05ef15250b1 100644 --- a/dev-packages/e2e-tests/test-applications/create-remix-app-express/tests/server-transactions.test.ts +++ b/dev-packages/e2e-tests/test-applications/create-remix-app-express/tests/server-transactions.test.ts @@ -19,7 +19,7 @@ test('Sends parameterized transaction name to Sentry', async ({ page }) => { test('Sends form data with action span', async ({ page }) => { const formdataActionTransaction = waitForTransaction('create-remix-app-express', transactionEvent => { - return transactionEvent?.spans?.some(span => span.data && span.data['code.function'] === 'action') || false; + return transactionEvent?.spans?.some(span => span.data && span.data['code.function.name'] === 'action') || false; }); await page.goto('/action-formdata'); @@ -34,11 +34,12 @@ test('Sends form data with action span', async ({ page }) => { await page.locator('button[type=submit]').click(); const actionSpan = (await formdataActionTransaction)?.spans?.find( - span => span.data && span.data['code.function'] === 'action', + span => span.data && span.data['code.function.name'] === 'action', ); expect(actionSpan).toBeDefined(); expect(actionSpan?.op).toBe('function'); + expect(actionSpan?.data?.['code.function.name']).toBe('action'); expect(actionSpan?.data).toMatchObject({ 'formData.text': 'test', 'formData.file': 'file.txt', @@ -47,17 +48,18 @@ test('Sends form data with action span', async ({ page }) => { test('Sends a loader span to Sentry', async ({ page }) => { const loaderTransactionPromise = waitForTransaction('create-remix-app-express', transactionEvent => { - return transactionEvent?.spans?.some(span => span.data && span.data['code.function'] === 'loader') || false; + return transactionEvent?.spans?.some(span => span.data && span.data['code.function.name'] === 'loader') || false; }); await page.goto('/'); const loaderSpan = (await loaderTransactionPromise)?.spans?.find( - span => span.data && span.data['code.function'] === 'loader', + span => span.data && span.data['code.function.name'] === 'loader', ); expect(loaderSpan).toBeDefined(); expect(loaderSpan?.op).toBe('function'); + expect(loaderSpan?.data?.['code.function.name']).toBe('loader'); }); test('Propagates trace when ErrorBoundary is triggered', async ({ page }) => { @@ -83,7 +85,7 @@ test('Propagates trace when ErrorBoundary is triggered', async ({ page }) => { const httpServerTraceId = httpServerTransaction.contexts?.trace?.trace_id; const httpServerSpanId = httpServerTransaction.contexts?.trace?.span_id; const loaderSpanId = httpServerTransaction?.spans?.find( - span => span.data && span.data['code.function'] === 'loader', + span => span.data && span.data['code.function.name'] === 'loader', )?.span_id; const pageLoadTraceId = pageloadTransaction.contexts?.trace?.trace_id; @@ -111,7 +113,7 @@ test('Parameterizes a 2-level nested route on the server', async ({ page }) => { const transaction = await transactionPromise; expect(transaction.contexts?.trace?.data?.['sentry.source']).toBe('route'); - expect(transaction.spans?.some(s => s.data?.['code.function'] === 'loader' && s.op === 'function')).toBe(true); + expect(transaction.spans?.some(s => s.data?.['code.function.name'] === 'loader' && s.op === 'function')).toBe(true); }); test('Parameterizes a 3-level nested API route on the server', async ({ page }) => { @@ -160,19 +162,19 @@ test('Records action and loader spans on a parameterized action route', async ({ const transaction = await transactionPromise; const actionSpan = transaction.spans?.find( - s => s.data?.['code.function'] === 'action' && s.data?.['match.route.id'] === 'routes/action-json-response.$id', + s => s.data?.['code.function.name'] === 'action' && s.data?.['match.route.id'] === 'routes/action-json-response.$id', ); expect(actionSpan).toBeDefined(); expect(actionSpan?.op).toBe('function'); expect(actionSpan?.data?.['match.params.id']).toBe('123123'); const rootLoaderSpan = transaction.spans?.find( - s => s.data?.['code.function'] === 'loader' && s.data?.['match.route.id'] === 'root', + s => s.data?.['code.function.name'] === 'loader' && s.data?.['match.route.id'] === 'root', ); expect(rootLoaderSpan).toBeDefined(); const routeLoaderSpan = transaction.spans?.find( - s => s.data?.['code.function'] === 'loader' && s.data?.['match.route.id'] === 'routes/action-json-response.$id', + s => s.data?.['code.function.name'] === 'loader' && s.data?.['match.route.id'] === 'routes/action-json-response.$id', ); expect(routeLoaderSpan).toBeDefined(); @@ -191,7 +193,7 @@ test('Records loader spans on a deferred loader response', async ({ page }) => { expect(transaction.contexts?.trace?.data?.['sentry.source']).toBe('route'); expect( transaction.spans?.some( - s => s.data?.['code.function'] === 'loader' && s.data?.['match.route.id'] === 'routes/loader-defer-response.$id', + s => s.data?.['code.function.name'] === 'loader' && s.data?.['match.route.id'] === 'routes/loader-defer-response.$id', ), ).toBe(true); }); @@ -263,7 +265,7 @@ test('Sends two linked transactions (server & client) to Sentry', async ({ page const httpServerTraceId = httpServerTransaction.contexts?.trace?.trace_id; const httpServerSpanId = httpServerTransaction.contexts?.trace?.span_id; - const loaderSpan = httpServerTransaction?.spans?.find(span => span.data && span.data['code.function'] === 'loader'); + const loaderSpan = httpServerTransaction?.spans?.find(span => span.data && span.data['code.function.name'] === 'loader'); const loaderSpanId = loaderSpan?.span_id; const loaderParentSpanId = loaderSpan?.parent_span_id; diff --git a/dev-packages/e2e-tests/test-applications/create-remix-app-v2-non-vite/tests/server-transactions.test.ts b/dev-packages/e2e-tests/test-applications/create-remix-app-v2-non-vite/tests/server-transactions.test.ts index 6f6581d6c05e..643d09099e96 100644 --- a/dev-packages/e2e-tests/test-applications/create-remix-app-v2-non-vite/tests/server-transactions.test.ts +++ b/dev-packages/e2e-tests/test-applications/create-remix-app-v2-non-vite/tests/server-transactions.test.ts @@ -39,7 +39,7 @@ test('Sends two linked transactions (server & client) to Sentry', async ({ page const httpServerTraceId = httpServerTransaction.contexts?.trace?.trace_id; const httpServerSpanId = httpServerTransaction.contexts?.trace?.span_id; const loaderSpanId = httpServerTransaction?.spans?.find( - span => span.data && span.data['code.function'] === 'loader', + span => span.data && span.data['code.function.name'] === 'loader', )?.span_id; const pageLoadTraceId = pageloadTransaction.contexts?.trace?.trace_id; diff --git a/dev-packages/e2e-tests/test-applications/create-remix-app-v2/tests/server-transactions.test.ts b/dev-packages/e2e-tests/test-applications/create-remix-app-v2/tests/server-transactions.test.ts index 0aff4ad4c4f2..915f813db060 100644 --- a/dev-packages/e2e-tests/test-applications/create-remix-app-v2/tests/server-transactions.test.ts +++ b/dev-packages/e2e-tests/test-applications/create-remix-app-v2/tests/server-transactions.test.ts @@ -39,7 +39,7 @@ test('Sends two linked transactions (server & client) to Sentry', async ({ page const httpServerTraceId = httpServerTransaction.contexts?.trace?.trace_id; const httpServerSpanId = httpServerTransaction.contexts?.trace?.span_id; const loaderSpanId = httpServerTransaction?.spans?.find( - span => span.data && span.data['code.function'] === 'loader', + span => span.data && span.data['code.function.name'] === 'loader', )?.span_id; const pageLoadTraceId = pageloadTransaction.contexts?.trace?.trace_id; diff --git a/dev-packages/e2e-tests/test-applications/react-router-7-framework-instrumentation/tests/performance/middleware.server.test.ts b/dev-packages/e2e-tests/test-applications/react-router-7-framework-instrumentation/tests/performance/middleware.server.test.ts index 11dc019761dd..3ec90edb4f37 100644 --- a/dev-packages/e2e-tests/test-applications/react-router-7-framework-instrumentation/tests/performance/middleware.server.test.ts +++ b/dev-packages/e2e-tests/test-applications/react-router-7-framework-instrumentation/tests/performance/middleware.server.test.ts @@ -45,7 +45,7 @@ test.describe('server - instrumentation API middleware', () => { trace_id: expect.any(String), data: expect.objectContaining({ 'sentry.origin': 'auto.function.react_router.instrumentation_api', - 'sentry.op': 'function', + 'sentry.op': 'middleware', 'code.function.name': 'middleware', 'react_router.route.id': 'routes/performance/with-middleware', 'http.route': '/performance/with-middleware', @@ -54,7 +54,7 @@ test.describe('server - instrumentation API middleware', () => { parent_span_id: expect.any(String), start_timestamp: expect.any(Number), timestamp: expect.any(Number), - op: 'function', + op: 'middleware', origin: 'auto.function.react_router.instrumentation_api', }); diff --git a/dev-packages/e2e-tests/test-applications/react-router-7-framework/tests/performance/performance.server.test.ts b/dev-packages/e2e-tests/test-applications/react-router-7-framework/tests/performance/performance.server.test.ts index db7e8f752350..b4b23f2012ea 100644 --- a/dev-packages/e2e-tests/test-applications/react-router-7-framework/tests/performance/performance.server.test.ts +++ b/dev-packages/e2e-tests/test-applications/react-router-7-framework/tests/performance/performance.server.test.ts @@ -104,61 +104,4 @@ test.describe('server - performance', () => { }, }); }); - - // This does not work on Node 20.19, sadly - test.skip('should automatically instrument server loader', async ({ page }) => { - const txPromise = waitForTransaction(APP_NAME, async transactionEvent => { - return transactionEvent.transaction === 'GET /performance/server-loader.data'; - }); - - await page.goto(`/performance`); // initial ssr pageloads do not contain .data requests - await page.waitForTimeout(500); // quick breather before navigation - await page.getByRole('link', { name: 'Server Loader' }).click(); // this will actually trigger a .data request - - const transaction = await txPromise; - - expect(transaction?.spans?.[transaction.spans?.length - 1]).toMatchObject({ - span_id: expect.any(String), - trace_id: expect.any(String), - data: { - 'sentry.origin': 'auto.http.react_router', - 'sentry.op': 'function', - }, - description: 'Executing Server Loader', - parent_span_id: expect.any(String), - start_timestamp: expect.any(Number), - timestamp: expect.any(Number), - status: 'ok', - op: 'function', - origin: 'auto.http.react_router', - }); - }); - - // This does not work on Node 20.19, sadly - test.skip('should automatically instrument server action', async ({ page }) => { - const txPromise = waitForTransaction(APP_NAME, async transactionEvent => { - return transactionEvent.transaction === 'POST /performance/server-action.data'; - }); - - await page.goto(`/performance/server-action`); - await page.getByRole('button', { name: 'Submit' }).click(); // this will trigger a .data request - - const transaction = await txPromise; - - expect(transaction?.spans?.[transaction.spans?.length - 1]).toMatchObject({ - span_id: expect.any(String), - trace_id: expect.any(String), - data: { - 'sentry.origin': 'auto.http.react_router', - 'sentry.op': 'function', - }, - description: 'Executing Server Action', - parent_span_id: expect.any(String), - start_timestamp: expect.any(Number), - timestamp: expect.any(Number), - status: 'ok', - op: 'function', - origin: 'auto.http.react_router', - }); - }); }); diff --git a/packages/react-router/src/client/createClientInstrumentation.ts b/packages/react-router/src/client/createClientInstrumentation.ts index e7c44ffb6343..50fa7658f127 100644 --- a/packages/react-router/src/client/createClientInstrumentation.ts +++ b/packages/react-router/src/client/createClientInstrumentation.ts @@ -25,7 +25,7 @@ import { updateNavigationSpanUrlFromLocation, } from './utils'; import { CODE_FUNCTION_NAME, SENTRY_OP, URL_FULL, URL_TEMPLATE } from '@sentry/conventions/attributes'; -import { GENERAL_FUNCTION_SPAN_OP } from '@sentry/conventions/op'; +import { GENERAL_FUNCTION_SPAN_OP, WEB_SERVER_MIDDLEWARE_SPAN_OP } from '@sentry/conventions/op'; const WINDOW = GLOBAL_OBJ as typeof GLOBAL_OBJ & Window; @@ -318,7 +318,7 @@ export function createSentryClientInstrumentation( { name: `middleware ${routeId}`, attributes: { - [SENTRY_OP]: GENERAL_FUNCTION_SPAN_OP, + [SENTRY_OP]: WEB_SERVER_MIDDLEWARE_SPAN_OP, [CODE_FUNCTION_NAME]: 'clientMiddleware', [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.react_router.instrumentation_api', 'react_router.route.id': routeId, diff --git a/packages/react-router/src/server/createServerInstrumentation.ts b/packages/react-router/src/server/createServerInstrumentation.ts index c0c81539171c..a05aba61cc0b 100644 --- a/packages/react-router/src/server/createServerInstrumentation.ts +++ b/packages/react-router/src/server/createServerInstrumentation.ts @@ -6,7 +6,7 @@ import { URL_FULL, URL_PATH, } from '@sentry/conventions/attributes'; -import { WEB_SERVER_FUNCTION_SPAN_OP } from '@sentry/conventions/op'; +import { WEB_SERVER_FUNCTION_SPAN_OP, WEB_SERVER_MIDDLEWARE_SPAN_OP } from '@sentry/conventions/op'; import { debug, flushIfServerless, @@ -210,7 +210,7 @@ export function createSentryServerInstrumentation( { name: `middleware ${middlewareName || routeId}`, attributes: { - [SENTRY_OP]: WEB_SERVER_FUNCTION_SPAN_OP, + [SENTRY_OP]: WEB_SERVER_MIDDLEWARE_SPAN_OP, [CODE_FUNCTION_NAME]: 'middleware', [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.react_router.instrumentation_api', 'react_router.route.id': routeId, diff --git a/packages/react-router/test/client/createClientInstrumentation.test.ts b/packages/react-router/test/client/createClientInstrumentation.test.ts index 8e95f141d810..284f66a72555 100644 --- a/packages/react-router/test/client/createClientInstrumentation.test.ts +++ b/packages/react-router/test/client/createClientInstrumentation.test.ts @@ -608,7 +608,7 @@ describe('createSentryClientInstrumentation', () => { expect.objectContaining({ name: 'middleware test-route', attributes: expect.objectContaining({ - 'sentry.op': 'function', + 'sentry.op': 'middleware', 'code.function.name': 'clientMiddleware', 'sentry.origin': 'auto.function.react_router.instrumentation_api', 'react_router.route.id': 'test-route', diff --git a/packages/react-router/test/server/createServerInstrumentation.test.ts b/packages/react-router/test/server/createServerInstrumentation.test.ts index 0928edec39a9..74f84108079d 100644 --- a/packages/react-router/test/server/createServerInstrumentation.test.ts +++ b/packages/react-router/test/server/createServerInstrumentation.test.ts @@ -366,7 +366,7 @@ describe('createSentryServerInstrumentation', () => { expect.objectContaining({ name: 'middleware test-route', attributes: expect.objectContaining({ - 'sentry.op': 'function', + 'sentry.op': 'middleware', 'code.function.name': 'middleware', 'sentry.origin': 'auto.function.react_router.instrumentation_api', 'react_router.route.id': 'test-route', @@ -398,7 +398,7 @@ describe('createSentryServerInstrumentation', () => { expect.objectContaining({ name: 'middleware authMiddleware', attributes: expect.objectContaining({ - 'sentry.op': 'function', + 'sentry.op': 'middleware', 'code.function.name': 'middleware', 'react_router.route.id': 'routes/protected', 'http.route': '/protected', diff --git a/packages/remix/src/server/integrations/tracing-channel.ts b/packages/remix/src/server/integrations/tracing-channel.ts index 234a7a0c30d3..e94d5151a88b 100644 --- a/packages/remix/src/server/integrations/tracing-channel.ts +++ b/packages/remix/src/server/integrations/tracing-channel.ts @@ -14,7 +14,7 @@ import { } from '@sentry/core'; import { bindTracingChannelToSpan } from '@sentry/server-utils'; import { - CODE_FUNCTION, + CODE_FUNCTION_NAME, HTTP_METHOD, HTTP_ROUTE, HTTP_STATUS_CODE, @@ -147,7 +147,7 @@ function subscribeRequestHandler(): void { [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN, [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'http.server', ...(hasUrlName && { [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url' }), - [CODE_FUNCTION]: 'requestHandler', + [CODE_FUNCTION_NAME]: 'requestHandler', ...requestAttributes, }, }); @@ -182,7 +182,7 @@ function subscribeCallRouteLoader(): void { attributes: { [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN, [SENTRY_OP]: WEB_SERVER_FUNCTION_SPAN_OP, - [CODE_FUNCTION]: 'loader', + [CODE_FUNCTION_NAME]: 'loader', ...getRequestAttributes(params.request), ...getMatchAttributes(params), }, @@ -216,7 +216,7 @@ function subscribeCallRouteAction(formDataCapture: FormDataCapture | undefined): attributes: { [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN, [SENTRY_OP]: WEB_SERVER_FUNCTION_SPAN_OP, - [CODE_FUNCTION]: 'action', + [CODE_FUNCTION_NAME]: 'action', ...getRequestAttributes(params.request), ...getMatchAttributes(params), }, diff --git a/packages/remix/test/server/tracing-channel-no-form-data.test.ts b/packages/remix/test/server/tracing-channel-no-form-data.test.ts index 421e05bb2791..18d469bf0803 100644 --- a/packages/remix/test/server/tracing-channel-no-form-data.test.ts +++ b/packages/remix/test/server/tracing-channel-no-form-data.test.ts @@ -55,7 +55,7 @@ describe('remixIntegration with orchestrion (no form-data capture configured)', name: 'ACTION routes/submit', attributes: expect.objectContaining({ 'sentry.op': 'function', - 'code.function': 'action', + 'code.function.name': 'action', 'http.method': 'POST', }), }), diff --git a/packages/remix/test/server/tracing-channel.test.ts b/packages/remix/test/server/tracing-channel.test.ts index bd2e2d6f9ecc..2f9c966e898e 100644 --- a/packages/remix/test/server/tracing-channel.test.ts +++ b/packages/remix/test/server/tracing-channel.test.ts @@ -49,7 +49,7 @@ describe('remixIntegration (Orchestrion-based)', () => { 'sentry.kind': 'server', 'sentry.op': 'http.server', 'sentry.source': 'url', - 'code.function': 'requestHandler', + 'code.function.name': 'requestHandler', 'http.method': 'GET', 'url.full': 'http://localhost/users', }), @@ -103,7 +103,7 @@ describe('remixIntegration (Orchestrion-based)', () => { attributes: expect.objectContaining({ 'sentry.origin': 'auto.http.remix', 'sentry.op': 'function', - 'code.function': 'loader', + 'code.function.name': 'loader', 'http.method': 'GET', 'url.full': 'http://localhost/users/123', 'match.route.id': 'routes/users.$userId', @@ -142,7 +142,7 @@ describe('remixIntegration (Orchestrion-based)', () => { name: 'ACTION routes/submit', attributes: expect.objectContaining({ 'sentry.op': 'function', - 'code.function': 'action', + 'code.function.name': 'action', 'http.method': 'POST', }), }), From c78de2d0b3ebc8cd211768b33a351459177d10da Mon Sep 17 00:00:00 2001 From: Martin Sonnberger Date: Thu, 6 Aug 2026 16:27:36 +0200 Subject: [PATCH 5/5] format --- .../tests/server-transactions.test.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/create-remix-app-express/tests/server-transactions.test.ts b/dev-packages/e2e-tests/test-applications/create-remix-app-express/tests/server-transactions.test.ts index d05ef15250b1..864b23959fb0 100644 --- a/dev-packages/e2e-tests/test-applications/create-remix-app-express/tests/server-transactions.test.ts +++ b/dev-packages/e2e-tests/test-applications/create-remix-app-express/tests/server-transactions.test.ts @@ -162,7 +162,8 @@ test('Records action and loader spans on a parameterized action route', async ({ const transaction = await transactionPromise; const actionSpan = transaction.spans?.find( - s => s.data?.['code.function.name'] === 'action' && s.data?.['match.route.id'] === 'routes/action-json-response.$id', + s => + s.data?.['code.function.name'] === 'action' && s.data?.['match.route.id'] === 'routes/action-json-response.$id', ); expect(actionSpan).toBeDefined(); expect(actionSpan?.op).toBe('function'); @@ -174,7 +175,8 @@ test('Records action and loader spans on a parameterized action route', async ({ expect(rootLoaderSpan).toBeDefined(); const routeLoaderSpan = transaction.spans?.find( - s => s.data?.['code.function.name'] === 'loader' && s.data?.['match.route.id'] === 'routes/action-json-response.$id', + s => + s.data?.['code.function.name'] === 'loader' && s.data?.['match.route.id'] === 'routes/action-json-response.$id', ); expect(routeLoaderSpan).toBeDefined(); @@ -193,7 +195,9 @@ test('Records loader spans on a deferred loader response', async ({ page }) => { expect(transaction.contexts?.trace?.data?.['sentry.source']).toBe('route'); expect( transaction.spans?.some( - s => s.data?.['code.function.name'] === 'loader' && s.data?.['match.route.id'] === 'routes/loader-defer-response.$id', + s => + s.data?.['code.function.name'] === 'loader' && + s.data?.['match.route.id'] === 'routes/loader-defer-response.$id', ), ).toBe(true); }); @@ -265,7 +269,9 @@ test('Sends two linked transactions (server & client) to Sentry', async ({ page const httpServerTraceId = httpServerTransaction.contexts?.trace?.trace_id; const httpServerSpanId = httpServerTransaction.contexts?.trace?.span_id; - const loaderSpan = httpServerTransaction?.spans?.find(span => span.data && span.data['code.function.name'] === 'loader'); + const loaderSpan = httpServerTransaction?.spans?.find( + span => span.data && span.data['code.function.name'] === 'loader', + ); const loaderSpanId = loaderSpan?.span_id; const loaderParentSpanId = loaderSpan?.parent_span_id;