Skip to content

Commit 94c68e0

Browse files
authored
ref(browser): Refactor browser integrations to use processEvent (#9022)
This refactors browser integrations to use the new `processEvent` hook on the Integrations interface. It also updates Replay to register it's event processor on the client, not globally. This is also needed to ensure the order is somewhat stable, as global processors are run separately from the client ones.
1 parent 27cf07f commit 94c68e0

File tree

5 files changed

+91
-90
lines changed

5 files changed

+91
-90
lines changed

packages/browser/src/integrations/dedupe.ts

Lines changed: 19 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { Event, EventProcessor, Exception, Hub, Integration, StackFrame } from '@sentry/types';
1+
import type { Event, Exception, Integration, StackFrame } from '@sentry/types';
22
import { logger } from '@sentry/utils';
33

44
/** Deduplication filter */
@@ -22,36 +22,30 @@ export class Dedupe implements Integration {
2222
this.name = Dedupe.id;
2323
}
2424

25+
/** @inheritDoc */
26+
public setupOnce(_addGlobaleventProcessor: unknown, _getCurrentHub: unknown): void {
27+
// noop
28+
}
29+
2530
/**
2631
* @inheritDoc
2732
*/
28-
public setupOnce(addGlobalEventProcessor: (callback: EventProcessor) => void, getCurrentHub: () => Hub): void {
29-
const eventProcessor: EventProcessor = currentEvent => {
30-
// We want to ignore any non-error type events, e.g. transactions or replays
31-
// These should never be deduped, and also not be compared against as _previousEvent.
32-
if (currentEvent.type) {
33-
return currentEvent;
34-
}
33+
public processEvent(currentEvent: Event): Event | null {
34+
// We want to ignore any non-error type events, e.g. transactions or replays
35+
// These should never be deduped, and also not be compared against as _previousEvent.
36+
if (currentEvent.type) {
37+
return currentEvent;
38+
}
3539

36-
const self = getCurrentHub().getIntegration(Dedupe);
37-
if (self) {
38-
// Juuust in case something goes wrong
39-
try {
40-
if (_shouldDropEvent(currentEvent, self._previousEvent)) {
41-
__DEBUG_BUILD__ && logger.warn('Event dropped due to being a duplicate of previously captured event.');
42-
return null;
43-
}
44-
} catch (_oO) {
45-
return (self._previousEvent = currentEvent);
46-
}
47-
48-
return (self._previousEvent = currentEvent);
40+
// Juuust in case something goes wrong
41+
try {
42+
if (_shouldDropEvent(currentEvent, this._previousEvent)) {
43+
__DEBUG_BUILD__ && logger.warn('Event dropped due to being a duplicate of previously captured event.');
44+
return null;
4945
}
50-
return currentEvent;
51-
};
46+
} catch (_oO) {} // eslint-disable-line no-empty
5247

53-
eventProcessor.id = this.name;
54-
addGlobalEventProcessor(eventProcessor);
48+
return (this._previousEvent = currentEvent);
5549
}
5650
}
5751

Lines changed: 23 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { addGlobalEventProcessor, getCurrentHub } from '@sentry/core';
21
import type { Event, Integration } from '@sentry/types';
32

43
import { WINDOW } from '../helpers';
@@ -23,28 +22,28 @@ export class HttpContext implements Integration {
2322
* @inheritDoc
2423
*/
2524
public setupOnce(): void {
26-
addGlobalEventProcessor((event: Event) => {
27-
if (getCurrentHub().getIntegration(HttpContext)) {
28-
// if none of the information we want exists, don't bother
29-
if (!WINDOW.navigator && !WINDOW.location && !WINDOW.document) {
30-
return event;
31-
}
32-
33-
// grab as much info as exists and add it to the event
34-
const url = (event.request && event.request.url) || (WINDOW.location && WINDOW.location.href);
35-
const { referrer } = WINDOW.document || {};
36-
const { userAgent } = WINDOW.navigator || {};
37-
38-
const headers = {
39-
...(event.request && event.request.headers),
40-
...(referrer && { Referer: referrer }),
41-
...(userAgent && { 'User-Agent': userAgent }),
42-
};
43-
const request = { ...event.request, ...(url && { url }), headers };
44-
45-
return { ...event, request };
46-
}
47-
return event;
48-
});
25+
// noop
26+
}
27+
28+
/** @inheritDoc */
29+
public preprocessEvent(event: Event): void {
30+
// if none of the information we want exists, don't bother
31+
if (!WINDOW.navigator && !WINDOW.location && !WINDOW.document) {
32+
return;
33+
}
34+
35+
// grab as much info as exists and add it to the event
36+
const url = (event.request && event.request.url) || (WINDOW.location && WINDOW.location.href);
37+
const { referrer } = WINDOW.document || {};
38+
const { userAgent } = WINDOW.navigator || {};
39+
40+
const headers = {
41+
...(event.request && event.request.headers),
42+
...(referrer && { Referer: referrer }),
43+
...(userAgent && { 'User-Agent': userAgent }),
44+
};
45+
const request = { ...event.request, ...(url && { url }), headers };
46+
47+
event.request = request;
4948
}
5049
}

packages/browser/src/profiling/integration.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export class BrowserProfilingIntegration implements Integration {
3535
/**
3636
* @inheritDoc
3737
*/
38-
public setupOnce(addGlobalEventProcessor: (callback: EventProcessor) => void, getCurrentHub: () => Hub): void {
38+
public setupOnce(_addGlobalEventProcessor: (callback: EventProcessor) => void, getCurrentHub: () => Hub): void {
3939
this.getCurrentHub = getCurrentHub;
4040
const client = this.getCurrentHub().getClient() as BrowserClient;
4141

packages/replay/src/coreHandlers/handleGlobalEvent.ts

Lines changed: 42 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -16,51 +16,54 @@ export function handleGlobalEventListener(
1616
): (event: Event, hint: EventHint) => Event | null {
1717
const afterSendHandler = includeAfterSendEventHandling ? handleAfterSendEvent(replay) : undefined;
1818

19-
return (event: Event, hint: EventHint) => {
20-
// Do nothing if replay has been disabled
21-
if (!replay.isEnabled()) {
22-
return event;
23-
}
19+
return Object.assign(
20+
(event: Event, hint: EventHint) => {
21+
// Do nothing if replay has been disabled
22+
if (!replay.isEnabled()) {
23+
return event;
24+
}
2425

25-
if (isReplayEvent(event)) {
26-
// Replays have separate set of breadcrumbs, do not include breadcrumbs
27-
// from core SDK
28-
delete event.breadcrumbs;
29-
return event;
30-
}
26+
if (isReplayEvent(event)) {
27+
// Replays have separate set of breadcrumbs, do not include breadcrumbs
28+
// from core SDK
29+
delete event.breadcrumbs;
30+
return event;
31+
}
3132

32-
// We only want to handle errors & transactions, nothing else
33-
if (!isErrorEvent(event) && !isTransactionEvent(event)) {
34-
return event;
35-
}
33+
// We only want to handle errors & transactions, nothing else
34+
if (!isErrorEvent(event) && !isTransactionEvent(event)) {
35+
return event;
36+
}
3637

37-
// Unless `captureExceptions` is enabled, we want to ignore errors coming from rrweb
38-
// As there can be a bunch of stuff going wrong in internals there, that we don't want to bubble up to users
39-
if (isRrwebError(event, hint) && !replay.getOptions()._experiments.captureExceptions) {
40-
__DEBUG_BUILD__ && logger.log('[Replay] Ignoring error from rrweb internals', event);
41-
return null;
42-
}
38+
// Unless `captureExceptions` is enabled, we want to ignore errors coming from rrweb
39+
// As there can be a bunch of stuff going wrong in internals there, that we don't want to bubble up to users
40+
if (isRrwebError(event, hint) && !replay.getOptions()._experiments.captureExceptions) {
41+
__DEBUG_BUILD__ && logger.log('[Replay] Ignoring error from rrweb internals', event);
42+
return null;
43+
}
4344

44-
// When in buffer mode, we decide to sample here.
45-
// Later, in `handleAfterSendEvent`, if the replayId is set, we know that we sampled
46-
// And convert the buffer session to a full session
47-
const isErrorEventSampled = shouldSampleForBufferEvent(replay, event);
45+
// When in buffer mode, we decide to sample here.
46+
// Later, in `handleAfterSendEvent`, if the replayId is set, we know that we sampled
47+
// And convert the buffer session to a full session
48+
const isErrorEventSampled = shouldSampleForBufferEvent(replay, event);
4849

49-
// Tag errors if it has been sampled in buffer mode, or if it is session mode
50-
// Only tag transactions if in session mode
51-
const shouldTagReplayId = isErrorEventSampled || replay.recordingMode === 'session';
50+
// Tag errors if it has been sampled in buffer mode, or if it is session mode
51+
// Only tag transactions if in session mode
52+
const shouldTagReplayId = isErrorEventSampled || replay.recordingMode === 'session';
5253

53-
if (shouldTagReplayId) {
54-
event.tags = { ...event.tags, replayId: replay.getSessionId() };
55-
}
54+
if (shouldTagReplayId) {
55+
event.tags = { ...event.tags, replayId: replay.getSessionId() };
56+
}
5657

57-
// In cases where a custom client is used that does not support the new hooks (yet),
58-
// we manually call this hook method here
59-
if (afterSendHandler) {
60-
// Pretend the error had a 200 response so we always capture it
61-
afterSendHandler(event, { statusCode: 200 });
62-
}
58+
// In cases where a custom client is used that does not support the new hooks (yet),
59+
// we manually call this hook method here
60+
if (afterSendHandler) {
61+
// Pretend the error had a 200 response so we always capture it
62+
afterSendHandler(event, { statusCode: 200 });
63+
}
6364

64-
return event;
65-
};
65+
return event;
66+
},
67+
{ id: 'Replay' },
68+
);
6669
}

packages/replay/src/util/addGlobalListeners.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,12 @@ export function addGlobalListeners(replay: ReplayContainer): void {
2626

2727
// Tag all (non replay) events that get sent to Sentry with the current
2828
// replay ID so that we can reference them later in the UI
29-
addGlobalEventProcessor(handleGlobalEventListener(replay, !hasHooks(client)));
29+
const eventProcessor = handleGlobalEventListener(replay, !hasHooks(client));
30+
if (client && client.addEventProcessor) {
31+
client.addEventProcessor(eventProcessor);
32+
} else {
33+
addGlobalEventProcessor(eventProcessor);
34+
}
3035

3136
// If a custom client has no hooks yet, we continue to use the "old" implementation
3237
if (hasHooks(client)) {

0 commit comments

Comments
 (0)