Skip to content

Commit 60c1081

Browse files
authored
ref(replay): Improve status logging (#8709)
This streamlines & improves logging for replay status, adding more logs for certain things to be better able to see what's going on, and adapting the `traceInternals` setting to result in console breadcrumbs being added (which should show up both in replay and for e.g. sentry errors etc). While at it I also fixed some `any` eslint warnings in replay. ref: #8400
1 parent 85effab commit 60c1081

12 files changed

+106
-57
lines changed

packages/replay/jest.setup.ts

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -129,21 +129,24 @@ function checkCallForSentReplay(
129129
}
130130

131131
/**
132-
* Only want calls that send replay events, i.e. ignore error events
133-
*/
132+
* Only want calls that send replay events, i.e. ignore error events
133+
*/
134+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
134135
function getReplayCalls(calls: any[][][]): any[][][] {
135-
return calls.map(call => {
136-
const arg = call[0];
136+
return calls
137+
.map(call => {
138+
const arg = call[0];
137139
if (arg.length !== 2) {
138140
return [];
139141
}
140142

141-
if (!arg[1][0].find(({type}: {type: string}) => ['replay_event', 'replay_recording'].includes(type))) {
143+
if (!arg[1][0].find(({ type }: { type: string }) => ['replay_event', 'replay_recording'].includes(type))) {
142144
return [];
143145
}
144146

145-
return [ arg ];
146-
}).filter(Boolean);
147+
return [arg];
148+
})
149+
.filter(Boolean);
147150
}
148151

149152
/**
@@ -159,9 +162,11 @@ const toHaveSentReplay = function (
159162

160163
let result: CheckCallForSentReplayResult;
161164

162-
const expectedKeysLength = expected ? ('sample' in expected ? Object.keys(expected.sample) : Object.keys(expected)).length : 0;
165+
const expectedKeysLength = expected
166+
? ('sample' in expected ? Object.keys(expected.sample) : Object.keys(expected)).length
167+
: 0;
163168

164-
const replayCalls = getReplayCalls(calls)
169+
const replayCalls = getReplayCalls(calls);
165170

166171
for (const currentCall of replayCalls) {
167172
result = checkCallForSentReplay.call(this, currentCall[0], expected);
@@ -213,7 +218,7 @@ const toHaveLastSentReplay = function (
213218
expected?: SentReplayExpected | { sample: SentReplayExpected; inverse: boolean },
214219
) {
215220
const { calls } = (getCurrentHub().getClient()?.getTransport()?.send as MockTransport).mock;
216-
const replayCalls = getReplayCalls(calls)
221+
const replayCalls = getReplayCalls(calls);
217222

218223
const lastCall = replayCalls[calls.length - 1]?.[0];
219224

packages/replay/src/eventBuffer/EventBufferProxy.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { ReplayRecordingData } from '@sentry/types';
22
import { logger } from '@sentry/utils';
33

44
import type { AddEventResult, EventBuffer, EventBufferType, RecordingEvent } from '../types';
5+
import { logInfo } from '../util/log';
56
import { EventBufferArray } from './EventBufferArray';
67
import { EventBufferCompressionWorker } from './EventBufferCompressionWorker';
78

@@ -79,7 +80,7 @@ export class EventBufferProxy implements EventBuffer {
7980
} catch (error) {
8081
// If the worker fails to load, we fall back to the simple buffer.
8182
// Nothing more to do from our side here
82-
__DEBUG_BUILD__ && logger.log('[Replay] Failed to load the compression worker, falling back to simple buffer');
83+
logInfo('[Replay] Failed to load the compression worker, falling back to simple buffer');
8384
return;
8485
}
8586

packages/replay/src/eventBuffer/WorkerHandler.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { logger } from '@sentry/utils';
22

33
import type { WorkerRequest, WorkerResponse } from '../types';
4+
import { logInfo } from '../util/log';
45

56
/**
67
* Event buffer that uses a web worker to compress events.
@@ -55,7 +56,7 @@ export class WorkerHandler {
5556
* Destroy the worker.
5657
*/
5758
public destroy(): void {
58-
__DEBUG_BUILD__ && logger.log('[Replay] Destroying compression worker');
59+
logInfo('[Replay] Destroying compression worker');
5960
this._worker.terminate();
6061
}
6162

packages/replay/src/eventBuffer/index.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { getWorkerURL } from '@sentry-internal/replay-worker';
2-
import { logger } from '@sentry/utils';
32

43
import type { EventBuffer } from '../types';
4+
import { logInfo } from '../util/log';
55
import { EventBufferArray } from './EventBufferArray';
66
import { EventBufferProxy } from './EventBufferProxy';
77

@@ -18,15 +18,15 @@ export function createEventBuffer({ useCompression }: CreateEventBufferParams):
1818
try {
1919
const workerUrl = getWorkerURL();
2020

21-
__DEBUG_BUILD__ && logger.log('[Replay] Using compression worker');
21+
logInfo('[Replay] Using compression worker');
2222
const worker = new Worker(workerUrl);
2323
return new EventBufferProxy(worker);
2424
} catch (error) {
25-
__DEBUG_BUILD__ && logger.log('[Replay] Failed to create compression worker');
25+
logInfo('[Replay] Failed to create compression worker');
2626
// Fall back to use simple event buffer array
2727
}
2828
}
2929

30-
__DEBUG_BUILD__ && logger.log('[Replay] Using simple buffer');
30+
logInfo('[Replay] Using simple buffer');
3131
return new EventBufferArray();
3232
}

packages/replay/src/replay.ts

Lines changed: 31 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ import { debounce } from './util/debounce';
4747
import { getHandleRecordingEmit } from './util/handleRecordingEmit';
4848
import { isExpired } from './util/isExpired';
4949
import { isSessionExpired } from './util/isSessionExpired';
50+
import { logInfo } from './util/log';
5051
import { sendReplay } from './util/sendReplay';
5152
import type { SKIPPED } from './util/throttle';
5253
import { throttle, THROTTLED } from './util/throttle';
@@ -249,6 +250,8 @@ export class ReplayContainer implements ReplayContainerInterface {
249250
this.recordingMode = 'buffer';
250251
}
251252

253+
logInfo(`[Replay] Starting replay in ${this.recordingMode} mode`, this._options._experiments.traceInternals);
254+
252255
this._initializeRecording();
253256
}
254257

@@ -268,6 +271,8 @@ export class ReplayContainer implements ReplayContainerInterface {
268271
throw new Error('Replay buffering is in progress, call `flush()` to save the replay');
269272
}
270273

274+
logInfo('[Replay] Starting replay in session mode', this._options._experiments.traceInternals);
275+
271276
const previousSessionId = this.session && this.session.id;
272277

273278
const { session } = getSession({
@@ -277,6 +282,7 @@ export class ReplayContainer implements ReplayContainerInterface {
277282
// This is intentional: create a new session-based replay when calling `start()`
278283
sessionSampleRate: 1,
279284
allowBuffering: false,
285+
traceInternals: this._options._experiments.traceInternals,
280286
});
281287

282288
session.previousSessionId = previousSessionId;
@@ -294,6 +300,8 @@ export class ReplayContainer implements ReplayContainerInterface {
294300
throw new Error('Replay recording is already in progress');
295301
}
296302

303+
logInfo('[Replay] Starting replay in buffer mode', this._options._experiments.traceInternals);
304+
297305
const previousSessionId = this.session && this.session.id;
298306

299307
const { session } = getSession({
@@ -302,6 +310,7 @@ export class ReplayContainer implements ReplayContainerInterface {
302310
currentSession: this.session,
303311
sessionSampleRate: 0,
304312
allowBuffering: true,
313+
traceInternals: this._options._experiments.traceInternals,
305314
});
306315

307316
session.previousSessionId = previousSessionId;
@@ -362,15 +371,10 @@ export class ReplayContainer implements ReplayContainerInterface {
362371
}
363372

364373
try {
365-
if (__DEBUG_BUILD__) {
366-
const msg = `[Replay] Stopping Replay${reason ? ` triggered by ${reason}` : ''}`;
367-
368-
// When `traceInternals` is enabled, we want to log this to the console
369-
// Else, use the regular debug output
370-
// eslint-disable-next-line
371-
const log = this.getOptions()._experiments.traceInternals ? console.warn : logger.log;
372-
log(msg);
373-
}
374+
logInfo(
375+
`[Replay] Stopping Replay${reason ? ` triggered by ${reason}` : ''}`,
376+
this._options._experiments.traceInternals,
377+
);
374378

375379
// We can't move `_isEnabled` after awaiting a flush, otherwise we can
376380
// enter into an infinite loop when `stop()` is called while flushing.
@@ -403,8 +407,14 @@ export class ReplayContainer implements ReplayContainerInterface {
403407
* not as thorough of a shutdown as `stop()`.
404408
*/
405409
public pause(): void {
410+
if (this._isPaused) {
411+
return;
412+
}
413+
406414
this._isPaused = true;
407415
this.stopRecording();
416+
417+
logInfo('[Replay] Pausing replay', this._options._experiments.traceInternals);
408418
}
409419

410420
/**
@@ -414,12 +424,14 @@ export class ReplayContainer implements ReplayContainerInterface {
414424
* new DOM checkout.`
415425
*/
416426
public resume(): void {
417-
if (!this._loadAndCheckSession()) {
427+
if (!this._isPaused || !this._loadAndCheckSession()) {
418428
return;
419429
}
420430

421431
this._isPaused = false;
422432
this.startRecording();
433+
434+
logInfo('[Replay] Resuming replay', this._options._experiments.traceInternals);
423435
}
424436

425437
/**
@@ -436,9 +448,7 @@ export class ReplayContainer implements ReplayContainerInterface {
436448

437449
const activityTime = Date.now();
438450

439-
// eslint-disable-next-line no-console
440-
const log = this.getOptions()._experiments.traceInternals ? console.info : logger.info;
441-
__DEBUG_BUILD__ && log(`[Replay] Converting buffer to session, starting at ${activityTime}`);
451+
logInfo('[Replay] Converting buffer to session', this._options._experiments.traceInternals);
442452

443453
// Allow flush to complete before resuming as a session recording, otherwise
444454
// the checkout from `startRecording` may be included in the payload.
@@ -746,6 +756,7 @@ export class ReplayContainer implements ReplayContainerInterface {
746756
currentSession: this.session,
747757
sessionSampleRate: this._options.sessionSampleRate,
748758
allowBuffering: this._options.errorSampleRate > 0 || this.recordingMode === 'buffer',
759+
traceInternals: this._options._experiments.traceInternals,
749760
});
750761

751762
// If session was newly created (i.e. was not loaded from storage), then
@@ -762,7 +773,7 @@ export class ReplayContainer implements ReplayContainerInterface {
762773
this.session = session;
763774

764775
if (!this.session.sampled) {
765-
void this.stop('session unsampled');
776+
void this.stop('session not refreshed');
766777
return false;
767778
}
768779

@@ -904,7 +915,7 @@ export class ReplayContainer implements ReplayContainerInterface {
904915
// If the user has come back to the page within SESSION_IDLE_PAUSE_DURATION
905916
// ms, we will re-use the existing session, otherwise create a new
906917
// session
907-
__DEBUG_BUILD__ && logger.log('[Replay] Document has become active, but session has expired');
918+
logInfo('[Replay] Document has become active, but session has expired');
908919
return;
909920
}
910921

@@ -919,7 +930,7 @@ export class ReplayContainer implements ReplayContainerInterface {
919930
*/
920931
private _triggerFullSnapshot(checkout = true): void {
921932
try {
922-
__DEBUG_BUILD__ && logger.log('[Replay] Taking full rrweb snapshot');
933+
logInfo('[Replay] Taking full rrweb snapshot');
923934
record.takeFullSnapshot(checkout);
924935
} catch (err) {
925936
this._handleException(err);
@@ -1121,13 +1132,10 @@ export class ReplayContainer implements ReplayContainerInterface {
11211132
// If session is too short, or too long (allow some wiggle room over maxSessionLife), do not send it
11221133
// This _should_ not happen, but it may happen if flush is triggered due to a page activity change or similar
11231134
if (duration < this._options.minReplayDuration || duration > this.timeouts.maxSessionLife + 5_000) {
1124-
// eslint-disable-next-line no-console
1125-
const log = this.getOptions()._experiments.traceInternals ? console.warn : logger.warn;
1126-
__DEBUG_BUILD__ &&
1127-
log(
1128-
`[Replay] Session duration (${Math.floor(duration / 1000)}s) is too short or too long, not sending replay.`,
1129-
);
1130-
1135+
logInfo(
1136+
`[Replay] Session duration (${Math.floor(duration / 1000)}s) is too short or too long, not sending replay.`,
1137+
this._options._experiments.traceInternals,
1138+
);
11311139
return;
11321140
}
11331141

packages/replay/src/session/createSession.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import { logger } from '@sentry/utils';
2-
31
import type { Sampled, Session, SessionOptions } from '../types';
42
import { isSampled } from '../util/isSampled';
53
import { saveSession } from './saveSession';
@@ -23,8 +21,6 @@ export function createSession({ sessionSampleRate, allowBuffering, stickySession
2321
sampled,
2422
});
2523

26-
__DEBUG_BUILD__ && logger.log(`[Replay] Creating new session: ${session.id}`);
27-
2824
if (stickySession) {
2925
saveSession(session);
3026
}

packages/replay/src/session/fetchSession.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import { REPLAY_SESSION_KEY, WINDOW } from '../constants';
22
import type { Session } from '../types';
33
import { hasSessionStorage } from '../util/hasSessionStorage';
4+
import { logInfo } from '../util/log';
45
import { makeSession } from './Session';
56

67
/**
78
* Fetches a session from storage
89
*/
9-
export function fetchSession(): Session | null {
10+
export function fetchSession(traceInternals?: boolean): Session | null {
1011
if (!hasSessionStorage()) {
1112
return null;
1213
}
@@ -21,6 +22,8 @@ export function fetchSession(): Session | null {
2122

2223
const sessionObj = JSON.parse(sessionStringFromStorage) as Session;
2324

25+
logInfo('[Replay] Loading existing session', traceInternals);
26+
2427
return makeSession(sessionObj);
2528
} catch {
2629
return null;

packages/replay/src/session/getSession.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
import { logger } from '@sentry/utils';
2-
31
import type { Session, SessionOptions, Timeouts } from '../types';
42
import { isSessionExpired } from '../util/isSessionExpired';
3+
import { logInfo } from '../util/log';
54
import { createSession } from './createSession';
65
import { fetchSession } from './fetchSession';
76
import { makeSession } from './Session';
@@ -13,6 +12,8 @@ interface GetSessionParams extends SessionOptions {
1312
* The current session (e.g. if stickySession is off)
1413
*/
1514
currentSession?: Session;
15+
16+
traceInternals?: boolean;
1617
}
1718

1819
/**
@@ -24,9 +25,10 @@ export function getSession({
2425
stickySession,
2526
sessionSampleRate,
2627
allowBuffering,
28+
traceInternals,
2729
}: GetSessionParams): { type: 'new' | 'saved'; session: Session } {
2830
// If session exists and is passed, use it instead of always hitting session storage
29-
const session = currentSession || (stickySession && fetchSession());
31+
const session = currentSession || (stickySession && fetchSession(traceInternals));
3032

3133
if (session) {
3234
// If there is a session, check if it is valid (e.g. "last activity" time
@@ -42,9 +44,10 @@ export function getSession({
4244
// and when this session is expired, it will not be renewed until user
4345
// reloads.
4446
const discardedSession = makeSession({ sampled: false });
47+
logInfo('[Replay] Session should not be refreshed', traceInternals);
4548
return { type: 'new', session: discardedSession };
4649
} else {
47-
__DEBUG_BUILD__ && logger.log('[Replay] Session has expired');
50+
logInfo('[Replay] Session has expired', traceInternals);
4851
}
4952
// Otherwise continue to create a new session
5053
}
@@ -54,6 +57,7 @@ export function getSession({
5457
sessionSampleRate,
5558
allowBuffering,
5659
});
60+
logInfo('[Replay] Created new session', traceInternals);
5761

5862
return { type: 'new', session: newSession };
5963
}

0 commit comments

Comments
 (0)