Skip to content

Commit b8af480

Browse files
committed
feat(utils): Refactor addInstrumentationHandler to dedicated methods
This is mostly an internal utility, but changes a bit. This improves the actual type safety a lot, because currently we've been duplicating the types everywhere, sometimes in slightly differing ways, leading to potential issues.
1 parent 04e7be9 commit b8af480

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+1323
-1067
lines changed

packages/browser/src/integrations/breadcrumbs.ts

Lines changed: 30 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,26 @@
1-
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
21
/* eslint-disable max-lines */
32
import { getCurrentHub } from '@sentry/core';
4-
import type { Event as SentryEvent, HandlerDataFetch, HandlerDataXhr, Integration } from '@sentry/types';
3+
import type {
4+
Event as SentryEvent,
5+
HandlerDataConsole,
6+
HandlerDataDom,
7+
HandlerDataFetch,
8+
HandlerDataHistory,
9+
HandlerDataXhr,
10+
Integration,
11+
} from '@sentry/types';
512
import type {
613
FetchBreadcrumbData,
714
FetchBreadcrumbHint,
815
XhrBreadcrumbData,
916
XhrBreadcrumbHint,
1017
} from '@sentry/types/build/types/breadcrumb';
1118
import {
12-
addInstrumentationHandler,
19+
addClickKeypressInstrumentationHandler,
20+
addConsoleInstrumentationHandler,
21+
addFetchInstrumentationHandler,
22+
addHistoryInstrumentationHandler,
23+
addXhrInstrumentationHandler,
1324
getEventDescription,
1425
htmlTreeAsString,
1526
logger,
@@ -21,8 +32,6 @@ import {
2132

2233
import { WINDOW } from '../helpers';
2334

24-
type HandlerData = Record<string, unknown>;
25-
2635
/** JSDoc */
2736
interface BreadcrumbsOptions {
2837
console: boolean;
@@ -88,19 +97,19 @@ export class Breadcrumbs implements Integration {
8897
*/
8998
public setupOnce(): void {
9099
if (this.options.console) {
91-
addInstrumentationHandler('console', _consoleBreadcrumb);
100+
addConsoleInstrumentationHandler(_consoleBreadcrumb);
92101
}
93102
if (this.options.dom) {
94-
addInstrumentationHandler('dom', _domBreadcrumb(this.options.dom));
103+
addClickKeypressInstrumentationHandler(_domBreadcrumb(this.options.dom));
95104
}
96105
if (this.options.xhr) {
97-
addInstrumentationHandler('xhr', _xhrBreadcrumb);
106+
addXhrInstrumentationHandler(_xhrBreadcrumb);
98107
}
99108
if (this.options.fetch) {
100-
addInstrumentationHandler('fetch', _fetchBreadcrumb);
109+
addFetchInstrumentationHandler(_fetchBreadcrumb);
101110
}
102111
if (this.options.history) {
103-
addInstrumentationHandler('history', _historyBreadcrumb);
112+
addHistoryInstrumentationHandler(_historyBreadcrumb);
104113
}
105114
if (this.options.sentry) {
106115
const client = getCurrentHub().getClient();
@@ -130,8 +139,8 @@ function addSentryBreadcrumb(event: SentryEvent): void {
130139
* A HOC that creaes a function that creates breadcrumbs from DOM API calls.
131140
* This is a HOC so that we get access to dom options in the closure.
132141
*/
133-
function _domBreadcrumb(dom: BreadcrumbsOptions['dom']): (handlerData: HandlerData) => void {
134-
function _innerDomBreadcrumb(handlerData: HandlerData): void {
142+
function _domBreadcrumb(dom: BreadcrumbsOptions['dom']): (handlerData: HandlerDataDom) => void {
143+
function _innerDomBreadcrumb(handlerData: HandlerDataDom): void {
135144
let target;
136145
let keyAttrs = typeof dom === 'object' ? dom.serializeAttribute : undefined;
137146

@@ -182,7 +191,7 @@ function _domBreadcrumb(dom: BreadcrumbsOptions['dom']): (handlerData: HandlerDa
182191
/**
183192
* Creates breadcrumbs from console API calls
184193
*/
185-
function _consoleBreadcrumb(handlerData: HandlerData & { args: unknown[]; level: string }): void {
194+
function _consoleBreadcrumb(handlerData: HandlerDataConsole): void {
186195
const breadcrumb = {
187196
category: 'console',
188197
data: {
@@ -212,7 +221,7 @@ function _consoleBreadcrumb(handlerData: HandlerData & { args: unknown[]; level:
212221
/**
213222
* Creates breadcrumbs from XHR API calls
214223
*/
215-
function _xhrBreadcrumb(handlerData: HandlerData & HandlerDataXhr): void {
224+
function _xhrBreadcrumb(handlerData: HandlerDataXhr): void {
216225
const { startTimestamp, endTimestamp } = handlerData;
217226

218227
const sentryXhrData = handlerData.xhr[SENTRY_XHR_DATA_KEY];
@@ -250,7 +259,7 @@ function _xhrBreadcrumb(handlerData: HandlerData & HandlerDataXhr): void {
250259
/**
251260
* Creates breadcrumbs from fetch API calls
252261
*/
253-
function _fetchBreadcrumb(handlerData: HandlerData & HandlerDataFetch & { response?: Response }): void {
262+
function _fetchBreadcrumb(handlerData: HandlerDataFetch): void {
254263
const { startTimestamp, endTimestamp } = handlerData;
255264

256265
// We only capture complete fetch requests
@@ -282,13 +291,14 @@ function _fetchBreadcrumb(handlerData: HandlerData & HandlerDataFetch & { respon
282291
hint,
283292
);
284293
} else {
294+
const response = handlerData.response as Response | undefined;
285295
const data: FetchBreadcrumbData = {
286296
...handlerData.fetchData,
287-
status_code: handlerData.response && handlerData.response.status,
297+
status_code: response && response.status,
288298
};
289299
const hint: FetchBreadcrumbHint = {
290300
input: handlerData.args,
291-
response: handlerData.response,
301+
response,
292302
startTimestamp,
293303
endTimestamp,
294304
};
@@ -306,15 +316,15 @@ function _fetchBreadcrumb(handlerData: HandlerData & HandlerDataFetch & { respon
306316
/**
307317
* Creates breadcrumbs from history API calls
308318
*/
309-
function _historyBreadcrumb(handlerData: HandlerData & { from: string; to: string }): void {
319+
function _historyBreadcrumb(handlerData: HandlerDataHistory): void {
310320
let from: string | undefined = handlerData.from;
311321
let to: string | undefined = handlerData.to;
312322
const parsedLoc = parseUrl(WINDOW.location.href);
313-
let parsedFrom = parseUrl(from);
323+
let parsedFrom = from ? parseUrl(from) : undefined;
314324
const parsedTo = parseUrl(to);
315325

316326
// Initial pushState doesn't provide `from` information
317-
if (!parsedFrom.path) {
327+
if (!parsedFrom || !parsedFrom.path) {
318328
parsedFrom = parsedLoc;
319329
}
320330

packages/browser/src/integrations/globalhandlers.ts

Lines changed: 65 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,18 @@
11
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
22
import { getCurrentHub } from '@sentry/core';
3-
import type { Event, EventHint, Hub, Integration, Primitive, StackParser } from '@sentry/types';
3+
import type {
4+
Event,
5+
EventHint,
6+
HandlerDataUnhandledRejection,
7+
Hub,
8+
Integration,
9+
Primitive,
10+
StackParser,
11+
} from '@sentry/types';
412
import {
513
addExceptionMechanism,
6-
addInstrumentationHandler,
14+
addGlobalErrorInstrumentationHandler,
15+
addGlobalUnhandledRejectionInstrumentationHandler,
716
getLocationHref,
817
isErrorEvent,
918
isPrimitive,
@@ -78,81 +87,73 @@ export class GlobalHandlers implements Integration {
7887

7988
/** JSDoc */
8089
function _installGlobalOnErrorHandler(): void {
81-
addInstrumentationHandler(
82-
'error',
83-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
84-
(data: { msg: any; url: any; line: any; column: any; error: any }) => {
85-
const [hub, stackParser, attachStacktrace] = getHubAndOptions();
86-
if (!hub.getIntegration(GlobalHandlers)) {
87-
return;
88-
}
89-
const { msg, url, line, column, error } = data;
90-
if (shouldIgnoreOnError() || (error && error.__sentry_own_request__)) {
91-
return;
92-
}
90+
addGlobalErrorInstrumentationHandler(data => {
91+
const [hub, stackParser, attachStacktrace] = getHubAndOptions();
92+
if (!hub.getIntegration(GlobalHandlers)) {
93+
return;
94+
}
95+
const { msg, url, line, column, error } = data;
96+
if (shouldIgnoreOnError()) {
97+
return;
98+
}
9399

94-
const event =
95-
error === undefined && isString(msg)
96-
? _eventFromIncompleteOnError(msg, url, line, column)
97-
: _enhanceEventWithInitialFrame(
98-
eventFromUnknownInput(stackParser, error || msg, undefined, attachStacktrace, false),
99-
url,
100-
line,
101-
column,
102-
);
100+
const event =
101+
error === undefined && isString(msg)
102+
? _eventFromIncompleteOnError(msg, url, line, column)
103+
: _enhanceEventWithInitialFrame(
104+
eventFromUnknownInput(stackParser, error || msg, undefined, attachStacktrace, false),
105+
url,
106+
line,
107+
column,
108+
);
103109

104-
event.level = 'error';
110+
event.level = 'error';
105111

106-
addMechanismAndCapture(hub, error, event, 'onerror');
107-
},
108-
);
112+
addMechanismAndCapture(hub, error, event, 'onerror');
113+
});
109114
}
110115

111116
/** JSDoc */
112117
function _installGlobalOnUnhandledRejectionHandler(): void {
113-
addInstrumentationHandler(
114-
'unhandledrejection',
115-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
116-
(e: any) => {
117-
const [hub, stackParser, attachStacktrace] = getHubAndOptions();
118-
if (!hub.getIntegration(GlobalHandlers)) {
119-
return;
118+
addGlobalUnhandledRejectionInstrumentationHandler(e => {
119+
const [hub, stackParser, attachStacktrace] = getHubAndOptions();
120+
if (!hub.getIntegration(GlobalHandlers)) {
121+
return;
122+
}
123+
let error: Error | HandlerDataUnhandledRejection = e;
124+
125+
// dig the object of the rejection out of known event types
126+
try {
127+
// PromiseRejectionEvents store the object of the rejection under 'reason'
128+
// see https://developer.mozilla.org/en-US/docs/Web/API/PromiseRejectionEvent
129+
if ('reason' in e && e.reason) {
130+
error = e.reason;
120131
}
121-
let error = e;
122-
123-
// dig the object of the rejection out of known event types
124-
try {
125-
// PromiseRejectionEvents store the object of the rejection under 'reason'
126-
// see https://developer.mozilla.org/en-US/docs/Web/API/PromiseRejectionEvent
127-
if ('reason' in e) {
128-
error = e.reason;
129-
}
130-
// something, somewhere, (likely a browser extension) effectively casts PromiseRejectionEvents
131-
// to CustomEvents, moving the `promise` and `reason` attributes of the PRE into
132-
// the CustomEvent's `detail` attribute, since they're not part of CustomEvent's spec
133-
// see https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent and
134-
// https://github.com/getsentry/sentry-javascript/issues/2380
135-
else if ('detail' in e && 'reason' in e.detail) {
136-
error = e.detail.reason;
137-
}
138-
} catch (_oO) {
139-
// no-empty
132+
// something, somewhere, (likely a browser extension) effectively casts PromiseRejectionEvents
133+
// to CustomEvents, moving the `promise` and `reason` attributes of the PRE into
134+
// the CustomEvent's `detail` attribute, since they're not part of CustomEvent's spec
135+
// see https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent and
136+
// https://github.com/getsentry/sentry-javascript/issues/2380
137+
else if ('detail' in e && e.detail && 'reason' in e.detail && e.detail.reason) {
138+
error = e.detail.reason;
140139
}
140+
} catch (_oO) {
141+
// no-empty
142+
}
141143

142-
if (shouldIgnoreOnError() || (error && error.__sentry_own_request__)) {
143-
return true;
144-
}
144+
if (shouldIgnoreOnError()) {
145+
return true;
146+
}
145147

146-
const event = isPrimitive(error)
147-
? _eventFromRejectionWithPrimitive(error)
148-
: eventFromUnknownInput(stackParser, error, undefined, attachStacktrace, true);
148+
const event = isPrimitive(error)
149+
? _eventFromRejectionWithPrimitive(error)
150+
: eventFromUnknownInput(stackParser, error, undefined, attachStacktrace, true);
149151

150-
event.level = 'error';
152+
event.level = 'error';
151153

152-
addMechanismAndCapture(hub, error, event, 'onunhandledrejection');
153-
return;
154-
},
155-
);
154+
addMechanismAndCapture(hub, error, event, 'onunhandledrejection');
155+
return;
156+
});
156157
}
157158

158159
/**

packages/browser/src/sdk.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,12 @@ import {
77
Integrations as CoreIntegrations,
88
} from '@sentry/core';
99
import type { UserFeedback } from '@sentry/types';
10-
import { addInstrumentationHandler, logger, stackParserFromStackParserOptions, supportsFetch } from '@sentry/utils';
10+
import {
11+
addHistoryInstrumentationHandler,
12+
logger,
13+
stackParserFromStackParserOptions,
14+
supportsFetch,
15+
} from '@sentry/utils';
1116

1217
import type { BrowserClientOptions, BrowserOptions } from './client';
1318
import { BrowserClient } from './client';
@@ -240,9 +245,9 @@ function startSessionTracking(): void {
240245
startSessionOnHub(hub);
241246

242247
// We want to create a session for every navigation as well
243-
addInstrumentationHandler('history', ({ from, to }) => {
248+
addHistoryInstrumentationHandler(({ from, to }) => {
244249
// Don't create an additional session for the initial route or if the location did not change
245-
if (!(from === undefined || from === to)) {
250+
if (from !== undefined && from !== to) {
246251
startSessionOnHub(getCurrentHub());
247252
}
248253
});

packages/core/src/tracing/errors.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
import { addInstrumentationHandler, logger } from '@sentry/utils';
1+
import {
2+
addGlobalErrorInstrumentationHandler,
3+
addGlobalUnhandledRejectionInstrumentationHandler,
4+
logger,
5+
} from '@sentry/utils';
26

37
import type { SpanStatusType } from './span';
48
import { getActiveTransaction } from './utils';
@@ -14,8 +18,8 @@ export function registerErrorInstrumentation(): void {
1418
}
1519

1620
errorsInstrumented = true;
17-
addInstrumentationHandler('error', errorCallback);
18-
addInstrumentationHandler('unhandledrejection', errorCallback);
21+
addGlobalErrorInstrumentationHandler(errorCallback);
22+
addGlobalUnhandledRejectionInstrumentationHandler(errorCallback);
1923
}
2024

2125
/**

0 commit comments

Comments
 (0)