diff --git a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/route-handlers.test.ts b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/route-handlers.test.ts index 421914877ce2..41f8d897d97b 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/route-handlers.test.ts +++ b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/route-handlers.test.ts @@ -52,7 +52,8 @@ test('Should record exceptions and transactions for faulty route handlers', asyn expect(routehandlerTransaction.contexts?.trace?.op).toBe('http.server'); expect(routehandlerError.exception?.values?.[0].value).toBe('route-handler-error'); - expect(routehandlerError.transaction).toBe('PUT /route-handlers/[param]/error'); + // TODO: Uncomment once we update the scope transaction name on the server side + // expect(routehandlerError.transaction).toBe('PUT /route-handlers/[param]/error'); }); test.describe('Edge runtime', () => { diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2/test/errors.server.test.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2/test/errors.server.test.ts index 972ee59955dd..5240489b0934 100644 --- a/dev-packages/e2e-tests/test-applications/sveltekit-2/test/errors.server.test.ts +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2/test/errors.server.test.ts @@ -60,6 +60,7 @@ test.describe('server-side errors', () => { }), ); - expect(errorEvent.transaction).toEqual('GET /server-route-error'); + // TODO: Uncomment once we update the scope transaction name on the server side + // expect(errorEvent.transaction).toEqual('GET /server-route-error'); }); }); diff --git a/dev-packages/e2e-tests/test-applications/sveltekit/test/errors.server.test.ts b/dev-packages/e2e-tests/test-applications/sveltekit/test/errors.server.test.ts index f2b7d2d21531..c36d44c80068 100644 --- a/dev-packages/e2e-tests/test-applications/sveltekit/test/errors.server.test.ts +++ b/dev-packages/e2e-tests/test-applications/sveltekit/test/errors.server.test.ts @@ -63,6 +63,7 @@ test.describe('server-side errors', () => { }), ); - expect(errorEvent.transaction).toEqual('GET /server-route-error'); + // TODO: Uncomment once we update the scope transaction name on the server side + // expect(errorEvent.transaction).toEqual('GET /server-route-error'); }); }); diff --git a/packages/core/src/scope.ts b/packages/core/src/scope.ts index ee66092d89a0..90961cc48bd0 100644 --- a/packages/core/src/scope.ts +++ b/packages/core/src/scope.ts @@ -81,6 +81,9 @@ export class Scope implements ScopeInterface { /** * Transaction Name + * + * IMPORTANT: The transaction name on the scope has nothing to do with root spans/transaction objects. + * It's purpose is to assign a transaction to the scope that's added to non-transaction events. */ protected _transactionName?: string; @@ -278,7 +281,7 @@ export class Scope implements ScopeInterface { } /** - * Sets the transaction name on the scope for future events. + * @inheritDoc */ public setTransactionName(name?: string): this { this._transactionName = name; diff --git a/packages/core/src/utils/applyScopeDataToEvent.ts b/packages/core/src/utils/applyScopeDataToEvent.ts index 76f75e3b6c51..b41aa2bb4818 100644 --- a/packages/core/src/utils/applyScopeDataToEvent.ts +++ b/packages/core/src/utils/applyScopeDataToEvent.ts @@ -38,7 +38,6 @@ export function mergeScopeData(data: ScopeData, mergeData: ScopeData): void { eventProcessors, attachments, propagationContext, - // eslint-disable-next-line deprecation/deprecation transactionName, span, } = mergeData; @@ -54,7 +53,6 @@ export function mergeScopeData(data: ScopeData, mergeData: ScopeData): void { } if (transactionName) { - // eslint-disable-next-line deprecation/deprecation data.transactionName = transactionName; } @@ -118,15 +116,7 @@ export function mergeArray( } function applyDataToEvent(event: Event, data: ScopeData): void { - const { - extra, - tags, - user, - contexts, - level, - // eslint-disable-next-line deprecation/deprecation - transactionName, - } = data; + const { extra, tags, user, contexts, level, transactionName } = data; const cleanedExtra = dropUndefinedKeys(extra); if (cleanedExtra && Object.keys(cleanedExtra).length) { @@ -152,7 +142,8 @@ function applyDataToEvent(event: Event, data: ScopeData): void { event.level = level; } - if (transactionName) { + // transaction events get their `transaction` from the root span name + if (transactionName && event.type !== 'transaction') { event.transaction = transactionName; } } @@ -179,7 +170,7 @@ function applySpanToEvent(event: Event, span: Span): void { const rootSpan = getRootSpan(span); const transactionName = spanToJSON(rootSpan).description; - if (transactionName && !event.transaction) { + if (transactionName && !event.transaction && event.type === 'transaction') { event.transaction = transactionName; } } diff --git a/packages/core/test/lib/utils/applyScopeDataToEvent.test.ts b/packages/core/test/lib/utils/applyScopeDataToEvent.test.ts index 7543d9ed39e3..e6370931f4cf 100644 --- a/packages/core/test/lib/utils/applyScopeDataToEvent.test.ts +++ b/packages/core/test/lib/utils/applyScopeDataToEvent.test.ts @@ -1,5 +1,11 @@ -import type { Attachment, Breadcrumb, EventProcessor, ScopeData } from '@sentry/types'; -import { mergeAndOverwriteScopeData, mergeArray, mergeScopeData } from '../../../src/utils/applyScopeDataToEvent'; +import type { Attachment, Breadcrumb, Event, EventProcessor, EventType, ScopeData } from '@sentry/types'; +import { startInactiveSpan } from '../../../src'; +import { + applyScopeDataToEvent, + mergeAndOverwriteScopeData, + mergeArray, + mergeScopeData, +} from '../../../src/utils/applyScopeDataToEvent'; describe('mergeArray', () => { it.each([ @@ -158,3 +164,103 @@ describe('mergeScopeData', () => { }); }); }); + +describe('applyScopeDataToEvent', () => { + it("doesn't apply scope.transactionName to transaction events", () => { + const data: ScopeData = { + eventProcessors: [], + breadcrumbs: [], + user: {}, + tags: {}, + extra: {}, + contexts: {}, + attachments: [], + propagationContext: { spanId: '1', traceId: '1' }, + sdkProcessingMetadata: {}, + fingerprint: [], + transactionName: 'foo', + }; + const event: Event = { type: 'transaction', transaction: '/users/:id' }; + + applyScopeDataToEvent(event, data); + + expect(event.transaction).toBe('/users/:id'); + }); + + it('applies the root span name to transaction events', () => { + const data: ScopeData = { + eventProcessors: [], + breadcrumbs: [], + user: {}, + tags: {}, + extra: {}, + contexts: {}, + attachments: [], + propagationContext: { spanId: '1', traceId: '1' }, + sdkProcessingMetadata: {}, + fingerprint: [], + transactionName: 'foo', + span: { + attributes: {}, + startTime: 1, + endTime: 2, + status: 'ok', + name: 'bar', + // @ts-expect-error - we don't need to provide all span context fields + spanContext: () => ({}), + }, + }; + + const event: Event = { type: 'transaction' }; + + applyScopeDataToEvent(event, data); + + expect(event.transaction).toBe('bar'); + }); + + it("doesn't apply the root span name to non-transaction events", () => { + const data: ScopeData = { + eventProcessors: [], + breadcrumbs: [], + user: {}, + tags: {}, + extra: {}, + contexts: {}, + attachments: [], + propagationContext: { spanId: '1', traceId: '1' }, + sdkProcessingMetadata: {}, + fingerprint: [], + transactionName: '/users/:id', + span: startInactiveSpan({ name: 'foo' }), + }; + const event: Event = { type: undefined }; + + applyScopeDataToEvent(event, data); + + expect(event.transaction).toBe('/users/:id'); + }); + + it.each([undefined, 'profile', 'replay_event', 'feedback'])( + 'applies scope.transactionName to event with type %s', + type => { + const data: ScopeData = { + eventProcessors: [], + breadcrumbs: [], + user: {}, + tags: {}, + extra: {}, + contexts: {}, + attachments: [], + propagationContext: { spanId: '1', traceId: '1' }, + sdkProcessingMetadata: {}, + fingerprint: [], + transactionName: 'foo', + }; + const event: Event = { type: type as EventType, transaction: '/users/:id' }; + + applyScopeDataToEvent(event, data); + + expect(event.transaction).toBe('foo'); + }, + ); +}); diff --git a/packages/remix/test/integration/test/client/capture-exception.test.ts b/packages/remix/test/integration/test/client/capture-exception.test.ts index b7e38abf2f4c..18f2fd9af196 100644 --- a/packages/remix/test/integration/test/client/capture-exception.test.ts +++ b/packages/remix/test/integration/test/client/capture-exception.test.ts @@ -8,7 +8,8 @@ test('should report a manually captured error.', async ({ page }) => { const [errorEnvelope, pageloadEnvelope] = envelopes; expect(errorEnvelope.level).toBe('error'); - expect(errorEnvelope.transaction).toBe('/capture-exception'); + // TODO: Comment back in once we update the scope transaction name on the client side + // expect(errorEnvelope.transaction).toBe('/capture-exception'); expect(errorEnvelope.exception?.values).toMatchObject([ { type: 'Error', diff --git a/packages/remix/test/integration/test/client/capture-message.test.ts b/packages/remix/test/integration/test/client/capture-message.test.ts index 234b6ee3d961..5c3e578ad81f 100644 --- a/packages/remix/test/integration/test/client/capture-message.test.ts +++ b/packages/remix/test/integration/test/client/capture-message.test.ts @@ -8,7 +8,8 @@ test('should report a manually captured message.', async ({ page }) => { const [messageEnvelope, pageloadEnvelope] = envelopes; expect(messageEnvelope.level).toBe('info'); - expect(messageEnvelope.transaction).toBe('/capture-message'); + // TODO: Comment back in once we update the scope transaction name on the client side + // expect(messageEnvelope.transaction).toBe('/capture-message'); expect(messageEnvelope.message).toBe('Sentry Manually Captured Message'); expect(pageloadEnvelope.contexts?.trace?.op).toBe('pageload'); diff --git a/packages/types/src/scope.ts b/packages/types/src/scope.ts index 3a3abbee0ac6..7883ee0eff91 100644 --- a/packages/types/src/scope.ts +++ b/packages/types/src/scope.ts @@ -40,7 +40,6 @@ export interface ScopeData { sdkProcessingMetadata: { [key: string]: unknown }; fingerprint: string[]; level?: SeverityLevel; - /** @deprecated This will be removed in v8. */ transactionName?: string; span?: Span; } @@ -127,7 +126,15 @@ export interface Scope { setLevel(level: SeverityLevel): this; /** - * Sets the transaction name on the scope for future events. + * Sets the transaction name on the scope so that the name of the transaction + * (e.g. taken server route or page location) is attached to future events. + * + * IMPORTANT: Calling this function does NOT change the name of the currently active + * span. If you want to change the name of the active span, use `span.updateName()` + * instead. + * + * By default, the SDK updates the scope's transaction name automatically on sensible + * occasions, such as a page navigation or when handling a new request on the server. */ setTransactionName(name?: string): this;