Skip to content

Commit 08738b0

Browse files
committed
ref: Move global error handler+ promise rejection
1 parent 46b5fb7 commit 08738b0

File tree

3 files changed

+135
-103
lines changed

3 files changed

+135
-103
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
- "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott
66

7+
- [browser] ref: Move global error handler + unhandled promise rejection to instrument
8+
79
## 5.13.2
810

911
- [apm] feat: Add `discardBackgroundSpans` to discard background spans by default

packages/browser/src/integrations/globalhandlers.ts

Lines changed: 78 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { getCurrentHub } from '@sentry/core';
22
import { Event, Integration, Severity } from '@sentry/types';
33
import {
44
addExceptionMechanism,
5-
getGlobalObject,
5+
addInstrumentationHandler,
66
getLocationHref,
77
isErrorEvent,
88
isPrimitive,
@@ -34,15 +34,6 @@ export class GlobalHandlers implements Integration {
3434
/** JSDoc */
3535
private readonly _options: GlobalHandlersIntegrations;
3636

37-
/** JSDoc */
38-
private readonly _global: Window = getGlobalObject();
39-
40-
/** JSDoc */
41-
private _oldOnErrorHandler: OnErrorEventHandler = null;
42-
43-
/** JSDoc */
44-
private _oldOnUnhandledRejectionHandler: ((e: any) => void) | null = null;
45-
4637
/** JSDoc */
4738
private _onErrorHandlerInstalled: boolean = false;
4839

@@ -80,49 +71,41 @@ export class GlobalHandlers implements Integration {
8071
return;
8172
}
8273

83-
const self = this; // tslint:disable-line:no-this-assignment
84-
this._oldOnErrorHandler = this._global.onerror;
74+
addInstrumentationHandler({
75+
callback: (data: { msg: any; url: any; line: any; column: any; error: any }) => {
76+
const error = data.error;
77+
const currentHub = getCurrentHub();
78+
const hasIntegration = currentHub.getIntegration(GlobalHandlers);
79+
const isFailedOwnDelivery = error && error.__sentry_own_request__ === true;
8580

86-
this._global.onerror = function(msg: any, url: any, line: any, column: any, error: any): boolean {
87-
const currentHub = getCurrentHub();
88-
const hasIntegration = currentHub.getIntegration(GlobalHandlers);
89-
const isFailedOwnDelivery = error && error.__sentry_own_request__ === true;
90-
91-
if (!hasIntegration || shouldIgnoreOnError() || isFailedOwnDelivery) {
92-
if (self._oldOnErrorHandler) {
93-
return self._oldOnErrorHandler.apply(this, arguments);
81+
if (!hasIntegration || shouldIgnoreOnError() || isFailedOwnDelivery) {
82+
return;
9483
}
95-
return false;
96-
}
97-
98-
const client = currentHub.getClient();
99-
const event = isPrimitive(error)
100-
? self._eventFromIncompleteOnError(msg, url, line, column)
101-
: self._enhanceEventWithInitialFrame(
102-
eventFromUnknownInput(error, undefined, {
103-
attachStacktrace: client && client.getOptions().attachStacktrace,
104-
rejection: false,
105-
}),
106-
url,
107-
line,
108-
column,
109-
);
110-
111-
addExceptionMechanism(event, {
112-
handled: false,
113-
type: 'onerror',
114-
});
115-
116-
currentHub.captureEvent(event, {
117-
originalException: error,
118-
});
119-
120-
if (self._oldOnErrorHandler) {
121-
return self._oldOnErrorHandler.apply(this, arguments);
122-
}
12384

124-
return false;
125-
};
85+
const client = currentHub.getClient();
86+
const event = isPrimitive(error)
87+
? this._eventFromIncompleteOnError(data.msg, data.url, data.line, data.column)
88+
: this._enhanceEventWithInitialFrame(
89+
eventFromUnknownInput(error, undefined, {
90+
attachStacktrace: client && client.getOptions().attachStacktrace,
91+
rejection: false,
92+
}),
93+
data.url,
94+
data.line,
95+
data.column,
96+
);
97+
98+
addExceptionMechanism(event, {
99+
handled: false,
100+
type: 'onerror',
101+
});
102+
103+
currentHub.captureEvent(event, {
104+
originalException: error,
105+
});
106+
},
107+
type: 'error',
108+
});
126109

127110
this._onErrorHandlerInstalled = true;
128111
}
@@ -133,67 +116,60 @@ export class GlobalHandlers implements Integration {
133116
return;
134117
}
135118

136-
const self = this; // tslint:disable-line:no-this-assignment
137-
this._oldOnUnhandledRejectionHandler = this._global.onunhandledrejection;
138-
139-
this._global.onunhandledrejection = function(e: any): boolean {
140-
let error = e;
141-
142-
// dig the object of the rejection out of known event types
143-
try {
144-
// PromiseRejectionEvents store the object of the rejection under 'reason'
145-
// see https://developer.mozilla.org/en-US/docs/Web/API/PromiseRejectionEvent
146-
if ('reason' in e) {
147-
error = e.reason;
119+
addInstrumentationHandler({
120+
callback: (e: any) => {
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
148140
}
149-
// something, somewhere, (likely a browser extension) effectively casts PromiseRejectionEvents
150-
// to CustomEvents, moving the `promise` and `reason` attributes of the PRE into
151-
// the CustomEvent's `detail` attribute, since they're not part of CustomEvent's spec
152-
// see https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent and
153-
// https://github.com/getsentry/sentry-javascript/issues/2380
154-
else if ('detail' in e && 'reason' in e.detail) {
155-
error = e.detail.reason;
156-
}
157-
} catch (_oO) {
158-
// no-empty
159-
}
160141

161-
const currentHub = getCurrentHub();
162-
const hasIntegration = currentHub.getIntegration(GlobalHandlers);
163-
const isFailedOwnDelivery = error && error.__sentry_own_request__ === true;
142+
const currentHub = getCurrentHub();
143+
const hasIntegration = currentHub.getIntegration(GlobalHandlers);
144+
const isFailedOwnDelivery = error && error.__sentry_own_request__ === true;
164145

165-
if (!hasIntegration || shouldIgnoreOnError() || isFailedOwnDelivery) {
166-
if (self._oldOnUnhandledRejectionHandler) {
167-
return self._oldOnUnhandledRejectionHandler.apply(this, arguments);
146+
if (!hasIntegration || shouldIgnoreOnError() || isFailedOwnDelivery) {
147+
return true;
168148
}
169-
return true;
170-
}
171149

172-
const client = currentHub.getClient();
173-
const event = isPrimitive(error)
174-
? self._eventFromIncompleteRejection(error)
175-
: eventFromUnknownInput(error, undefined, {
176-
attachStacktrace: client && client.getOptions().attachStacktrace,
177-
rejection: true,
178-
});
150+
const client = currentHub.getClient();
151+
const event = isPrimitive(error)
152+
? this._eventFromIncompleteRejection(error)
153+
: eventFromUnknownInput(error, undefined, {
154+
attachStacktrace: client && client.getOptions().attachStacktrace,
155+
rejection: true,
156+
});
179157

180-
event.level = Severity.Error;
158+
event.level = Severity.Error;
181159

182-
addExceptionMechanism(event, {
183-
handled: false,
184-
type: 'onunhandledrejection',
185-
});
160+
addExceptionMechanism(event, {
161+
handled: false,
162+
type: 'onunhandledrejection',
163+
});
186164

187-
currentHub.captureEvent(event, {
188-
originalException: error,
189-
});
165+
currentHub.captureEvent(event, {
166+
originalException: error,
167+
});
190168

191-
if (self._oldOnUnhandledRejectionHandler) {
192-
return self._oldOnUnhandledRejectionHandler.apply(this, arguments);
193-
}
194-
195-
return true;
196-
};
169+
return;
170+
},
171+
type: 'unhandledrejection',
172+
});
197173

198174
this._onUnhandledRejectionHandlerInstalled = true;
199175
}

packages/utils/src/instrument.ts

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,15 @@ interface InstrumentHandler {
1515
type: InstrumentHandlerType;
1616
callback: InstrumentHandlerCallback;
1717
}
18-
type InstrumentHandlerType = 'console' | 'dom' | 'fetch' | 'history' | 'sentry' | 'xhr';
18+
type InstrumentHandlerType =
19+
| 'console'
20+
| 'dom'
21+
| 'fetch'
22+
| 'history'
23+
| 'sentry'
24+
| 'xhr'
25+
| 'error'
26+
| 'unhandledrejection';
1927
type InstrumentHandlerCallback = (data: any) => void;
2028

2129
/**
@@ -25,6 +33,8 @@ type InstrumentHandlerCallback = (data: any) => void;
2533
* - XHR API
2634
* - History API
2735
* - DOM API (click/typing)
36+
* - Error API
37+
* - UnhandledRejection API
2838
*/
2939

3040
const handlers: { [key in InstrumentHandlerType]?: InstrumentHandlerCallback[] } = {};
@@ -54,6 +64,12 @@ function instrument(type: InstrumentHandlerType): void {
5464
case 'history':
5565
instrumentHistory();
5666
break;
67+
case 'error':
68+
instrumentError();
69+
break;
70+
case 'unhandledrejection':
71+
instrumentUnhandledRejection();
72+
break;
5773
default:
5874
logger.warn('unknown instrumentation type:', type);
5975
}
@@ -471,3 +487,41 @@ function keypressEventHandler(handler: Function): (event: Event) => void {
471487
}, debounceDuration) as any) as number;
472488
};
473489
}
490+
491+
let _oldOnErrorHandler: OnErrorEventHandler = null;
492+
/** JSDoc */
493+
function instrumentError(): void {
494+
_oldOnErrorHandler = global.onerror;
495+
496+
global.onerror = function(msg: any, url: any, line: any, column: any, error: any): boolean {
497+
triggerHandlers('error', {
498+
column,
499+
error,
500+
line,
501+
msg,
502+
url,
503+
});
504+
505+
if (_oldOnErrorHandler) {
506+
return _oldOnErrorHandler.apply(this, arguments);
507+
}
508+
509+
return false;
510+
};
511+
}
512+
513+
let _oldOnUnhandledRejectionHandler: ((e: any) => void) | null = null;
514+
/** JSDoc */
515+
function instrumentUnhandledRejection(): void {
516+
_oldOnUnhandledRejectionHandler = global.onunhandledrejection;
517+
518+
global.onunhandledrejection = function(e: any): boolean {
519+
triggerHandlers('unhandledrejection', e);
520+
521+
if (_oldOnUnhandledRejectionHandler) {
522+
return _oldOnUnhandledRejectionHandler.apply(this, arguments);
523+
}
524+
525+
return true;
526+
};
527+
}

0 commit comments

Comments
 (0)