@@ -16,7 +16,7 @@ import {
16
16
APP_START_COLD as APP_START_COLD_MEASUREMENT ,
17
17
APP_START_WARM as APP_START_WARM_MEASUREMENT ,
18
18
} from '../../measurements' ;
19
- import type { NativeAppStartResponse } from '../../NativeRNSentry' ;
19
+ import type { NativeAppStartResponse , NativeFramesResponse } from '../../NativeRNSentry' ;
20
20
import type { ReactNativeClientOptions } from '../../options' ;
21
21
import { convertSpanToTransaction , isRootSpan , setEndTimeValue } from '../../utils/span' ;
22
22
import { NATIVE } from '../../wrapper' ;
@@ -49,7 +49,12 @@ const MAX_APP_START_AGE_MS = 60_000;
49
49
/** App Start transaction name */
50
50
const APP_START_TX_NAME = 'App Start' ;
51
51
52
- let recordedAppStartEndTimestampMs : number | undefined = undefined ;
52
+ interface AppStartEndData {
53
+ timestampMs : number ;
54
+ endFrames : NativeFramesResponse | null ;
55
+ }
56
+
57
+ let appStartEndData : AppStartEndData | undefined = undefined ;
53
58
let isRecordedAppStartEndTimestampMsManual = false ;
54
59
55
60
let rootComponentCreationTimestampMs : number | undefined = undefined ;
@@ -76,7 +81,24 @@ export async function _captureAppStart({ isManual }: { isManual: boolean }): Pro
76
81
}
77
82
78
83
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
+
80
102
await client . getIntegrationByName < AppStartIntegration > ( INTEGRATION_NAME ) ?. captureStandaloneAppStart ( ) ;
81
103
}
82
104
@@ -85,8 +107,7 @@ export async function _captureAppStart({ isManual }: { isManual: boolean }): Pro
85
107
* Used automatically by `Sentry.wrap` and `Sentry.ReactNativeProfiler`.
86
108
*/
87
109
export 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.' ) ;
90
111
rootComponentCreationTimestampMs && logger . warn ( 'Overwriting already set root component creation timestamp.' ) ;
91
112
rootComponentCreationTimestampMs = timestampMs ;
92
113
isRootComponentCreationTimestampMsManual = true ;
@@ -107,9 +128,9 @@ export function _setRootComponentCreationTimestampMs(timestampMs: number): void
107
128
*
108
129
* @private
109
130
*/
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 ;
113
134
} ;
114
135
115
136
/**
@@ -121,6 +142,29 @@ export function _clearRootComponentCreationTimestampMs(): void {
121
142
rootComponentCreationTimestampMs = undefined ;
122
143
}
123
144
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
+
124
168
/**
125
169
* Adds AppStart spans from the native layer to the transaction event.
126
170
*/
@@ -220,6 +264,21 @@ export const appStartIntegration = ({
220
264
221
265
logger . debug ( '[AppStart] App start tracking standalone root span (transaction).' ) ;
222
266
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
+
223
282
const span = startInactiveSpan ( {
224
283
forceTransaction : true ,
225
284
name : APP_START_TX_NAME ,
@@ -288,10 +347,10 @@ export const appStartIntegration = ({
288
347
return ;
289
348
}
290
349
291
- const appStartEndTimestampMs = recordedAppStartEndTimestampMs || getBundleStartTimestampMs ( ) ;
350
+ const appStartEndTimestampMs = appStartEndData ?. timestampMs || getBundleStartTimestampMs ( ) ;
292
351
if ( ! appStartEndTimestampMs ) {
293
352
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.' ,
295
354
) ;
296
355
return ;
297
356
}
@@ -368,6 +427,11 @@ export const appStartIntegration = ({
368
427
parent_span_id : event . contexts . trace . span_id ,
369
428
origin,
370
429
} ) ;
430
+
431
+ if ( appStartEndData ?. endFrames ) {
432
+ attachFrameDataToSpan ( appStartSpanJSON , appStartEndData . endFrames ) ;
433
+ }
434
+
371
435
const jsExecutionSpanJSON = createJSExecutionStartSpan ( appStartSpanJSON , rootComponentCreationTimestampMs ) ;
372
436
373
437
const appStartSpans = [
0 commit comments