Skip to content

Commit 93feea9

Browse files
committed
fix(replay): Ensure dropped errors are removed from replay reference
1 parent 7954cf3 commit 93feea9

File tree

2 files changed

+67
-1
lines changed

2 files changed

+67
-1
lines changed

packages/replay/src/index.ts

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/* eslint-disable max-lines */ // TODO: We might want to split this file up
22
import { addGlobalEventProcessor, getCurrentHub, Scope, setContext } from '@sentry/core';
3-
import { Breadcrumb, Client, Event, Integration } from '@sentry/types';
3+
import { Breadcrumb, Client, DataCategory, Event, EventDropReason, Integration } from '@sentry/types';
44
import { addInstrumentationHandler, createEnvelope, logger } from '@sentry/utils';
55
import debounce from 'lodash.debounce';
66
import { PerformanceObserverEntryList } from 'perf_hooks';
@@ -124,6 +124,11 @@ export class Replay implements Integration {
124124
*/
125125
private stopRecording: ReturnType<typeof record> | null = null;
126126

127+
/**
128+
* We overwrite `client.recordDroppedEvent`, but store the original method here so we can restore it.
129+
*/
130+
private _originalRecordDroppedEvent?: Client['recordDroppedEvent'];
131+
127132
private context: InternalEventContext = {
128133
errorIds: new Set(),
129134
traceIds: new Set(),
@@ -432,6 +437,8 @@ export class Replay implements Integration {
432437
// Tag all (non replay) events that get sent to Sentry with the current
433438
// replay ID so that we can reference them later in the UI
434439
addGlobalEventProcessor(this.handleGlobalEvent);
440+
// We need to filter out dropped events
441+
this._overwriteRecordDroppedEvent();
435442

436443
this.hasInitializedCoreListeners = true;
437444
}
@@ -481,6 +488,8 @@ export class Replay implements Integration {
481488
window.removeEventListener('blur', this.handleWindowBlur);
482489
window.removeEventListener('focus', this.handleWindowFocus);
483490

491+
this._restoreRecordDroppedEvent();
492+
484493
if (this.performanceObserver) {
485494
this.performanceObserver.disconnect();
486495
this.performanceObserver = null;
@@ -1342,4 +1351,39 @@ export class Replay implements Integration {
13421351
});
13431352
}
13441353
}
1354+
1355+
_overwriteRecordDroppedEvent(): void {
1356+
const client = getCurrentHub().getClient();
1357+
1358+
if (!client) {
1359+
return;
1360+
}
1361+
1362+
const _originalCallback = client.recordDroppedEvent.bind(client);
1363+
1364+
const recordDroppedEvent: Client['recordDroppedEvent'] = (
1365+
reason: EventDropReason,
1366+
category: DataCategory,
1367+
event?: Event,
1368+
): void => {
1369+
if (event && event.event_id) {
1370+
this.context.errorIds.delete(event.event_id);
1371+
}
1372+
1373+
return _originalCallback(reason, category, event);
1374+
};
1375+
1376+
client.recordDroppedEvent = recordDroppedEvent;
1377+
this._originalRecordDroppedEvent = _originalCallback;
1378+
}
1379+
1380+
_restoreRecordDroppedEvent(): void {
1381+
const client = getCurrentHub().getClient();
1382+
1383+
if (!client || !this._originalRecordDroppedEvent) {
1384+
return;
1385+
}
1386+
1387+
client.recordDroppedEvent = this._originalRecordDroppedEvent;
1388+
}
13451389
}

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { getCurrentHub } from '@sentry/core';
12
import { Error } from '@test/fixtures/error';
23
import { Transaction } from '@test/fixtures/transaction';
34
import { resetSdkMock } from '@test/mocks';
@@ -83,6 +84,27 @@ it('only tags errors with replay id, adds trace and error id to context for erro
8384
expect(replay.waitForError).toBe(false);
8485
});
8586

87+
it('strips out dropped events from errorIds', async () => {
88+
const error1 = Error({ event_id: 'err1' });
89+
const error2 = Error({ event_id: 'err2' });
90+
const error3 = Error({ event_id: 'err3' });
91+
92+
replay._overwriteRecordDroppedEvent();
93+
94+
const client = getCurrentHub().getClient()!;
95+
96+
replay.handleGlobalEvent(error1);
97+
replay.handleGlobalEvent(error2);
98+
replay.handleGlobalEvent(error3);
99+
100+
client.recordDroppedEvent('before_send', 'error', { event_id: 'err2' });
101+
102+
// @ts-ignore private
103+
expect(Array.from(replay.context.errorIds)).toEqual(['err1', 'err3']);
104+
105+
replay._restoreRecordDroppedEvent();
106+
});
107+
86108
it('tags errors and transactions with replay id for session samples', async () => {
87109
({ replay } = await resetSdkMock({
88110
sessionSampleRate: 1.0,

0 commit comments

Comments
 (0)