@@ -52,6 +52,7 @@ interface TracingOptions {
52
52
* Default: 500
53
53
*/
54
54
idleTimeout : number ;
55
+
55
56
/**
56
57
* Flag to enable/disable creation of `navigation` transaction on history changes. Useful for react applications with
57
58
* a router.
@@ -60,6 +61,13 @@ interface TracingOptions {
60
61
*/
61
62
startTransactionOnLocationChange : boolean ;
62
63
64
+ /**
65
+ * Flag to enable/disable creation of `pageload` transaction on first pageload.
66
+ *
67
+ * Default: true
68
+ */
69
+ startTransactionOnPageLoad : boolean ;
70
+
63
71
/**
64
72
* The maximum duration of a transaction before it will be marked as "deadline_exceeded".
65
73
* If you never want to mark a transaction set it to 0.
@@ -137,8 +145,6 @@ export class Tracing implements Integration {
137
145
138
146
public static _activities : { [ key : number ] : Activity } = { } ;
139
147
140
- private static _idleTransactionEndTimestamp : number = 0 ;
141
-
142
148
private readonly _emitOptionsWarning : boolean = false ;
143
149
144
150
private static _performanceCursor : number = 0 ;
@@ -174,6 +180,7 @@ export class Tracing implements Integration {
174
180
) ;
175
181
} ,
176
182
startTransactionOnLocationChange : true ,
183
+ startTransactionOnPageLoad : true ,
177
184
traceFetch : true ,
178
185
traceXHR : true ,
179
186
tracingOrigins : defaultTracingOrigins ,
@@ -202,7 +209,7 @@ export class Tracing implements Integration {
202
209
}
203
210
204
211
// Starting pageload transaction
205
- if ( global . location && global . location . href ) {
212
+ if ( global . location && global . location . href && Tracing . options && Tracing . options . startTransactionOnPageLoad ) {
206
213
// Use `${global.location.href}` as transaction name
207
214
Tracing . startIdleTransaction ( {
208
215
name : global . location . href ,
@@ -285,7 +292,7 @@ export class Tracing implements Integration {
285
292
) ;
286
293
Tracing . _activeTransaction . setStatus ( SpanStatus . DeadlineExceeded ) ;
287
294
Tracing . _activeTransaction . setTag ( 'heartbeat' , 'failed' ) ;
288
- Tracing . finishIdleTransaction ( ) ;
295
+ Tracing . finishIdleTransaction ( timestampWithMs ( ) ) ;
289
296
}
290
297
}
291
298
Tracing . _prevHeartbeatString = heartbeatString ;
@@ -303,7 +310,7 @@ export class Tracing implements Integration {
303
310
Tracing . _log ( `[Tracing] Transaction: ${ SpanStatus . Cancelled } -> since tab moved to the background` ) ;
304
311
Tracing . _activeTransaction . setStatus ( SpanStatus . Cancelled ) ;
305
312
Tracing . _activeTransaction . setTag ( 'visibilitychange' , 'document.hidden' ) ;
306
- Tracing . finishIdleTransaction ( ) ;
313
+ Tracing . finishIdleTransaction ( timestampWithMs ( ) ) ;
307
314
}
308
315
} ) ;
309
316
}
@@ -403,7 +410,6 @@ export class Tracing implements Integration {
403
410
message : safeJoin ( args , ' ' ) ,
404
411
type : 'debug' ,
405
412
} ) ;
406
- return ;
407
413
}
408
414
}
409
415
logger . log ( ...args ) ;
@@ -413,11 +419,6 @@ export class Tracing implements Integration {
413
419
* Starts a Transaction waiting for activity idle to finish
414
420
*/
415
421
public static startIdleTransaction ( transactionContext : TransactionContext ) : Transaction | undefined {
416
- // If we already have an active transaction it means one of two things
417
- // a) The user did rapid navigation changes and didn't wait until the transaction was finished
418
- // b) A activity wasn't popped correctly and therefore the transaction is stalling
419
- Tracing . finishIdleTransaction ( ) ;
420
-
421
422
Tracing . _log ( '[Tracing] startIdleTransaction' ) ;
422
423
423
424
const _getCurrentHub = Tracing . _getCurrentHub ;
@@ -448,27 +449,44 @@ export class Tracing implements Integration {
448
449
/**
449
450
* Finshes the current active transaction
450
451
*/
451
- public static finishIdleTransaction ( ) : void {
452
+ public static finishIdleTransaction ( endTimestamp : number ) : void {
452
453
const active = Tracing . _activeTransaction ;
453
454
if ( active ) {
455
+ Tracing . _log ( '[Tracing] finishing IdleTransaction' , new Date ( endTimestamp * 1000 ) . toISOString ( ) ) ;
454
456
Tracing . _addPerformanceEntries ( active ) ;
455
- Tracing . _log ( '[Tracing] finishIdleTransaction' ) ;
456
457
457
458
if ( active . spanRecorder ) {
458
- const timeout = ( Tracing . options && Tracing . options . idleTimeout ) || 100 ;
459
- active . spanRecorder . spans = active . spanRecorder . spans . filter ( ( finishedSpan : Span ) => {
460
- const keepSpan = finishedSpan . startTimestamp < Tracing . _idleTransactionEndTimestamp + timeout ;
459
+ active . spanRecorder . spans = active . spanRecorder . spans . filter ( ( span : Span ) => {
460
+ // If we are dealing with the transaction itself, we just return it
461
+ if ( span . spanId === active . spanId ) {
462
+ return span ;
463
+ }
464
+
465
+ // We cancel all pending spans with status "cancelled" to indicate the idle transaction was finished early
466
+ if ( ! span . endTimestamp ) {
467
+ span . endTimestamp = endTimestamp ;
468
+ span . setStatus ( SpanStatus . Cancelled ) ;
469
+ Tracing . _log ( '[Tracing] cancelling span since transaction ended early' , JSON . stringify ( span , undefined , 2 ) ) ;
470
+ }
471
+
472
+ // We remove all spans that happend after the end of the transaction
473
+ // This is here to prevent super long transactions and timing issues
474
+ const keepSpan = span . startTimestamp < endTimestamp ;
461
475
if ( ! keepSpan ) {
462
476
Tracing . _log (
463
477
'[Tracing] discarding Span since it happened after Transaction was finished' ,
464
- finishedSpan . toJSON ( ) ,
478
+ JSON . stringify ( span , undefined , 2 ) ,
465
479
) ;
466
480
}
467
481
return keepSpan ;
468
482
} ) ;
469
483
}
484
+
485
+ Tracing . _log ( '[Tracing] flushing IdleTransaction' ) ;
470
486
active . finish ( ) ;
471
487
Tracing . _resetActiveTransaction ( ) ;
488
+ } else {
489
+ Tracing . _log ( '[Tracing] No active IdleTransaction' ) ;
472
490
}
473
491
}
474
492
@@ -491,29 +509,29 @@ export class Tracing implements Integration {
491
509
492
510
// tslint:disable-next-line: completed-docs
493
511
function addPerformanceNavigationTiming ( parent : Span , entry : { [ key : string ] : number } , event : string ) : void {
494
- const span = parent . startChild ( {
512
+ parent . startChild ( {
495
513
description : event ,
514
+ endTimestamp : timeOrigin + Tracing . _msToSec ( entry [ `${ event } End` ] ) ,
496
515
op : 'browser' ,
516
+ startTimestamp : timeOrigin + Tracing . _msToSec ( entry [ `${ event } Start` ] ) ,
497
517
} ) ;
498
- span . startTimestamp = timeOrigin + Tracing . _msToSec ( entry [ `${ event } Start` ] ) ;
499
- span . endTimestamp = timeOrigin + Tracing . _msToSec ( entry [ `${ event } End` ] ) ;
500
518
}
501
519
502
520
// tslint:disable-next-line: completed-docs
503
521
function addRequest ( parent : Span , entry : { [ key : string ] : number } ) : void {
504
- const request = parent . startChild ( {
522
+ parent . startChild ( {
505
523
description : 'request' ,
524
+ endTimestamp : timeOrigin + Tracing . _msToSec ( entry . responseEnd ) ,
506
525
op : 'browser' ,
526
+ startTimestamp : timeOrigin + Tracing . _msToSec ( entry . requestStart ) ,
507
527
} ) ;
508
- request . startTimestamp = timeOrigin + Tracing . _msToSec ( entry . requestStart ) ;
509
- request . endTimestamp = timeOrigin + Tracing . _msToSec ( entry . responseEnd ) ;
510
528
511
- const response = parent . startChild ( {
529
+ parent . startChild ( {
512
530
description : 'response' ,
531
+ endTimestamp : timeOrigin + Tracing . _msToSec ( entry . responseEnd ) ,
513
532
op : 'browser' ,
533
+ startTimestamp : timeOrigin + Tracing . _msToSec ( entry . responseStart ) ,
514
534
} ) ;
515
- response . startTimestamp = timeOrigin + Tracing . _msToSec ( entry . responseStart ) ;
516
- response . endTimestamp = timeOrigin + Tracing . _msToSec ( entry . responseEnd ) ;
517
535
}
518
536
519
537
let entryScriptSrc : string | undefined ;
@@ -599,16 +617,15 @@ export class Tracing implements Integration {
599
617
} ) ;
600
618
601
619
if ( entryScriptStartEndTime !== undefined && tracingInitMarkStartTime !== undefined ) {
602
- const evaluation = transactionSpan . startChild ( {
620
+ transactionSpan . startChild ( {
603
621
description : 'evaluation' ,
622
+ endTimestamp : tracingInitMarkStartTime ,
604
623
op : `script` ,
624
+ startTimestamp : entryScriptStartEndTime ,
605
625
} ) ;
606
- evaluation . startTimestamp = entryScriptStartEndTime ;
607
- evaluation . endTimestamp = tracingInitMarkStartTime ;
608
626
}
609
627
610
628
Tracing . _performanceCursor = Math . max ( performance . getEntries ( ) . length - 1 , 0 ) ;
611
-
612
629
// tslint:enable: no-unsafe-any
613
630
}
614
631
@@ -756,9 +773,11 @@ export class Tracing implements Integration {
756
773
if ( count === 0 && Tracing . _activeTransaction ) {
757
774
const timeout = Tracing . options && Tracing . options . idleTimeout ;
758
775
Tracing . _log ( `[Tracing] Flushing Transaction in ${ timeout } ms` ) ;
759
- Tracing . _idleTransactionEndTimestamp = timestampWithMs ( ) ;
776
+ // We need to add the timeout here to have the real endtimestamp of the transaction
777
+ // Remeber timestampWithMs is in seconds, timeout is in ms
778
+ const end = timestampWithMs ( ) + timeout / 1000 ;
760
779
setTimeout ( ( ) => {
761
- Tracing . finishIdleTransaction ( ) ;
780
+ Tracing . finishIdleTransaction ( end ) ;
762
781
} , timeout ) ;
763
782
}
764
783
}
@@ -871,6 +890,7 @@ function fetchCallback(handlerData: { [key: string]: any }): void {
871
890
*/
872
891
function historyCallback ( _ : { [ key : string ] : any } ) : void {
873
892
if ( Tracing . options . startTransactionOnLocationChange && global && global . location ) {
893
+ Tracing . finishIdleTransaction ( timestampWithMs ( ) ) ;
874
894
Tracing . startIdleTransaction ( {
875
895
name : global . location . href ,
876
896
op : 'navigation' ,
0 commit comments