Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
### Features

- Add experimental `enableStandaloneAppStartTracing` to send app start as a standalone `app.start` transaction ([#6359](https://github.com/getsentry/sentry-react-native/pull/6359))
- Set `app.vitals.start.screen` on the standalone `app.start` transaction from the current route ([#6369](https://github.com/getsentry/sentry-react-native/pull/6369))
- Add `enableTurboModuleTracking` opt-in experimental option to enable Turbo Module performance tracking in the New Architecture ([#6307](https://github.com/getsentry/sentry-react-native/pull/6307))
- Use the runtime's native `btoa` for envelope base64 encoding when available, to improve `captureEnvelope` performance. Falls back to the bundled JS encoder if `btoa` is missing ([#6351](https://github.com/getsentry/sentry-react-native/pull/6351)).
- Opt-in build-time auto-wrap for Expo Router's per-route `ErrorBoundary` ([#6347](https://github.com/getsentry/sentry-react-native/pull/6347))
Expand Down
11 changes: 11 additions & 0 deletions packages/core/src/js/tracing/integrations/appStart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ import {
UI_LOAD as UI_LOAD_OP,
} from '../ops';
import { SPAN_ORIGIN_AUTO_APP_START, SPAN_ORIGIN_MANUAL_APP_START } from '../origin';
import { getCurrentReactNativeTracingIntegration } from '../reactnativetracing';
import {
SEMANTIC_ATTRIBUTE_APP_VITALS_START_SCREEN,
SEMANTIC_ATTRIBUTE_APP_VITALS_START_TYPE,
SEMANTIC_ATTRIBUTE_APP_VITALS_START_VALUE,
SEMANTIC_ATTRIBUTE_SENTRY_OP,
Expand Down Expand Up @@ -659,6 +661,15 @@ export const appStartIntegration = ({
event.contexts.trace.data[SEMANTIC_ATTRIBUTE_APP_VITALS_START_VALUE] = appStartDurationMs;
event.contexts.trace.data[SEMANTIC_ATTRIBUTE_APP_VITALS_START_TYPE] = appStart.type;

// Screen shown when app start completes. Unlike the non-standalone `ui.load` transaction
// (whose name is the screen, which Relay backfills from), the standalone transaction is named
// `App Start`, so we set the screen explicitly. Sourced from the current route tracked by the
// tracing integration; omitted when no route has been registered yet at capture time.
const screen = getCurrentReactNativeTracingIntegration()?.state.currentRoute;
if (screen) {
event.contexts.trace.data[SEMANTIC_ATTRIBUTE_APP_VITALS_START_SCREEN] = screen;
}

// Minimal parent referencing the root transaction span, so the breakdown spans attach
// directly under it (the helpers only read op/origin/span_id/trace_id/start_timestamp).
// `data` is shared with the root trace context so frame data lands on the root span.
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/js/tracing/semanticAttributes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ export const SEMANTIC_ATTRIBUTE_NAVIGATION_ACTION_TYPE = 'navigation.action_type
// App start vitals (Span V2 / EAP). Emitted on the standalone `app.start` transaction.
export const SEMANTIC_ATTRIBUTE_APP_VITALS_START_VALUE = 'app.vitals.start.value';
export const SEMANTIC_ATTRIBUTE_APP_VITALS_START_TYPE = 'app.vitals.start.type';
export const SEMANTIC_ATTRIBUTE_APP_VITALS_START_SCREEN = 'app.vitals.start.screen';
28 changes: 28 additions & 0 deletions packages/core/test/tracing/integrations/appStart.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ import {
setRootComponentCreationTimestampMs,
} from '../../../src/js/tracing/integrations/appStart';
import { SPAN_ORIGIN_AUTO_APP_START, SPAN_ORIGIN_MANUAL_APP_START } from '../../../src/js/tracing/origin';
import * as ReactNativeTracing from '../../../src/js/tracing/reactnativetracing';
import {
SEMANTIC_ATTRIBUTE_APP_VITALS_START_SCREEN,
SEMANTIC_ATTRIBUTE_APP_VITALS_START_TYPE,
SEMANTIC_ATTRIBUTE_APP_VITALS_START_VALUE,
} from '../../../src/js/tracing/semanticAttributes';
Expand Down Expand Up @@ -147,6 +149,32 @@ describe('App Start Integration', () => {
});
});

it('sets app.vitals.start.screen from the current route', async () => {
const [timeOriginMilliseconds, appStartTimeMilliseconds] = mockAppStart({ cold: true });
const screenSpy = jest
.spyOn(ReactNativeTracing, 'getCurrentReactNativeTracingIntegration')
.mockReturnValue({ state: { currentRoute: 'HomeScreen' } } as ReturnType<
typeof ReactNativeTracing.getCurrentReactNativeTracingIntegration
>);

try {
const actualEvent = await captureStandAloneAppStart();
expect(actualEvent).toEqual(
expectEventWithStandaloneColdAppStart(actualEvent, { timeOriginMilliseconds, appStartTimeMilliseconds }),
);
expect(actualEvent?.contexts?.trace?.data?.[SEMANTIC_ATTRIBUTE_APP_VITALS_START_SCREEN]).toBe('HomeScreen');
} finally {
screenSpy.mockRestore();
}
});

it('omits app.vitals.start.screen when no route has been registered', async () => {
mockAppStart({ cold: true });

const actualEvent = await captureStandAloneAppStart();
expect(actualEvent?.contexts?.trace?.data).not.toHaveProperty(SEMANTIC_ATTRIBUTE_APP_VITALS_START_SCREEN);
});

it('Does not add any spans or measurements when App Start Span is longer than threshold', async () => {
set__DEV__(false);
mockTooLongAppStart();
Expand Down
Loading