1
1
/* eslint-disable max-lines */ // TODO: We might want to split this file up
2
- import { addGlobalEventProcessor , captureException , getCurrentHub } from '@sentry/core' ;
2
+ import { captureException } from '@sentry/core' ;
3
3
import type { Breadcrumb , ReplayRecordingMode } from '@sentry/types' ;
4
4
import type { RateLimits } from '@sentry/utils' ;
5
- import { addInstrumentationHandler , disabledUntil , logger } from '@sentry/utils' ;
5
+ import { disabledUntil , logger } from '@sentry/utils' ;
6
6
import { EventType , record } from 'rrweb' ;
7
7
8
8
import { MAX_SESSION_LIFE , SESSION_IDLE_DURATION , VISIBILITY_CHANGE_TIMEOUT , WINDOW } from './constants' ;
9
- import { breadcrumbHandler } from './coreHandlers/breadcrumbHandler' ;
10
- import { handleFetchSpanListener } from './coreHandlers/handleFetch' ;
11
- import { handleGlobalEventListener } from './coreHandlers/handleGlobalEvent' ;
12
- import { handleHistorySpanListener } from './coreHandlers/handleHistory' ;
13
- import { handleXhrSpanListener } from './coreHandlers/handleXhr' ;
14
9
import { setupPerformanceObserver } from './coreHandlers/performanceObserver' ;
15
10
import { createEventBuffer } from './eventBuffer' ;
16
11
import { getSession } from './session/getSession' ;
@@ -20,7 +15,6 @@ import type {
20
15
AddUpdateCallback ,
21
16
AllPerformanceEntry ,
22
17
EventBuffer ,
23
- InstrumentationTypeBreadcrumb ,
24
18
InternalEventContext ,
25
19
PopEventContext ,
26
20
RecordingEvent ,
@@ -30,6 +24,7 @@ import type {
30
24
Session ,
31
25
} from './types' ;
32
26
import { addEvent } from './util/addEvent' ;
27
+ import { addGlobalListeners } from './util/addGlobalListeners' ;
33
28
import { addMemoryEntry } from './util/addMemoryEntry' ;
34
29
import { createBreadcrumb } from './util/createBreadcrumb' ;
35
30
import { createPerformanceEntries } from './util/createPerformanceEntries' ;
@@ -318,7 +313,7 @@ export class ReplayContainer implements ReplayContainerInterface {
318
313
}
319
314
320
315
// Otherwise... recording was never suspended, continue as normalish
321
- this . _checkAndHandleExpiredSession ( ) ;
316
+ this . checkAndHandleExpiredSession ( ) ;
322
317
323
318
this . _updateSessionActivity ( ) ;
324
319
}
@@ -340,6 +335,44 @@ export class ReplayContainer implements ReplayContainerInterface {
340
335
return this . session && this . session . id ;
341
336
}
342
337
338
+ /**
339
+ * Checks if recording should be stopped due to user inactivity. Otherwise
340
+ * check if session is expired and create a new session if so. Triggers a new
341
+ * full snapshot on new session.
342
+ *
343
+ * Returns true if session is not expired, false otherwise.
344
+ * @hidden
345
+ */
346
+ public checkAndHandleExpiredSession ( { expiry = SESSION_IDLE_DURATION } : { expiry ?: number } = { } ) : boolean | void {
347
+ const oldSessionId = this . getSessionId ( ) ;
348
+
349
+ // Prevent starting a new session if the last user activity is older than
350
+ // MAX_SESSION_LIFE. Otherwise non-user activity can trigger a new
351
+ // session+recording. This creates noisy replays that do not have much
352
+ // content in them.
353
+ if ( this . _lastActivity && isExpired ( this . _lastActivity , MAX_SESSION_LIFE ) ) {
354
+ // Pause recording
355
+ this . pause ( ) ;
356
+ return ;
357
+ }
358
+
359
+ // --- There is recent user activity --- //
360
+ // This will create a new session if expired, based on expiry length
361
+ this . _loadSession ( { expiry } ) ;
362
+
363
+ // Session was expired if session ids do not match
364
+ const expired = oldSessionId !== this . getSessionId ( ) ;
365
+
366
+ if ( ! expired ) {
367
+ return true ;
368
+ }
369
+
370
+ // Session is expired, trigger a full snapshot (which will create a new session)
371
+ this . _triggerFullSnapshot ( ) ;
372
+
373
+ return false ;
374
+ }
375
+
343
376
/** A wrapper to conditionally capture exceptions. */
344
377
private _handleException ( error : unknown ) : void {
345
378
__DEBUG_BUILD__ && logger . error ( '[Replay]' , error ) ;
@@ -409,19 +442,7 @@ export class ReplayContainer implements ReplayContainerInterface {
409
442
410
443
// There is no way to remove these listeners, so ensure they are only added once
411
444
if ( ! this . _hasInitializedCoreListeners ) {
412
- // Listeners from core SDK //
413
- const scope = getCurrentHub ( ) . getScope ( ) ;
414
- if ( scope ) {
415
- scope . addScopeListener ( this . _handleCoreBreadcrumbListener ( 'scope' ) ) ;
416
- }
417
- addInstrumentationHandler ( 'dom' , this . _handleCoreBreadcrumbListener ( 'dom' ) ) ;
418
- addInstrumentationHandler ( 'fetch' , handleFetchSpanListener ( this ) ) ;
419
- addInstrumentationHandler ( 'xhr' , handleXhrSpanListener ( this ) ) ;
420
- addInstrumentationHandler ( 'history' , handleHistorySpanListener ( this ) ) ;
421
-
422
- // Tag all (non replay) events that get sent to Sentry with the current
423
- // replay ID so that we can reference them later in the UI
424
- addGlobalEventProcessor ( handleGlobalEventListener ( this ) ) ;
445
+ addGlobalListeners ( this ) ;
425
446
426
447
this . _hasInitializedCoreListeners = true ;
427
448
}
@@ -468,7 +489,7 @@ export class ReplayContainer implements ReplayContainerInterface {
468
489
isCheckout ?: boolean ,
469
490
) => {
470
491
// If this is false, it means session is expired, create and a new session and wait for checkout
471
- if ( ! this . _checkAndHandleExpiredSession ( ) ) {
492
+ if ( ! this . checkAndHandleExpiredSession ( ) ) {
472
493
__DEBUG_BUILD__ && logger . error ( '[Replay] Received replay event after session expired.' ) ;
473
494
474
495
return ;
@@ -563,51 +584,6 @@ export class ReplayContainer implements ReplayContainerInterface {
563
584
this . _doChangeToForegroundTasks ( breadcrumb ) ;
564
585
} ;
565
586
566
- /**
567
- * Handler for Sentry Core SDK events.
568
- *
569
- * These events will create breadcrumb-like objects in the recording.
570
- */
571
- private _handleCoreBreadcrumbListener : ( type : InstrumentationTypeBreadcrumb ) => ( handlerData : unknown ) => void =
572
- ( type : InstrumentationTypeBreadcrumb ) =>
573
- ( handlerData : unknown ) : void => {
574
- if ( ! this . _isEnabled ) {
575
- return ;
576
- }
577
-
578
- const result = breadcrumbHandler ( type , handlerData ) ;
579
-
580
- if ( result === null ) {
581
- return ;
582
- }
583
-
584
- if ( result . category === 'sentry.transaction' ) {
585
- return ;
586
- }
587
-
588
- if ( result . category === 'ui.click' ) {
589
- this . triggerUserActivity ( ) ;
590
- } else {
591
- this . _checkAndHandleExpiredSession ( ) ;
592
- }
593
-
594
- this . addUpdate ( ( ) => {
595
- void addEvent ( this , {
596
- type : EventType . Custom ,
597
- // TODO: We were converting from ms to seconds for breadcrumbs, spans,
598
- // but maybe we should just keep them as milliseconds
599
- timestamp : ( result . timestamp || 0 ) * 1000 ,
600
- data : {
601
- tag : 'breadcrumb' ,
602
- payload : result ,
603
- } ,
604
- } ) ;
605
-
606
- // Do not flush after console log messages
607
- return result . category === 'console' ;
608
- } ) ;
609
- } ;
610
-
611
587
/**
612
588
* Tasks to run when we consider a page to be hidden (via blurring and/or visibility)
613
589
*/
@@ -636,7 +612,7 @@ export class ReplayContainer implements ReplayContainerInterface {
636
612
return ;
637
613
}
638
614
639
- const isSessionActive = this . _checkAndHandleExpiredSession ( {
615
+ const isSessionActive = this . checkAndHandleExpiredSession ( {
640
616
expiry : VISIBILITY_CHANGE_TIMEOUT ,
641
617
} ) ;
642
618
@@ -707,43 +683,6 @@ export class ReplayContainer implements ReplayContainerInterface {
707
683
return Promise . all ( createPerformanceSpans ( this , createPerformanceEntries ( entries ) ) ) ;
708
684
}
709
685
710
- /**
711
- * Checks if recording should be stopped due to user inactivity. Otherwise
712
- * check if session is expired and create a new session if so. Triggers a new
713
- * full snapshot on new session.
714
- *
715
- * Returns true if session is not expired, false otherwise.
716
- */
717
- private _checkAndHandleExpiredSession ( { expiry = SESSION_IDLE_DURATION } : { expiry ?: number } = { } ) : boolean | void {
718
- const oldSessionId = this . getSessionId ( ) ;
719
-
720
- // Prevent starting a new session if the last user activity is older than
721
- // MAX_SESSION_LIFE. Otherwise non-user activity can trigger a new
722
- // session+recording. This creates noisy replays that do not have much
723
- // content in them.
724
- if ( this . _lastActivity && isExpired ( this . _lastActivity , MAX_SESSION_LIFE ) ) {
725
- // Pause recording
726
- this . pause ( ) ;
727
- return ;
728
- }
729
-
730
- // --- There is recent user activity --- //
731
- // This will create a new session if expired, based on expiry length
732
- this . _loadSession ( { expiry } ) ;
733
-
734
- // Session was expired if session ids do not match
735
- const expired = oldSessionId !== this . getSessionId ( ) ;
736
-
737
- if ( ! expired ) {
738
- return true ;
739
- }
740
-
741
- // Session is expired, trigger a full snapshot (which will create a new session)
742
- this . _triggerFullSnapshot ( ) ;
743
-
744
- return false ;
745
- }
746
-
747
686
/**
748
687
* Only flush if `this.recordingMode === 'session'`
749
688
*/
@@ -862,7 +801,7 @@ export class ReplayContainer implements ReplayContainerInterface {
862
801
return ;
863
802
}
864
803
865
- if ( ! this . _checkAndHandleExpiredSession ( ) ) {
804
+ if ( ! this . checkAndHandleExpiredSession ( ) ) {
866
805
__DEBUG_BUILD__ && logger . error ( '[Replay] Attempting to finish replay event after session expired.' ) ;
867
806
return ;
868
807
}
0 commit comments