@@ -16,7 +16,7 @@ import {
1616 APP_START_COLD as APP_START_COLD_MEASUREMENT ,
1717 APP_START_WARM as APP_START_WARM_MEASUREMENT ,
1818} from '../../measurements' ;
19- import type { NativeAppStartResponse } from '../../NativeRNSentry' ;
19+ import type { NativeAppStartResponse , NativeFramesResponse } from '../../NativeRNSentry' ;
2020import type { ReactNativeClientOptions } from '../../options' ;
2121import { convertSpanToTransaction , isRootSpan , setEndTimeValue } from '../../utils/span' ;
2222import { NATIVE } from '../../wrapper' ;
@@ -49,7 +49,12 @@ const MAX_APP_START_AGE_MS = 60_000;
4949/** App Start transaction name */
5050const APP_START_TX_NAME = 'App Start' ;
5151
52- let recordedAppStartEndTimestampMs : number | undefined = undefined ;
52+ interface AppStartEndData {
53+ timestampMs : number ;
54+ endFrames : NativeFramesResponse | null ;
55+ }
56+
57+ let appStartEndData : AppStartEndData | undefined = undefined ;
5358let isRecordedAppStartEndTimestampMsManual = false ;
5459
5560let rootComponentCreationTimestampMs : number | undefined = undefined ;
@@ -76,7 +81,24 @@ export async function _captureAppStart({ isManual }: { isManual: boolean }): Pro
7681 }
7782
7883 isRecordedAppStartEndTimestampMsManual = isManual ;
79- _setAppStartEndTimestampMs ( timestampInSeconds ( ) * 1000 ) ;
84+
85+ const timestampMs = timestampInSeconds ( ) * 1000 ;
86+ let endFrames : NativeFramesResponse | null = null ;
87+
88+ if ( NATIVE . enableNative ) {
89+ try {
90+ endFrames = await NATIVE . fetchNativeFrames ( ) ;
91+ logger . debug ( '[AppStart] Captured end frames for app start.' , endFrames ) ;
92+ } catch ( error ) {
93+ logger . debug ( '[AppStart] Failed to capture end frames for app start.' , error ) ;
94+ }
95+ }
96+
97+ _setAppStartEndData ( {
98+ timestampMs,
99+ endFrames,
100+ } ) ;
101+
80102 await client . getIntegrationByName < AppStartIntegration > ( INTEGRATION_NAME ) ?. captureStandaloneAppStart ( ) ;
81103}
82104
@@ -85,8 +107,7 @@ export async function _captureAppStart({ isManual }: { isManual: boolean }): Pro
85107 * Used automatically by `Sentry.wrap` and `Sentry.ReactNativeProfiler`.
86108 */
87109export function setRootComponentCreationTimestampMs ( timestampMs : number ) : void {
88- recordedAppStartEndTimestampMs &&
89- logger . warn ( 'Setting Root component creation timestamp after app start end is set.' ) ;
110+ appStartEndData ?. timestampMs && logger . warn ( 'Setting Root component creation timestamp after app start end is set.' ) ;
90111 rootComponentCreationTimestampMs && logger . warn ( 'Overwriting already set root component creation timestamp.' ) ;
91112 rootComponentCreationTimestampMs = timestampMs ;
92113 isRootComponentCreationTimestampMsManual = true ;
@@ -107,9 +128,9 @@ export function _setRootComponentCreationTimestampMs(timestampMs: number): void
107128 *
108129 * @private
109130 */
110- export const _setAppStartEndTimestampMs = ( timestampMs : number ) : void => {
111- recordedAppStartEndTimestampMs && logger . warn ( 'Overwriting already set app start.' ) ;
112- recordedAppStartEndTimestampMs = timestampMs ;
131+ export const _setAppStartEndData = ( data : AppStartEndData ) : void => {
132+ appStartEndData && logger . warn ( 'Overwriting already set app start end data .' ) ;
133+ appStartEndData = data ;
113134} ;
114135
115136/**
@@ -121,6 +142,29 @@ export function _clearRootComponentCreationTimestampMs(): void {
121142 rootComponentCreationTimestampMs = undefined ;
122143}
123144
145+ /**
146+ * Attaches frame data to a span's data object.
147+ */
148+ function attachFrameDataToSpan ( span : SpanJSON , frames : NativeFramesResponse ) : void {
149+ if ( frames . totalFrames <= 0 && frames . slowFrames <= 0 && frames . totalFrames <= 0 ) {
150+ logger . warn ( `[AppStart] Detected zero slow or frozen frames. Not adding measurements to spanId (${ span . span_id } ).` ) ;
151+ return ;
152+ }
153+ span . data = span . data || { } ;
154+ span . data [ 'frames.total' ] = frames . totalFrames ;
155+ span . data [ 'frames.slow' ] = frames . slowFrames ;
156+ span . data [ 'frames.frozen' ] = frames . frozenFrames ;
157+
158+ logger . debug ( '[AppStart] Attached frame data to span.' , {
159+ spanId : span . span_id ,
160+ frameData : {
161+ total : frames . totalFrames ,
162+ slow : frames . slowFrames ,
163+ frozen : frames . frozenFrames ,
164+ } ,
165+ } ) ;
166+ }
167+
124168/**
125169 * Adds AppStart spans from the native layer to the transaction event.
126170 */
@@ -220,6 +264,21 @@ export const appStartIntegration = ({
220264
221265 logger . debug ( '[AppStart] App start tracking standalone root span (transaction).' ) ;
222266
267+ if ( ! appStartEndData ?. endFrames && NATIVE . enableNative ) {
268+ try {
269+ const endFrames = await NATIVE . fetchNativeFrames ( ) ;
270+ logger . debug ( '[AppStart] Captured end frames for standalone app start.' , endFrames ) ;
271+
272+ const currentTimestamp = appStartEndData ?. timestampMs || timestampInSeconds ( ) * 1000 ;
273+ _setAppStartEndData ( {
274+ timestampMs : currentTimestamp ,
275+ endFrames,
276+ } ) ;
277+ } catch ( error ) {
278+ logger . debug ( '[AppStart] Failed to capture frames for standalone app start.' , error ) ;
279+ }
280+ }
281+
223282 const span = startInactiveSpan ( {
224283 forceTransaction : true ,
225284 name : APP_START_TX_NAME ,
@@ -288,10 +347,10 @@ export const appStartIntegration = ({
288347 return ;
289348 }
290349
291- const appStartEndTimestampMs = recordedAppStartEndTimestampMs || getBundleStartTimestampMs ( ) ;
350+ const appStartEndTimestampMs = appStartEndData ?. timestampMs || getBundleStartTimestampMs ( ) ;
292351 if ( ! appStartEndTimestampMs ) {
293352 logger . warn (
294- '[AppStart] Javascript failed to record app start end. `setAppStartEndTimestampMs ` was not called nor could the bundle start be found.' ,
353+ '[AppStart] Javascript failed to record app start end. `_setAppStartEndData ` was not called nor could the bundle start be found.' ,
295354 ) ;
296355 return ;
297356 }
@@ -368,6 +427,11 @@ export const appStartIntegration = ({
368427 parent_span_id : event . contexts . trace . span_id ,
369428 origin,
370429 } ) ;
430+
431+ if ( appStartEndData ?. endFrames ) {
432+ attachFrameDataToSpan ( appStartSpanJSON , appStartEndData . endFrames ) ;
433+ }
434+
371435 const jsExecutionSpanJSON = createJSExecutionStartSpan ( appStartSpanJSON , rootComponentCreationTimestampMs ) ;
372436
373437 const appStartSpans = [
0 commit comments