Skip to content

Commit 497a3c7

Browse files
authored
fix(replay): Replace _waitForError with recordingMode (#6489)
1 parent 8342523 commit 497a3c7

File tree

4 files changed

+25
-23
lines changed

4 files changed

+25
-23
lines changed

packages/replay/src/coreHandlers/handleGlobalEvent.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,11 @@ export function handleGlobalEventListener(replay: ReplayContainer): (event: Even
2222

2323
// Only tag transactions with replayId if not waiting for an error
2424
// @ts-ignore private
25-
if (event.type !== 'transaction' || !replay._waitForError) {
25+
if (event.type !== 'transaction' || replay.recordingMode === 'session') {
2626
event.tags = { ...event.tags, replayId: replay.session?.id };
2727
}
2828

29-
// Collect traceIds in _context regardless of `_waitForError` - if it's true,
29+
// Collect traceIds in _context regardless of `recordingMode` - if it's true,
3030
// _context gets cleared on every checkout
3131
if (event.type === 'transaction' && event.contexts && event.contexts.trace && event.contexts.trace.trace_id) {
3232
replay.getContext().traceIds.add(event.contexts.trace.trace_id as string);
@@ -47,8 +47,7 @@ export function handleGlobalEventListener(replay: ReplayContainer): (event: Even
4747

4848
// Need to be very careful that this does not cause an infinite loop
4949
if (
50-
// @ts-ignore private
51-
replay._waitForError &&
50+
replay.recordingMode === 'error' &&
5251
event.exception &&
5352
event.message !== UNABLE_TO_SEND_REPLAY // ignore this error because otherwise we could loop indefinitely with trying to capture replay and failing
5453
) {
@@ -62,8 +61,7 @@ export function handleGlobalEventListener(replay: ReplayContainer): (event: Even
6261
if (replay.stopRecording()) {
6362
// Reset all "capture on error" configuration before
6463
// starting a new recording
65-
// @ts-ignore private
66-
replay._waitForError = false;
64+
replay.recordingMode = 'session';
6765
replay.startRecording();
6866
}
6967
});

packages/replay/src/replay.ts

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import type {
3535
RecordingOptions,
3636
ReplayContainer as ReplayContainerInterface,
3737
ReplayPluginOptions,
38+
ReplayRecordingMode,
3839
SendReplay,
3940
Session,
4041
} from './types';
@@ -64,6 +65,13 @@ export class ReplayContainer implements ReplayContainerInterface {
6465

6566
public session: Session | undefined;
6667

68+
/**
69+
* Recording can happen in one of two modes:
70+
* * session: Record the whole session, sending it continuously
71+
* * error: Always keep the last 60s of recording, and when an error occurs, send it immediately
72+
*/
73+
public recordingMode: ReplayRecordingMode = 'session';
74+
6775
/**
6876
* Options to pass to `rrweb.record()`
6977
*/
@@ -96,12 +104,6 @@ export class ReplayContainer implements ReplayContainerInterface {
96104
*/
97105
private _isPaused: boolean = false;
98106

99-
/**
100-
* Integration will wait until an error occurs before creating and sending a
101-
* replay.
102-
*/
103-
private _waitForError: boolean = false;
104-
105107
/**
106108
* Have we attached listeners to the core SDK?
107109
* Note we have to track this as there is no way to remove instrumentation handlers.
@@ -173,7 +175,7 @@ export class ReplayContainer implements ReplayContainerInterface {
173175
// when an error will occur, so we need to keep a buffer of
174176
// replay events.
175177
if (this.session.sampled === 'error') {
176-
this._waitForError = true;
178+
this.recordingMode = 'error';
177179
}
178180

179181
// setup() is generally called on page load or manually - in both cases we
@@ -203,7 +205,7 @@ export class ReplayContainer implements ReplayContainerInterface {
203205
// When running in error sampling mode, we need to overwrite `checkoutEveryNth`
204206
// Without this, it would record forever, until an error happens, which we don't want
205207
// instead, we'll always keep the last 60 seconds of replay before an error happened
206-
...(this._waitForError && { checkoutEveryNth: 60000 }),
208+
...(this.recordingMode === 'error' && { checkoutEveryNth: 60000 }),
207209
emit: this.handleRecordingEmit,
208210
});
209211
} catch (err) {
@@ -403,12 +405,12 @@ export class ReplayContainer implements ReplayContainerInterface {
403405
* processing and hand back control to caller.
404406
*/
405407
addUpdate(cb: AddUpdateCallback): void {
406-
// We need to always run `cb` (e.g. in the case of `this._waitForError == true`)
408+
// We need to always run `cb` (e.g. in the case of `this.recordingMode == 'error'`)
407409
const cbResult = cb?.();
408410

409411
// If this option is turned on then we will only want to call `flush`
410412
// explicitly
411-
if (this._waitForError) {
413+
if (this.recordingMode === 'error') {
412414
return;
413415
}
414416

@@ -445,7 +447,7 @@ export class ReplayContainer implements ReplayContainerInterface {
445447
// when an error occurs. Clear any state that happens before this current
446448
// checkout. This needs to happen before `addEvent()` which updates state
447449
// dependent on this reset.
448-
if (this._waitForError && event.type === 2) {
450+
if (this.recordingMode === 'error' && event.type === 2) {
449451
this.setInitialState();
450452
}
451453

@@ -471,7 +473,7 @@ export class ReplayContainer implements ReplayContainerInterface {
471473

472474
// See note above re: session start needs to reflect the most recent
473475
// checkout.
474-
if (this._waitForError && this.session && this._context.earliestEvent) {
476+
if (this.recordingMode === 'error' && this.session && this._context.earliestEvent) {
475477
this.session.started = this._context.earliestEvent;
476478
this._maybeSaveSession();
477479
}
@@ -744,10 +746,10 @@ export class ReplayContainer implements ReplayContainerInterface {
744746
}
745747

746748
/**
747-
* Only flush if `this._waitForError` is false.
749+
* Only flush if `this.recordingMode === 'session'`
748750
*/
749751
conditionalFlush(): void {
750-
if (this._waitForError) {
752+
if (this.recordingMode === 'error') {
751753
return;
752754
}
753755

packages/replay/src/types.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ export type RecordedEvents = Uint8Array | string;
77

88
export type AllPerformanceEntry = PerformancePaintTiming | PerformanceResourceTiming | PerformanceNavigationTiming;
99

10+
export type ReplayRecordingMode = 'session' | 'error';
11+
1012
export interface SendReplay {
1113
events: RecordedEvents;
1214
replayId: string;
@@ -215,6 +217,7 @@ export interface ReplayContainer {
215217
eventBuffer: EventBuffer | null;
216218
performanceEvents: AllPerformanceEntry[];
217219
session: Session | undefined;
220+
recordingMode: ReplayRecordingMode;
218221
isEnabled(): boolean;
219222
isPaused(): boolean;
220223
getContext(): InternalEventContext;

packages/replay/test/unit/index-handleGlobalEvent.test.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,8 @@ it('only tags errors with replay id, adds trace and error id to context for erro
8383
jest.runAllTimers();
8484
await new Promise(process.nextTick); // wait for flush
8585

86-
// Turns off `_waitForError` mode
87-
// @ts-ignore private
88-
expect(replay._waitForError).toBe(false);
86+
// Rerverts `recordingMode` to session
87+
expect(replay.recordingMode).toBe('session');
8988
});
9089

9190
it('strips out dropped events from errorIds', async () => {

0 commit comments

Comments
 (0)