@@ -7,6 +7,7 @@ import type {
7
7
Event ,
8
8
EventProcessor ,
9
9
Integration ,
10
+ Span ,
10
11
Transaction as TransactionType ,
11
12
TransactionContext ,
12
13
} from '@sentry/types' ;
@@ -23,6 +24,7 @@ import { cancelInBackground, onlySampleIfChildSpans } from './transaction';
23
24
import type { BeforeNavigate , RouteChangeContextData } from './types' ;
24
25
import {
25
26
adjustTransactionDuration ,
27
+ getBundleStartTimestampMs ,
26
28
getTimeOriginMilliseconds ,
27
29
isNearToNow ,
28
30
setSpanDurationAsMeasurement ,
@@ -152,6 +154,7 @@ export class ReactNativeTracing implements Integration {
152
154
private _hasSetTracePropagationTargets : boolean ;
153
155
private _hasSetTracingOrigins : boolean ;
154
156
private _currentViewName : string | undefined ;
157
+ private _firstConstructorCallTimestampMs : number | undefined ;
155
158
156
159
public constructor ( options : Partial < ReactNativeTracingOptions > = { } ) {
157
160
this . _hasSetTracePropagationTargets = ! ! (
@@ -294,6 +297,13 @@ export class ReactNativeTracing implements Integration {
294
297
this . _appStartFinishTimestamp = endTimestamp ;
295
298
}
296
299
300
+ /**
301
+ * Sets the root component first constructor call timestamp.
302
+ */
303
+ public setRootComponentFirstConstructorCallTimestampMs ( timestamp : number ) : void {
304
+ this . _firstConstructorCallTimestampMs = timestamp ;
305
+ }
306
+
297
307
/**
298
308
* Starts a new transaction for a user interaction.
299
309
* @param userInteractionId Consists of `op` representation UI Event and `elementId` unique element identifier on current screen.
@@ -478,17 +488,46 @@ export class ReactNativeTracing implements Integration {
478
488
}
479
489
480
490
const op = appStart . isColdStart ? APP_START_COLD_OP : APP_START_WARM_OP ;
481
- transaction . startChild ( {
491
+ const appStartSpan = transaction . startChild ( {
482
492
description : appStart . isColdStart ? 'Cold App Start' : 'Warm App Start' ,
483
493
op,
484
494
startTimestamp : appStartTimeSeconds ,
485
495
endTimestamp : this . _appStartFinishTimestamp ,
486
496
} ) ;
497
+ this . _addJSExecutionBeforeRoot ( appStartSpan ) ;
487
498
488
499
const measurement = appStart . isColdStart ? APP_START_COLD : APP_START_WARM ;
489
500
transaction . setMeasurement ( measurement , appStartDurationMilliseconds , 'millisecond' ) ;
490
501
}
491
502
503
+ /**
504
+ * Adds JS Execution before React Root. If `Sentry.wrap` is not used, create a span for the start of JS Bundle execution.
505
+ */
506
+ private _addJSExecutionBeforeRoot ( appStartSpan : Span ) : void {
507
+ const bundleStartTimestampMs = getBundleStartTimestampMs ( ) ;
508
+ if ( ! bundleStartTimestampMs ) {
509
+ return ;
510
+ }
511
+
512
+ if ( ! this . _firstConstructorCallTimestampMs ) {
513
+ logger . warn ( 'Missing the root component first constructor call timestamp.' ) ;
514
+ appStartSpan . startChild ( {
515
+ description : 'JS Bundle Execution Start' ,
516
+ op : appStartSpan . op ,
517
+ startTimestamp : bundleStartTimestampMs / 1000 ,
518
+ endTimestamp : bundleStartTimestampMs / 1000 ,
519
+ } ) ;
520
+ return ;
521
+ }
522
+
523
+ appStartSpan . startChild ( {
524
+ description : 'JS Bundle Execution Before React Root' ,
525
+ op : appStartSpan . op ,
526
+ startTimestamp : bundleStartTimestampMs / 1000 ,
527
+ endTimestamp : this . _firstConstructorCallTimestampMs / 1000 ,
528
+ } ) ;
529
+ }
530
+
492
531
/** To be called when the route changes, but BEFORE the components of the new route mount. */
493
532
private _onRouteWillChange ( context : TransactionContext ) : TransactionType | undefined {
494
533
return this . _createRouteTransaction ( context ) ;
0 commit comments