Skip to content

Commit 30619a0

Browse files
committed
fix(replay): Ensure dropped errors are removed from replay reference
1 parent b49082e commit 30619a0

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 } from '@sentry/utils';
55
import debounce from 'lodash.debounce';
66
import { PerformanceObserverEntryList } from 'perf_hooks';
@@ -125,6 +125,11 @@ export class Replay implements Integration {
125125
*/
126126
private stopRecording: ReturnType<typeof record> | null = null;
127127

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

437444
this.hasInitializedCoreListeners = true;
438445
}
@@ -482,6 +489,8 @@ export class Replay implements Integration {
482489
window.removeEventListener('blur', this.handleWindowBlur);
483490
window.removeEventListener('focus', this.handleWindowFocus);
484491

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

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)