diff --git a/CHANGELOG.md b/CHANGELOG.md index a3e236681..5f6cdf5ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Features +- Add `sentry.origin` to SDK spans to indicated if spans are created by a part of the SDK or manually ([#4066](https://github.com/getsentry/sentry-react-native/pull/4066)) - Exclude Sentry Web Replay by default, reducing the code in 130KB. ([#4006](https://github.com/getsentry/sentry-react-native/pull/4006)) - You can keep Sentry Web Replay by setting `includeWebReplay` to `true` in your metro config as shown in the snippet: diff --git a/packages/core/src/js/touchevents.tsx b/packages/core/src/js/touchevents.tsx index 06ab4b552..35a150c7b 100644 --- a/packages/core/src/js/touchevents.tsx +++ b/packages/core/src/js/touchevents.tsx @@ -1,4 +1,4 @@ -import { addBreadcrumb, getClient } from '@sentry/core'; +import { addBreadcrumb, getClient, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/core'; import type { SeverityLevel } from '@sentry/types'; import { dropUndefinedKeys, logger } from '@sentry/utils'; import * as React from 'react'; @@ -8,6 +8,7 @@ import { StyleSheet, View } from 'react-native'; import { createIntegration } from './integrations/factory'; import { startUserInteractionSpan } from './tracing/integrations/userInteraction'; import { UI_ACTION_TOUCH } from './tracing/ops'; +import { SPAN_ORIGIN_AUTO_INTERACTION } from './tracing/origin'; export type TouchEventBoundaryProps = { /** @@ -195,10 +196,13 @@ class TouchEventBoundary extends React.Component { this._logTouchEvent(touchPath, label); } - startUserInteractionSpan({ + const span = startUserInteractionSpan({ elementId: label, op: UI_ACTION_TOUCH, }); + if (span) { + span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SPAN_ORIGIN_AUTO_INTERACTION); + } } /** diff --git a/packages/core/src/js/tracing/gesturetracing.ts b/packages/core/src/js/tracing/gesturetracing.ts index ffa2e38df..be7ee5d3d 100644 --- a/packages/core/src/js/tracing/gesturetracing.ts +++ b/packages/core/src/js/tracing/gesturetracing.ts @@ -1,9 +1,10 @@ -import { addBreadcrumb } from '@sentry/core'; +import { addBreadcrumb, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/core'; import type { Breadcrumb } from '@sentry/types'; import { logger } from '@sentry/utils'; import { startUserInteractionSpan } from './integrations/userInteraction'; import { UI_ACTION } from './ops'; +import { SPAN_ORIGIN_AUTO_INTERACTION } from './origin'; export const DEFAULT_BREADCRUMB_CATEGORY = 'gesture'; export const DEFAULT_BREADCRUMB_TYPE = 'user'; @@ -69,7 +70,10 @@ export function sentryTraceGesture( const originalOnBegin = gestureCandidate.handlers.onBegin; (gesture as unknown as Required).handlers.onBegin = (event: GestureEvent) => { - startUserInteractionSpan({ elementId: label, op: `${UI_ACTION}.${name}` }); + const span = startUserInteractionSpan({ elementId: label, op: `${UI_ACTION}.${name}` }); + if (span) { + span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SPAN_ORIGIN_AUTO_INTERACTION); + } addGestureBreadcrumb(`Gesture ${label} begin.`, { event, name }); diff --git a/packages/core/src/js/tracing/integrations/appStart.ts b/packages/core/src/js/tracing/integrations/appStart.ts index 3b1365b04..7d203fa83 100644 --- a/packages/core/src/js/tracing/integrations/appStart.ts +++ b/packages/core/src/js/tracing/integrations/appStart.ts @@ -3,6 +3,7 @@ import { getCapturedScopesOnSpan, getClient, getCurrentScope, + SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SentryNonRecordingSpan, startInactiveSpan, } from '@sentry/core'; @@ -22,6 +23,7 @@ import { APP_START_WARM as APP_START_WARM_OP, UI_LOAD as UI_LOAD_OP, } from '../ops'; +import { SPAN_ORIGIN_AUTO_APP_START, SPAN_ORIGIN_MANUAL_APP_START } from '../origin'; import { SEMANTIC_ATTRIBUTE_SENTRY_OP } from '../semanticAttributes'; import { createChildSpanJSON, createSpanJSON, getBundleStartTimestampMs } from '../utils'; @@ -45,19 +47,32 @@ const MAX_APP_START_AGE_MS = 60_000; const APP_START_TX_NAME = 'App Start'; let recordedAppStartEndTimestampMs: number | undefined = undefined; +let isRecordedAppStartEndTimestampMsManual = false; + let rootComponentCreationTimestampMs: number | undefined = undefined; +let isRootComponentCreationTimestampMsManual = false; /** * Records the application start end. * Used automatically by `Sentry.wrap` and `Sentry.ReactNativeProfiler`. */ -export async function captureAppStart(): Promise { +export function captureAppStart(): Promise { + return _captureAppStart({ isManual: true }); +} + +/** + * For internal use only. + * + * @private + */ +export async function _captureAppStart({ isManual }: { isManual: boolean }): Promise { const client = getClient(); if (!client) { logger.warn('[AppStart] Could not capture App Start, missing client.'); return; } + isRecordedAppStartEndTimestampMsManual = isManual; _setAppStartEndTimestampMs(timestampInSeconds() * 1000); await client.getIntegrationByName(INTEGRATION_NAME)?.captureStandaloneAppStart(); } @@ -71,6 +86,17 @@ export function setRootComponentCreationTimestampMs(timestampMs: number): void { logger.warn('Setting Root component creation timestamp after app start end is set.'); rootComponentCreationTimestampMs && logger.warn('Overwriting already set root component creation timestamp.'); rootComponentCreationTimestampMs = timestampMs; + isRootComponentCreationTimestampMsManual = true; +} + +/** + * For internal use only. + * + * @private + */ +export function _setRootComponentCreationTimestampMs(timestampMs: number): void { + setRootComponentCreationTimestampMs(timestampMs); + isRootComponentCreationTimestampMsManual = false; } /** @@ -234,6 +260,10 @@ export const appStartIntegration = ({ event.contexts.trace.data[SEMANTIC_ATTRIBUTE_SENTRY_OP] = UI_LOAD_OP; event.contexts.trace.op = UI_LOAD_OP; + const origin = isRecordedAppStartEndTimestampMsManual ? SPAN_ORIGIN_MANUAL_APP_START : SPAN_ORIGIN_AUTO_APP_START; + event.contexts.trace.data[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN] = origin; + event.contexts.trace.origin = origin; + const appStartTimestampSeconds = appStartTimestampMs / 1000; event.start_timestamp = appStartTimestampSeconds; @@ -269,7 +299,7 @@ export const appStartIntegration = ({ timestamp: appStartEndTimestampSeconds, trace_id: event.contexts.trace.trace_id, parent_span_id: event.contexts.trace.span_id, - origin: 'auto', + origin, }); const jsExecutionSpanJSON = createJSExecutionStartSpan(appStartSpanJSON, rootComponentCreationTimestampMs); @@ -335,6 +365,7 @@ function createJSExecutionStartSpan( description: 'JS Bundle Execution Start', start_timestamp: bundleStartTimestampMs / 1000, timestamp: bundleStartTimestampMs / 1000, + origin: SPAN_ORIGIN_AUTO_APP_START, }); } @@ -342,6 +373,7 @@ function createJSExecutionStartSpan( description: 'JS Bundle Execution Before React Root', start_timestamp: bundleStartTimestampMs / 1000, timestamp: rootComponentCreationTimestampMs / 1000, + origin: isRootComponentCreationTimestampMsManual ? SPAN_ORIGIN_MANUAL_APP_START : SPAN_ORIGIN_AUTO_APP_START, }); } @@ -358,6 +390,7 @@ function convertNativeSpansToSpanJSON(parentSpan: SpanJSON, nativeSpans: NativeA description: span.description, start_timestamp: span.start_timestamp_ms / 1000, timestamp: span.end_timestamp_ms / 1000, + origin: SPAN_ORIGIN_AUTO_APP_START, }); }); } @@ -377,12 +410,14 @@ function createUIKitSpan(parentSpan: SpanJSON, nativeUIKitSpan: NativeAppStartRe description: 'UIKit Init to JS Exec Start', start_timestamp: nativeUIKitSpan.start_timestamp_ms / 1000, timestamp: bundleStart / 1000, + origin: SPAN_ORIGIN_AUTO_APP_START, }); } else { return createChildSpanJSON(parentSpan, { description: 'UIKit Init', start_timestamp: nativeUIKitSpan.start_timestamp_ms / 1000, timestamp: nativeUIKitSpan.end_timestamp_ms / 1000, + origin: SPAN_ORIGIN_AUTO_APP_START, }); } } diff --git a/packages/core/src/js/tracing/integrations/userInteraction.ts b/packages/core/src/js/tracing/integrations/userInteraction.ts index b187f1b30..73d6fb769 100644 --- a/packages/core/src/js/tracing/integrations/userInteraction.ts +++ b/packages/core/src/js/tracing/integrations/userInteraction.ts @@ -4,7 +4,7 @@ import { logger } from '@sentry/utils'; import type { ReactNativeClientOptions } from '../../options'; import { onlySampleIfChildSpans } from '../onSpanEndUtils'; -import { SPAN_ORIGIN_AUTO_INTERACTION } from '../origin'; +import { SPAN_ORIGIN_MANUAL_INTERACTION } from '../origin'; import { getCurrentReactNativeTracingIntegration } from '../reactnativetracing'; import { clearActiveSpanFromScope, isSentryInteractionSpan, startIdleSpan } from '../span'; @@ -86,7 +86,7 @@ export const startUserInteractionSpan = (userInteractionId: { idleTimeout: tracing.options.idleTimeoutMs, finalTimeout: tracing.options.finalTimeoutMs, }); - newSpan.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SPAN_ORIGIN_AUTO_INTERACTION); + newSpan.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SPAN_ORIGIN_MANUAL_INTERACTION); onlySampleIfChildSpans(client, newSpan); logger.log(`[${INTEGRATION_NAME}] User Interaction Tracing Created ${op} transaction ${name}.`); return newSpan; diff --git a/packages/core/src/js/tracing/origin.ts b/packages/core/src/js/tracing/origin.ts index 830e7f158..342ca237b 100644 --- a/packages/core/src/js/tracing/origin.ts +++ b/packages/core/src/js/tracing/origin.ts @@ -1 +1,12 @@ export const SPAN_ORIGIN_AUTO_INTERACTION = 'auto.interaction'; +export const SPAN_ORIGIN_MANUAL_INTERACTION = 'manual.interaction'; + +export const SPAN_ORIGIN_MANUAL_APP_START = 'manual.app.start'; +export const SPAN_ORIGIN_AUTO_APP_START = 'auto.app.start'; + +export const SPAN_ORIGIN_AUTO_NAVIGATION_REACT_NATIVE_NAVIGATION = 'auto.navigation.react_native_navigation'; +export const SPAN_ORIGIN_AUTO_NAVIGATION_REACT_NAVIGATION = 'auto.navigation.react_navigation'; +export const SPAN_ORIGIN_AUTO_NAVIGATION_CUSTOM = 'auto.navigation.custom'; + +export const SPAN_ORIGIN_AUTO_UI_TIME_TO_DISPLAY = 'auto.ui.time_to_display'; +export const SPAN_ORIGIN_MANUAL_UI_TIME_TO_DISPLAY = 'manual.ui.time_to_display'; diff --git a/packages/core/src/js/tracing/reactnativenavigation.ts b/packages/core/src/js/tracing/reactnativenavigation.ts index 3f0378d96..45f517353 100644 --- a/packages/core/src/js/tracing/reactnativenavigation.ts +++ b/packages/core/src/js/tracing/reactnativenavigation.ts @@ -2,6 +2,7 @@ import { addBreadcrumb, getClient, SEMANTIC_ATTRIBUTE_SENTRY_OP, + SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, spanToJSON, } from '@sentry/core'; @@ -10,6 +11,7 @@ import type { Client, Integration, Span } from '@sentry/types'; import type { EmitterSubscription } from '../utils/rnlibrariesinterface'; import { isSentrySpan } from '../utils/span'; import { ignoreEmptyBackNavigation } from './onSpanEndUtils'; +import { SPAN_ORIGIN_AUTO_NAVIGATION_REACT_NATIVE_NAVIGATION } from './origin'; import type { ReactNativeTracingIntegration } from './reactnativetracing'; import { getReactNativeTracingIntegration } from './reactnativetracing'; import { @@ -132,6 +134,10 @@ export const reactNativeNavigationIntegration = ({ : getDefaultIdleNavigationSpanOptions(), idleSpanOptions, ); + latestNavigationSpan?.setAttribute( + SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, + SPAN_ORIGIN_AUTO_NAVIGATION_REACT_NATIVE_NAVIGATION, + ); if (ignoreEmptyBackNavigationTransactions) { ignoreEmptyBackNavigation(getClient(), latestNavigationSpan); } diff --git a/packages/core/src/js/tracing/reactnativeprofiler.tsx b/packages/core/src/js/tracing/reactnativeprofiler.tsx index 7fbb05345..048a48954 100644 --- a/packages/core/src/js/tracing/reactnativeprofiler.tsx +++ b/packages/core/src/js/tracing/reactnativeprofiler.tsx @@ -2,7 +2,7 @@ import { getClient, Profiler } from '@sentry/react'; import { timestampInSeconds } from '@sentry/utils'; import { createIntegration } from '../integrations/factory'; -import { captureAppStart, setRootComponentCreationTimestampMs } from '../tracing/integrations/appStart'; +import { _captureAppStart, _setRootComponentCreationTimestampMs } from '../tracing/integrations/appStart'; const ReactNativeProfilerGlobalState = { appStartReported: false, @@ -15,7 +15,7 @@ export class ReactNativeProfiler extends Profiler { public readonly name: string = 'ReactNativeProfiler'; public constructor(props: ConstructorParameters[0]) { - setRootComponentCreationTimestampMs(timestampInSeconds() * 1000); + _setRootComponentCreationTimestampMs(timestampInSeconds() * 1000); super(props); } @@ -45,6 +45,6 @@ export class ReactNativeProfiler extends Profiler { client.addIntegration && client.addIntegration(createIntegration(this.name)); // eslint-disable-next-line @typescript-eslint/no-floating-promises - captureAppStart(); + _captureAppStart({ isManual: false }); } } diff --git a/packages/core/src/js/tracing/reactnavigation.ts b/packages/core/src/js/tracing/reactnavigation.ts index f6bc7c1c5..871485bd7 100644 --- a/packages/core/src/js/tracing/reactnavigation.ts +++ b/packages/core/src/js/tracing/reactnavigation.ts @@ -4,6 +4,7 @@ import { getActiveSpan, getClient, SEMANTIC_ATTRIBUTE_SENTRY_OP, + SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SPAN_STATUS_OK, spanToJSON, startInactiveSpan, @@ -17,6 +18,7 @@ import { isSentrySpan } from '../utils/span'; import { RN_GLOBAL_OBJ } from '../utils/worldwide'; import { NATIVE } from '../wrapper'; import { ignoreEmptyBackNavigation } from './onSpanEndUtils'; +import { SPAN_ORIGIN_AUTO_NAVIGATION_REACT_NAVIGATION } from './origin'; import type { ReactNativeTracingIntegration } from './reactnativetracing'; import { getReactNativeTracingIntegration } from './reactnativetracing'; import { SEMANTIC_ATTRIBUTE_SENTRY_SOURCE } from './semanticAttributes'; @@ -191,6 +193,7 @@ export const reactNavigationIntegration = ({ : getDefaultIdleNavigationSpanOptions(), idleSpanOptions, ); + latestNavigationSpan?.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SPAN_ORIGIN_AUTO_NAVIGATION_REACT_NAVIGATION); if (ignoreEmptyBackNavigationTransactions) { ignoreEmptyBackNavigation(getClient(), latestNavigationSpan); } @@ -201,6 +204,10 @@ export const reactNavigationIntegration = ({ name: 'Navigation processing', startTime: latestNavigationSpan && spanToJSON(latestNavigationSpan).start_timestamp, }); + navigationProcessingSpan.setAttribute( + SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, + SPAN_ORIGIN_AUTO_NAVIGATION_REACT_NAVIGATION, + ); } stateChangeTimeout = setTimeout(_discardLatestTransaction, routeChangeTimeoutMs); diff --git a/packages/core/src/js/tracing/span.ts b/packages/core/src/js/tracing/span.ts index 1a9abeb0a..63bfb44c4 100644 --- a/packages/core/src/js/tracing/span.ts +++ b/packages/core/src/js/tracing/span.ts @@ -3,6 +3,7 @@ import { getClient, getCurrentScope, SEMANTIC_ATTRIBUTE_SENTRY_OP, + SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SentryNonRecordingSpan, SPAN_STATUS_ERROR, spanToJSON, @@ -13,7 +14,11 @@ import { generatePropagationContext, logger } from '@sentry/utils'; import { isRootSpan } from '../utils/span'; import { adjustTransactionDuration, cancelInBackground } from './onSpanEndUtils'; -import { SPAN_ORIGIN_AUTO_INTERACTION } from './origin'; +import { + SPAN_ORIGIN_AUTO_INTERACTION, + SPAN_ORIGIN_AUTO_NAVIGATION_CUSTOM, + SPAN_ORIGIN_MANUAL_INTERACTION, +} from './origin'; export const DEFAULT_NAVIGATION_SPAN_NAME = 'Route Change'; @@ -75,6 +80,8 @@ export const startIdleNavigationSpan = ( ); adjustTransactionDuration(client, idleSpan, finalTimeout); + + idleSpan.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SPAN_ORIGIN_AUTO_NAVIGATION_CUSTOM); return idleSpan; }; @@ -118,7 +125,7 @@ export function getDefaultIdleNavigationSpanOptions(): StartSpanOptions { * Checks if the span is a Sentry User Interaction span. */ export function isSentryInteractionSpan(span: Span): boolean { - return spanToJSON(span).origin === SPAN_ORIGIN_AUTO_INTERACTION; + return [SPAN_ORIGIN_AUTO_INTERACTION, SPAN_ORIGIN_MANUAL_INTERACTION].includes(spanToJSON(span).origin); } const SCOPE_SPAN_FIELD = '_sentrySpan'; diff --git a/packages/core/src/js/tracing/timetodisplay.tsx b/packages/core/src/js/tracing/timetodisplay.tsx index f258000b5..a79d37937 100644 --- a/packages/core/src/js/tracing/timetodisplay.tsx +++ b/packages/core/src/js/tracing/timetodisplay.tsx @@ -1,8 +1,9 @@ -import { getActiveSpan, getSpanDescendants, SPAN_STATUS_ERROR, SPAN_STATUS_OK, spanToJSON, startInactiveSpan } from '@sentry/core'; +import { getActiveSpan, getSpanDescendants, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SPAN_STATUS_ERROR, SPAN_STATUS_OK, spanToJSON, startInactiveSpan } from '@sentry/core'; import type { Span,StartSpanOptions } from '@sentry/types'; import { fill, logger } from '@sentry/utils'; import * as React from 'react'; +import { SPAN_ORIGIN_AUTO_UI_TIME_TO_DISPLAY, SPAN_ORIGIN_MANUAL_UI_TIME_TO_DISPLAY } from './origin'; import { getRNSentryOnDrawReporter, nativeComponentExists } from './timetodisplaynative'; import type {RNSentryOnDrawNextFrameEvent } from './timetodisplaynative.types'; import { setSpanDurationAsMeasurement } from './utils'; @@ -116,9 +117,13 @@ export function startTimeToInitialDisplaySpan( return undefined; } - if (!options?.isAutoInstrumented) { + if (options?.isAutoInstrumented) { + initialDisplaySpan.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SPAN_ORIGIN_AUTO_UI_TIME_TO_DISPLAY); + } else { manualInitialDisplaySpans.set(activeSpan, true); + initialDisplaySpan.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SPAN_ORIGIN_MANUAL_UI_TIME_TO_DISPLAY); } + return initialDisplaySpan; } @@ -128,7 +133,11 @@ export function startTimeToInitialDisplaySpan( * Returns current span if already exists in the currently active span. */ export function startTimeToFullDisplaySpan( - options: Omit & { name?: string, timeoutMs?: number } = { + options: Omit & { + name?: string, + timeoutMs?: number, + isAutoInstrumented?: boolean + } = { timeoutMs: 30_000, }, ): Span | undefined { @@ -177,6 +186,12 @@ export function startTimeToFullDisplaySpan( originalEnd.call(fullDisplaySpan, endTimestamp); }); + if (options?.isAutoInstrumented) { + fullDisplaySpan.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SPAN_ORIGIN_AUTO_UI_TIME_TO_DISPLAY); + } else { + fullDisplaySpan.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SPAN_ORIGIN_MANUAL_UI_TIME_TO_DISPLAY); + } + return fullDisplaySpan; } @@ -242,7 +257,9 @@ function updateFullDisplaySpan(frameTimestampSeconds: number, passedInitialDispl return; } - const span = startTimeToFullDisplaySpan(); + const span = startTimeToFullDisplaySpan({ + isAutoInstrumented: true, + }); if (!span) { logger.warn(`[TimeToDisplay] No TimeToFullDisplay span found or created, possibly performance is disabled.`); return; diff --git a/packages/core/test/tracing/gesturetracing.test.ts b/packages/core/test/tracing/gesturetracing.test.ts index 515146615..cc19777a9 100644 --- a/packages/core/test/tracing/gesturetracing.test.ts +++ b/packages/core/test/tracing/gesturetracing.test.ts @@ -8,8 +8,10 @@ import { sentryTraceGesture, } from '../../src/js/tracing/gesturetracing'; import { startUserInteractionSpan } from '../../src/js/tracing/integrations/userInteraction'; +import { SPAN_ORIGIN_AUTO_INTERACTION } from '../../src/js/tracing/origin'; import type { ReactNativeTracingIntegration } from '../../src/js/tracing/reactnativetracing'; import { reactNativeTracingIntegration } from '../../src/js/tracing/reactnativetracing'; +import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '../../src/js/tracing/semanticAttributes'; import { type TestClient, setupTestClient } from '../mocks/client'; jest.mock('../../src/js/wrapper', () => { @@ -85,6 +87,9 @@ describe('GestureTracing', () => { expect.objectContaining({ timestamp: expect.any(Number), op: `${UI_ACTION}.mock`, + data: expect.objectContaining({ + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: SPAN_ORIGIN_AUTO_INTERACTION, + }), }), ); }); @@ -101,6 +106,9 @@ describe('GestureTracing', () => { expect.objectContaining({ timestamp: expect.any(Number), op: `${UI_ACTION}.gesture`, + data: expect.objectContaining({ + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: SPAN_ORIGIN_AUTO_INTERACTION, + }), }), ); }); diff --git a/packages/core/test/tracing/integrations/appStart.test.ts b/packages/core/test/tracing/integrations/appStart.test.ts index 09e8c5329..36055a62c 100644 --- a/packages/core/test/tracing/integrations/appStart.test.ts +++ b/packages/core/test/tracing/integrations/appStart.test.ts @@ -22,9 +22,11 @@ import { import { _clearRootComponentCreationTimestampMs, _setAppStartEndTimestampMs, + _setRootComponentCreationTimestampMs, appStartIntegration, setRootComponentCreationTimestampMs, } from '../../../src/js/tracing/integrations/appStart'; +import { SPAN_ORIGIN_AUTO_APP_START, SPAN_ORIGIN_MANUAL_APP_START } from '../../../src/js/tracing/origin'; import { getTimeOriginMilliseconds } from '../../../src/js/tracing/utils'; import { RN_GLOBAL_OBJ } from '../../../src/js/utils/worldwide'; import { NATIVE } from '../../../src/js/wrapper'; @@ -153,27 +155,35 @@ describe('App Start Integration', () => { ); expect(appStartRootSpan).toEqual( - expect.objectContaining({ - description: 'Cold App Start', + expect.objectContaining(>{ span_id: expect.any(String), + description: 'Cold App Start', op: APP_START_COLD_OP, + data: { + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: APP_START_COLD_OP, + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: SPAN_ORIGIN_AUTO_APP_START, + }, }), ); expect(bundleStartSpan).toEqual( - expect.objectContaining({ + expect.objectContaining(>{ description: 'JS Bundle Execution Start', start_timestamp: expect.closeTo((timeOriginMilliseconds - 50) / 1000), timestamp: expect.closeTo((timeOriginMilliseconds - 50) / 1000), parent_span_id: appStartRootSpan!.span_id, // parent is the root app start span op: appStartRootSpan!.op, // op is the same as the root app start span + data: { + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: appStartRootSpan!.op, + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: SPAN_ORIGIN_AUTO_APP_START, + }, }), ); }); - it('adds bundle execution before react root', async () => { + it('adds bundle execution before react root via private api (used by Sentry.wrap())', async () => { mockReactNativeBundleExecutionStartTimestamp(); const [timeOriginMilliseconds] = mockAppStart({ cold: true }); - setRootComponentCreationTimestampMs(timeOriginMilliseconds - 10); + _setRootComponentCreationTimestampMs(timeOriginMilliseconds - 10); const actualEvent = await captureStandAloneAppStart(); @@ -183,19 +193,27 @@ describe('App Start Integration', () => { ); expect(appStartRootSpan).toEqual( - expect.objectContaining({ - description: 'Cold App Start', + expect.objectContaining(>{ span_id: expect.any(String), + description: 'Cold App Start', op: APP_START_COLD_OP, + data: { + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: APP_START_COLD_OP, + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: SPAN_ORIGIN_AUTO_APP_START, + }, }), ); expect(bundleStartSpan).toEqual( - expect.objectContaining({ + expect.objectContaining(>{ description: 'JS Bundle Execution Before React Root', start_timestamp: expect.closeTo((timeOriginMilliseconds - 50) / 1000), timestamp: (timeOriginMilliseconds - 10) / 1000, parent_span_id: appStartRootSpan!.span_id, // parent is the root app start span op: appStartRootSpan!.op, // op is the same as the root app start span + data: { + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: appStartRootSpan!.op, + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: SPAN_ORIGIN_AUTO_APP_START, + }, }), ); }); @@ -212,19 +230,27 @@ describe('App Start Integration', () => { const nativeSpan = actualEvent!.spans!.find(({ description }) => description === 'test native app start span'); expect(appStartRootSpan).toEqual( - expect.objectContaining({ - description: 'Cold App Start', + expect.objectContaining(>{ span_id: expect.any(String), + description: 'Cold App Start', op: APP_START_COLD_OP, + data: { + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: APP_START_COLD_OP, + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: SPAN_ORIGIN_AUTO_APP_START, + }, }), ); expect(nativeSpan).toEqual( - expect.objectContaining({ + expect.objectContaining(>{ description: 'test native app start span', start_timestamp: (timeOriginMilliseconds - 100) / 1000, timestamp: (timeOriginMilliseconds - 50) / 1000, parent_span_id: appStartRootSpan!.span_id, // parent is the root app start span op: appStartRootSpan!.op, // op is the same as the root app start span + data: { + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: appStartRootSpan!.op, + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: SPAN_ORIGIN_AUTO_APP_START, + }, }), ); }); @@ -250,10 +276,14 @@ describe('App Start Integration', () => { expect(nativeSpan).toBeDefined(); expect(nativeSpan).toEqual( - expect.objectContaining({ + expect.objectContaining(>{ description: 'UIKit Init', start_timestamp: (timeOriginMilliseconds - 100) / 1000, timestamp: (timeOriginMilliseconds - 60) / 1000, + origin: SPAN_ORIGIN_AUTO_APP_START, + data: expect.objectContaining({ + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: SPAN_ORIGIN_AUTO_APP_START, + }), }), ); }); @@ -281,10 +311,14 @@ describe('App Start Integration', () => { expect(nativeRuntimeInitSpan).toBeDefined(); expect(nativeRuntimeInitSpan).toEqual( - expect.objectContaining({ + expect.objectContaining(>{ description: 'UIKit Init to JS Exec Start', start_timestamp: (timeOriginMilliseconds - 100) / 1000, timestamp: expect.closeTo((timeOriginMilliseconds - 50) / 1000), + origin: SPAN_ORIGIN_AUTO_APP_START, + data: expect.objectContaining({ + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: SPAN_ORIGIN_AUTO_APP_START, + }), }), ); }); @@ -435,24 +469,34 @@ describe('App Start Integration', () => { ); expect(appStartRootSpan).toEqual( - expect.objectContaining({ + expect.objectContaining(>{ description: 'Cold App Start', span_id: expect.any(String), op: APP_START_COLD_OP, + origin: SPAN_ORIGIN_AUTO_APP_START, + data: { + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: APP_START_COLD_OP, + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: SPAN_ORIGIN_AUTO_APP_START, + }, }), ); expect(bundleStartSpan).toEqual( - expect.objectContaining({ + expect.objectContaining(>{ description: 'JS Bundle Execution Start', start_timestamp: expect.closeTo((timeOriginMilliseconds - 50) / 1000), timestamp: expect.closeTo((timeOriginMilliseconds - 50) / 1000), parent_span_id: appStartRootSpan!.span_id, // parent is the root app start span op: appStartRootSpan!.op, // op is the same as the root app start span + origin: SPAN_ORIGIN_AUTO_APP_START, + data: { + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: appStartRootSpan!.op, + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: SPAN_ORIGIN_AUTO_APP_START, + }, }), ); }); - it('adds bundle execution before react root', async () => { + it('adds bundle execution before react root via public api', async () => { mockReactNativeBundleExecutionStartTimestamp(); const [timeOriginMilliseconds] = mockAppStart({ cold: true }); setRootComponentCreationTimestampMs(timeOriginMilliseconds - 10); @@ -465,19 +509,69 @@ describe('App Start Integration', () => { ); expect(appStartRootSpan).toEqual( - expect.objectContaining({ + expect.objectContaining(>{ description: 'Cold App Start', span_id: expect.any(String), op: APP_START_COLD_OP, + origin: SPAN_ORIGIN_AUTO_APP_START, + data: { + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: APP_START_COLD_OP, + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: SPAN_ORIGIN_AUTO_APP_START, + }, }), ); expect(bundleStartSpan).toEqual( - expect.objectContaining({ + expect.objectContaining(>{ description: 'JS Bundle Execution Before React Root', start_timestamp: expect.closeTo((timeOriginMilliseconds - 50) / 1000), timestamp: (timeOriginMilliseconds - 10) / 1000, parent_span_id: appStartRootSpan!.span_id, // parent is the root app start span op: appStartRootSpan!.op, // op is the same as the root app start span + origin: SPAN_ORIGIN_MANUAL_APP_START, + data: { + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: appStartRootSpan!.op, + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: SPAN_ORIGIN_MANUAL_APP_START, + }, + }), + ); + }); + + it('adds bundle execution before react root via private api (used by Sentry.wrap())', async () => { + mockReactNativeBundleExecutionStartTimestamp(); + const [timeOriginMilliseconds] = mockAppStart({ cold: true }); + _setRootComponentCreationTimestampMs(timeOriginMilliseconds - 10); + + const actualEvent = await processEvent(getMinimalTransactionEvent()); + + const appStartRootSpan = actualEvent!.spans!.find(({ description }) => description === 'Cold App Start'); + const bundleStartSpan = actualEvent!.spans!.find( + ({ description }) => description === 'JS Bundle Execution Before React Root', + ); + + expect(appStartRootSpan).toEqual( + expect.objectContaining(>{ + description: 'Cold App Start', + span_id: expect.any(String), + op: APP_START_COLD_OP, + origin: SPAN_ORIGIN_AUTO_APP_START, + data: { + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: APP_START_COLD_OP, + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: SPAN_ORIGIN_AUTO_APP_START, + }, + }), + ); + expect(bundleStartSpan).toEqual( + expect.objectContaining(>{ + description: 'JS Bundle Execution Before React Root', + start_timestamp: expect.closeTo((timeOriginMilliseconds - 50) / 1000), + timestamp: (timeOriginMilliseconds - 10) / 1000, + parent_span_id: appStartRootSpan!.span_id, // parent is the root app start span + op: appStartRootSpan!.op, // op is the same as the root app start span + origin: SPAN_ORIGIN_AUTO_APP_START, + data: { + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: appStartRootSpan!.op, + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: SPAN_ORIGIN_AUTO_APP_START, + }, }), ); }); @@ -494,19 +588,29 @@ describe('App Start Integration', () => { const nativeSpan = actualEvent!.spans!.find(({ description }) => description === 'test native app start span'); expect(appStartRootSpan).toEqual( - expect.objectContaining({ + expect.objectContaining(>{ description: 'Cold App Start', span_id: expect.any(String), op: APP_START_COLD_OP, + origin: SPAN_ORIGIN_AUTO_APP_START, + data: { + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: APP_START_COLD_OP, + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: SPAN_ORIGIN_AUTO_APP_START, + }, }), ); expect(nativeSpan).toEqual( - expect.objectContaining({ + expect.objectContaining(>{ description: 'test native app start span', start_timestamp: (timeOriginMilliseconds - 100) / 1000, timestamp: (timeOriginMilliseconds - 50) / 1000, parent_span_id: appStartRootSpan!.span_id, // parent is the root app start span op: appStartRootSpan!.op, // op is the same as the root app start span + origin: SPAN_ORIGIN_AUTO_APP_START, + data: { + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: appStartRootSpan!.op, + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: SPAN_ORIGIN_AUTO_APP_START, + }, }), ); }); @@ -532,10 +636,13 @@ describe('App Start Integration', () => { expect(nativeSpan).toBeDefined(); expect(nativeSpan).toEqual( - expect.objectContaining({ + expect.objectContaining(>{ description: 'UIKit Init', start_timestamp: (timeOriginMilliseconds - 100) / 1000, timestamp: (timeOriginMilliseconds - 60) / 1000, + data: expect.objectContaining({ + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: SPAN_ORIGIN_AUTO_APP_START, + }), }), ); }); @@ -563,10 +670,13 @@ describe('App Start Integration', () => { expect(nativeRuntimeInitSpan).toBeDefined(); expect(nativeRuntimeInitSpan).toEqual( - expect.objectContaining({ + expect.objectContaining(>{ description: 'UIKit Init to JS Exec Start', start_timestamp: (timeOriginMilliseconds - 100) / 1000, timestamp: expect.closeTo((timeOriginMilliseconds - 50) / 1000), + data: expect.objectContaining({ + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: SPAN_ORIGIN_AUTO_APP_START, + }), }), ); }); @@ -674,8 +784,10 @@ function expectEventWithAttachedColdAppStart({ contexts: expect.objectContaining({ trace: expect.objectContaining({ op: UI_LOAD, + origin: SPAN_ORIGIN_AUTO_APP_START, data: expect.objectContaining({ [SEMANTIC_ATTRIBUTE_SENTRY_OP]: UI_LOAD, + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: SPAN_ORIGIN_AUTO_APP_START, }), }), }), @@ -694,11 +806,11 @@ function expectEventWithAttachedColdAppStart({ trace_id: expect.any(String), span_id: expect.any(String), parent_span_id: '123', - origin: 'auto', + origin: SPAN_ORIGIN_AUTO_APP_START, status: 'ok', data: { [SEMANTIC_ATTRIBUTE_SENTRY_OP]: APP_START_COLD_OP, - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto', + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: SPAN_ORIGIN_AUTO_APP_START, }, }, { @@ -726,8 +838,10 @@ function expectEventWithAttachedWarmAppStart({ contexts: expect.objectContaining({ trace: expect.objectContaining({ op: UI_LOAD, + origin: SPAN_ORIGIN_AUTO_APP_START, data: expect.objectContaining({ [SEMANTIC_ATTRIBUTE_SENTRY_OP]: UI_LOAD, + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: SPAN_ORIGIN_AUTO_APP_START, }), }), }), @@ -746,11 +860,11 @@ function expectEventWithAttachedWarmAppStart({ trace_id: expect.any(String), span_id: expect.any(String), parent_span_id: '123', - origin: 'auto', + origin: SPAN_ORIGIN_AUTO_APP_START, status: 'ok', data: { [SEMANTIC_ATTRIBUTE_SENTRY_OP]: APP_START_WARM_OP, - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto', + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: SPAN_ORIGIN_AUTO_APP_START, }, }, { @@ -781,8 +895,10 @@ function expectEventWithStandaloneColdAppStart( contexts: expect.objectContaining({ trace: expect.objectContaining({ op: UI_LOAD, + origin: SPAN_ORIGIN_AUTO_APP_START, data: expect.objectContaining({ [SEMANTIC_ATTRIBUTE_SENTRY_OP]: UI_LOAD, + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: SPAN_ORIGIN_AUTO_APP_START, }), }), }), @@ -801,11 +917,11 @@ function expectEventWithStandaloneColdAppStart( trace_id: expect.any(String), span_id: expect.any(String), parent_span_id: actualEvent!.contexts!.trace!.span_id, - origin: 'auto', + origin: SPAN_ORIGIN_AUTO_APP_START, status: 'ok', data: { [SEMANTIC_ATTRIBUTE_SENTRY_OP]: APP_START_COLD_OP, - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto', + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: SPAN_ORIGIN_AUTO_APP_START, }, }, ]), @@ -828,8 +944,10 @@ function expectEventWithStandaloneWarmAppStart( contexts: expect.objectContaining({ trace: expect.objectContaining({ op: UI_LOAD, + origin: SPAN_ORIGIN_AUTO_APP_START, data: expect.objectContaining({ [SEMANTIC_ATTRIBUTE_SENTRY_OP]: UI_LOAD, + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: SPAN_ORIGIN_AUTO_APP_START, }), }), }), @@ -848,11 +966,11 @@ function expectEventWithStandaloneWarmAppStart( trace_id: expect.any(String), span_id: expect.any(String), parent_span_id: actualEvent!.contexts!.trace!.span_id, - origin: 'auto', + origin: SPAN_ORIGIN_AUTO_APP_START, status: 'ok', data: { [SEMANTIC_ATTRIBUTE_SENTRY_OP]: APP_START_WARM_OP, - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto', + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: SPAN_ORIGIN_AUTO_APP_START, }, }, ]), diff --git a/packages/core/test/tracing/integrations/userInteraction.test.ts b/packages/core/test/tracing/integrations/userInteraction.test.ts index 3ec8905a3..72dfaeea0 100644 --- a/packages/core/test/tracing/integrations/userInteraction.test.ts +++ b/packages/core/test/tracing/integrations/userInteraction.test.ts @@ -13,8 +13,10 @@ import { startUserInteractionSpan, userInteractionIntegration, } from '../../../src/js/tracing/integrations/userInteraction'; +import { SPAN_ORIGIN_MANUAL_INTERACTION } from '../../../src/js/tracing/origin'; import type { ReactNativeTracingIntegration } from '../../../src/js/tracing/reactnativetracing'; import { reactNativeTracingIntegration } from '../../../src/js/tracing/reactnativetracing'; +import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '../../../src/js/tracing/semanticAttributes'; import { startIdleNavigationSpan } from '../../../src/js/tracing/span'; import { NATIVE } from '../../../src/js/wrapper'; import type { TestClient } from '../../mocks/client'; @@ -111,6 +113,9 @@ describe('User Interaction Tracing', () => { expect.objectContaining({ description: 'mockedRouteName.mockedElementId', op: 'mocked.op', + data: expect.objectContaining({ + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: SPAN_ORIGIN_MANUAL_INTERACTION, + }), }), ); }); diff --git a/packages/core/test/tracing/reactnativenavigation.test.ts b/packages/core/test/tracing/reactnativenavigation.test.ts index 5e1b9563d..c6939c6b5 100644 --- a/packages/core/test/tracing/reactnativenavigation.test.ts +++ b/packages/core/test/tracing/reactnativenavigation.test.ts @@ -11,6 +11,7 @@ import type { Event, StartSpanOptions } from '@sentry/types'; import type { EmitterSubscription } from 'react-native'; import { reactNativeTracingIntegration } from '../../src/js'; +import { SPAN_ORIGIN_AUTO_NAVIGATION_REACT_NATIVE_NAVIGATION } from '../../src/js/tracing/origin'; import type { BottomTabPressedEvent, ComponentWillAppearEvent, @@ -81,7 +82,7 @@ describe('React Native Navigation Instrumentation', () => { [SEMANTIC_ATTRIBUTE_ROUTE_COMPONENT_ID]: '0', [SEMANTIC_ATTRIBUTE_ROUTE_COMPONENT_TYPE]: 'Component', [SEMANTIC_ATTRIBUTE_ROUTE_HAS_BEEN_SEEN]: false, - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'manual', + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: SPAN_ORIGIN_AUTO_NAVIGATION_REACT_NATIVE_NAVIGATION, [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'component', [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation', [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: 1, @@ -125,7 +126,7 @@ describe('React Native Navigation Instrumentation', () => { [SEMANTIC_ATTRIBUTE_ROUTE_COMPONENT_ID]: '0', [SEMANTIC_ATTRIBUTE_ROUTE_COMPONENT_TYPE]: 'Component', [SEMANTIC_ATTRIBUTE_ROUTE_HAS_BEEN_SEEN]: false, - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'manual', + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: SPAN_ORIGIN_AUTO_NAVIGATION_REACT_NATIVE_NAVIGATION, [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'component', [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation', [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: 1, @@ -200,7 +201,7 @@ describe('React Native Navigation Instrumentation', () => { [SEMANTIC_ATTRIBUTE_ROUTE_COMPONENT_ID]: '0', [SEMANTIC_ATTRIBUTE_ROUTE_COMPONENT_TYPE]: 'Component', [SEMANTIC_ATTRIBUTE_ROUTE_HAS_BEEN_SEEN]: false, - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'manual', + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: SPAN_ORIGIN_AUTO_NAVIGATION_REACT_NATIVE_NAVIGATION, [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'component', [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation', [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: 1, @@ -288,7 +289,7 @@ describe('React Native Navigation Instrumentation', () => { [SEMANTIC_ATTRIBUTE_PREVIOUS_ROUTE_NAME]: 'Test 1', [SEMANTIC_ATTRIBUTE_PREVIOUS_ROUTE_COMPONENT_ID]: '1', [SEMANTIC_ATTRIBUTE_PREVIOUS_ROUTE_COMPONENT_TYPE]: 'Component', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'manual', + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: SPAN_ORIGIN_AUTO_NAVIGATION_REACT_NATIVE_NAVIGATION, [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'component', [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation', [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: 1, @@ -336,7 +337,7 @@ describe('React Native Navigation Instrumentation', () => { [SEMANTIC_ATTRIBUTE_ROUTE_COMPONENT_ID]: '1', [SEMANTIC_ATTRIBUTE_ROUTE_COMPONENT_TYPE]: 'Component', [SEMANTIC_ATTRIBUTE_ROUTE_HAS_BEEN_SEEN]: false, - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'manual', + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: SPAN_ORIGIN_AUTO_NAVIGATION_REACT_NATIVE_NAVIGATION, [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'component', [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation', [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: 1, diff --git a/packages/core/test/tracing/reactnavigation.test.ts b/packages/core/test/tracing/reactnavigation.test.ts index a7e3ba7a0..6d8a234d6 100644 --- a/packages/core/test/tracing/reactnavigation.test.ts +++ b/packages/core/test/tracing/reactnavigation.test.ts @@ -5,6 +5,7 @@ import { getActiveSpan, getCurrentScope, getGlobalScope, getIsolationScope, setC import type { Event, Measurements, StartSpanOptions } from '@sentry/types'; import { nativeFramesIntegration, reactNativeTracingIntegration } from '../../src/js'; +import { SPAN_ORIGIN_AUTO_NAVIGATION_REACT_NAVIGATION } from '../../src/js/tracing/origin'; import type { NavigationRoute } from '../../src/js/tracing/reactnavigation'; import { reactNavigationIntegration } from '../../src/js/tracing/reactnavigation'; import { @@ -73,7 +74,7 @@ describe('ReactNavigationInstrumentation', () => { [SEMANTIC_ATTRIBUTE_ROUTE_NAME]: 'Initial Screen', [SEMANTIC_ATTRIBUTE_ROUTE_KEY]: 'initial_screen', [SEMANTIC_ATTRIBUTE_ROUTE_HAS_BEEN_SEEN]: false, - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'manual', + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: SPAN_ORIGIN_AUTO_NAVIGATION_REACT_NAVIGATION, [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'component', [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation', [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: 1, @@ -182,7 +183,7 @@ describe('ReactNavigationInstrumentation', () => { [SEMANTIC_ATTRIBUTE_ROUTE_HAS_BEEN_SEEN]: false, [SEMANTIC_ATTRIBUTE_PREVIOUS_ROUTE_NAME]: 'Initial Screen', [SEMANTIC_ATTRIBUTE_PREVIOUS_ROUTE_KEY]: 'initial_screen', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'manual', + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: SPAN_ORIGIN_AUTO_NAVIGATION_REACT_NAVIGATION, [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'component', [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation', [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: 1, @@ -219,7 +220,7 @@ describe('ReactNavigationInstrumentation', () => { [SEMANTIC_ATTRIBUTE_ROUTE_HAS_BEEN_SEEN]: false, [SEMANTIC_ATTRIBUTE_PREVIOUS_ROUTE_NAME]: 'New Screen', [SEMANTIC_ATTRIBUTE_PREVIOUS_ROUTE_KEY]: 'new_screen', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'manual', + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: SPAN_ORIGIN_AUTO_NAVIGATION_REACT_NAVIGATION, [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'component', [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation', [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: 1, @@ -258,7 +259,7 @@ describe('ReactNavigationInstrumentation', () => { [SEMANTIC_ATTRIBUTE_ROUTE_HAS_BEEN_SEEN]: false, [SEMANTIC_ATTRIBUTE_PREVIOUS_ROUTE_NAME]: 'Initial Screen', [SEMANTIC_ATTRIBUTE_PREVIOUS_ROUTE_KEY]: 'initial_screen', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'manual', + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: SPAN_ORIGIN_AUTO_NAVIGATION_REACT_NAVIGATION, [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'component', [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation', [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: 1, diff --git a/packages/core/test/tracing/reactnavigation.ttid.test.tsx b/packages/core/test/tracing/reactnavigation.ttid.test.tsx index 606562505..78dacd003 100644 --- a/packages/core/test/tracing/reactnavigation.ttid.test.tsx +++ b/packages/core/test/tracing/reactnavigation.ttid.test.tsx @@ -15,6 +15,7 @@ import * as Sentry from '../../src/js'; import { startSpanManual } from '../../src/js'; import { TimeToFullDisplay, TimeToInitialDisplay } from '../../src/js/tracing'; import { _setAppStartEndTimestampMs } from '../../src/js/tracing/integrations/appStart'; +import { SPAN_ORIGIN_AUTO_NAVIGATION_REACT_NAVIGATION, SPAN_ORIGIN_AUTO_UI_TIME_TO_DISPLAY, SPAN_ORIGIN_MANUAL_UI_TIME_TO_DISPLAY } from '../../src/js/tracing/origin'; import { isHermesEnabled, notWeb } from '../../src/js/utils/environment'; import { createSentryEventEmitter } from '../../src/js/utils/sentryeventemitter'; import { RN_GLOBAL_OBJ } from '../../src/js/utils/worldwide'; @@ -78,11 +79,11 @@ describe('React Navigation - TTID', () => { expect.objectContaining>({ data: { 'sentry.op': 'ui.load.initial_display', - 'sentry.origin': 'manual', + 'sentry.origin': SPAN_ORIGIN_AUTO_UI_TIME_TO_DISPLAY, }, description: 'New Screen initial display', op: 'ui.load.initial_display', - origin: 'manual', + origin: SPAN_ORIGIN_AUTO_UI_TIME_TO_DISPLAY, status: 'ok', start_timestamp: transaction.start_timestamp, timestamp: expect.any(Number), @@ -108,11 +109,11 @@ describe('React Navigation - TTID', () => { expect.objectContaining>({ data: { 'sentry.op': 'ui.load.initial_display', - 'sentry.origin': 'manual', + 'sentry.origin': SPAN_ORIGIN_AUTO_UI_TIME_TO_DISPLAY, }, description: 'New Screen initial display', op: 'ui.load.initial_display', - origin: 'manual', + origin: SPAN_ORIGIN_AUTO_UI_TIME_TO_DISPLAY, status: 'ok', start_timestamp: transaction.start_timestamp, timestamp: expect.any(Number), @@ -144,11 +145,11 @@ describe('React Navigation - TTID', () => { expect.objectContaining>({ data: { 'sentry.op': 'ui.load.initial_display', - 'sentry.origin': 'manual', + 'sentry.origin': SPAN_ORIGIN_AUTO_UI_TIME_TO_DISPLAY, }, description: 'New Screen initial display', op: 'ui.load.initial_display', - origin: 'manual', + origin: SPAN_ORIGIN_AUTO_UI_TIME_TO_DISPLAY, status: 'ok', start_timestamp: transaction.start_timestamp, timestamp: expect.any(Number), @@ -200,11 +201,11 @@ describe('React Navigation - TTID', () => { expect.objectContaining>({ data: { 'sentry.op': 'navigation.processing', - 'sentry.origin': 'manual', + 'sentry.origin': SPAN_ORIGIN_AUTO_NAVIGATION_REACT_NAVIGATION, }, description: 'Processing navigation to New Screen', op: 'navigation.processing', - origin: 'manual', + origin: SPAN_ORIGIN_AUTO_NAVIGATION_REACT_NAVIGATION, status: 'ok', start_timestamp: transaction.start_timestamp, timestamp: expect.any(Number), @@ -227,11 +228,11 @@ describe('React Navigation - TTID', () => { expect.objectContaining>({ data: { 'sentry.op': 'navigation.processing', - 'sentry.origin': 'manual', + 'sentry.origin': SPAN_ORIGIN_AUTO_NAVIGATION_REACT_NAVIGATION, }, description: 'Processing navigation to Initial Screen', op: 'navigation.processing', - origin: 'manual', + origin: SPAN_ORIGIN_AUTO_NAVIGATION_REACT_NAVIGATION, status: 'ok', start_timestamp: expect.any(Number), timestamp: expect.any(Number), @@ -257,11 +258,11 @@ describe('React Navigation - TTID', () => { expect.objectContaining>({ data: { 'sentry.op': 'ui.load.initial_display', - 'sentry.origin': 'manual', + 'sentry.origin': SPAN_ORIGIN_AUTO_UI_TIME_TO_DISPLAY, }, description: 'Initial Screen initial display', op: 'ui.load.initial_display', - origin: 'manual', + origin: SPAN_ORIGIN_AUTO_UI_TIME_TO_DISPLAY, status: 'ok', start_timestamp: mockedAppStartTimeSeconds, timestamp: expect.any(Number), @@ -291,11 +292,11 @@ describe('React Navigation - TTID', () => { expect.objectContaining>({ data: { 'sentry.op': 'ui.load.full_display', - 'sentry.origin': 'manual', + 'sentry.origin': SPAN_ORIGIN_MANUAL_UI_TIME_TO_DISPLAY, }, description: 'Time To Full Display', op: 'ui.load.full_display', - origin: 'manual', + origin: SPAN_ORIGIN_MANUAL_UI_TIME_TO_DISPLAY, status: 'ok', start_timestamp: mockedAppStartTimeSeconds, timestamp: expect.any(Number), @@ -367,11 +368,11 @@ describe('React Navigation - TTID', () => { expect.objectContaining>({ data: { 'sentry.op': 'ui.load.initial_display', - 'sentry.origin': 'manual', + 'sentry.origin': SPAN_ORIGIN_AUTO_UI_TIME_TO_DISPLAY, }, description: 'New Screen initial display', op: 'ui.load.initial_display', - origin: 'manual', + origin: SPAN_ORIGIN_AUTO_UI_TIME_TO_DISPLAY, status: 'cancelled', start_timestamp: transaction.start_timestamp, timestamp: expect.any(Number), diff --git a/packages/core/test/tracing/timetodisplay.test.tsx b/packages/core/test/tracing/timetodisplay.test.tsx index 0b2f80faf..c9f58ff52 100644 --- a/packages/core/test/tracing/timetodisplay.test.tsx +++ b/packages/core/test/tracing/timetodisplay.test.tsx @@ -6,6 +6,8 @@ import type { Event, Measurements, Span, SpanJSON} from '@sentry/types'; import * as React from "react"; import * as TestRenderer from 'react-test-renderer'; +import { SPAN_ORIGIN_MANUAL_UI_TIME_TO_DISPLAY } from '../../src/js/tracing/origin'; +import { SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '../../src/js/tracing/semanticAttributes'; import { startTimeToFullDisplaySpan, startTimeToInitialDisplaySpan, TimeToFullDisplay, TimeToInitialDisplay } from '../../src/js/tracing/timetodisplay'; import { getDefaultTestClientOptions, TestClient } from '../mocks/client'; import { secondAgoTimestampMs, secondInFutureTimestampMs } from '../testutils'; @@ -382,8 +384,8 @@ function getFullDisplaySpan(span?: Span) { function expectFinishedInitialDisplaySpan(actualSpan?: Span, expectedParentSpan?: Span) { expect(spanToJSON(actualSpan!)).toEqual(expect.objectContaining>({ data: { - "sentry.op": "ui.load.initial_display", - "sentry.origin": "manual", + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: "ui.load.initial_display", + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: SPAN_ORIGIN_MANUAL_UI_TIME_TO_DISPLAY, }, description: 'Time To Initial Display', op: 'ui.load.initial_display', @@ -397,8 +399,8 @@ function expectFinishedInitialDisplaySpan(actualSpan?: Span, expectedParentSpan? function expectFinishedFullDisplaySpan(actualSpan?: Span, expectedParentSpan?: Span) { expect(spanToJSON(actualSpan!)).toEqual(expect.objectContaining>({ data: { - "sentry.op": "ui.load.full_display", - "sentry.origin": "manual", + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: "ui.load.full_display", + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: SPAN_ORIGIN_MANUAL_UI_TIME_TO_DISPLAY, }, description: 'Time To Full Display', op: 'ui.load.full_display', @@ -413,8 +415,8 @@ function expectFinishedFullDisplaySpan(actualSpan?: Span, expectedParentSpan?: S function expectDeadlineExceededFullDisplaySpan(actualSpan?: Span, expectedParentSpan?: Span) { expect(spanToJSON(actualSpan!)).toEqual(expect.objectContaining>({ data: { - "sentry.op": "ui.load.full_display", - "sentry.origin": "manual", + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: "ui.load.full_display", + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: SPAN_ORIGIN_MANUAL_UI_TIME_TO_DISPLAY, }, description: 'Time To Full Display', op: 'ui.load.full_display',