This article was created in partnership with Sentry. Thank you for supporting the partners who make SitePoint possible.
onerror
is a special browser event that fires whenever an uncaught JavaScript error has been thrown. It's one of the easiest ways to log client-side errors and report them to your servers. It's also one of the major mechanisms by which Sentry's client JavaScript integration (raven-js) works.
You listen to the onerror event by assigning a function to window.onerror
:
window.onerror = function (msg, url, lineNo, columnNo, error) {
// ... handle error ...
return false;
}
When an error is thrown, the following arguments are passed to the function:
- msg – The message associated with the error, e.g. "Uncaught ReferenceError: foo is not defined"
- url – The URL of the script or document associated with the error, e.g. "/dist/app.js"
- lineNo – The line number (if available)
- columnNo – The column number (if available)
- error – The Error object associated with this error (if available)
The first four arguments tell you in which script, line, and column the error occurred. The final argument, Error object, is perhaps the most valuable. Let's learn why.
The Error Object and error.stack
At first glance the Error object isn't very special. It contains 3 standardized properties: message, fileName, and lineNumber. Redundant values that are already provided to you via window.onerror
.
The valuable part is a non-standard property: Error.prototype.stack
. This stack property tells you at what source location each frame of the program was when the error occurred. The error stack trace can be a critical part of debugging. And despite being non-standard, this property is available in every modern browser.
Here's an example of the Error object's stack property in Chrome 46:
"Error: foobar\n at new bar (<anonymous>:241:11)\n at foo (<anonymous>:245:5)\n at <anonymous>:250:5\n at <anonymous>:251:3\n at <anonymous>:267:4\n at callFunction (<anonymous>:229:33)\n at <anonymous>:239:23\n at <anonymous>:240:3\n at Object.InjectedScript._evaluateOn (<anonymous>:875:140)\n at Object.InjectedScript._evaluateAndWrap (<anonymous>:808:34)"
Hard to read, right? The stack property is actually just an unformatted string.
Here's what it looks like formatted:
Error: foobar
at new bar (<anonymous>:241:11)
at foo (<anonymous>:245:5)
at callFunction (<anonymous>:229:33)
at Object.InjectedScript._evaluateOn (<anonymous>:875:140)
at Object.InjectedScript._evaluateAndWrap (<anonymous>:808:34)
Once it has been formatted, it's easy to see how the stack property can be critical in helping to debug an error.
There's just one snag: the stack property is non-standard, and its implementation differs among browsers. For example, here's the same stack trace from Internet Explorer 11:
Error: foobar
at bar (Unknown script code:2:5)
at foo (Unknown script code:6:5)
at Anonymous function (Unknown script code:11:5)
at Anonymous function (Unknown script code:10:2)
at Anonymous function (Unknown script code:1:73)
Not only is the format of each frame different, the frames also have less detail. For example, Chrome identifies that the new
keyword has been used, and has greater insight into eval
invocations. And this is just IE 11 vs. Chrome — other browsers similarly have varying formats and detail.
Luckily, there are tools out there that normalize stack properties so that it is consistent across browsers. For example, raven-js uses TraceKit to normalize error strings. There's also stacktrace.js and a few other projects.
Browser Compatibility
window.onerror
has been available in browsers for some time — you'll find it in browsers as old as IE6 and Firefox 2.
The problem is that every browser implements window.onerror
differently, particularly, in how many arguments are sent to the onerror listener and the structure of those arguments.
Here's a table of which arguments are passed to onerror in most browsers:
Browser | Message | URL | lineNo | colNo | errorObj |
---|---|---|---|---|---|
Firefox | ✓ | ✓ | ✓ | ✓ | ✓ |
Chrome | ✓ | ✓ | ✓ | ✓ | ✓ |
Edge | ✓ | ✓ | ✓ | ✓ | ✓ |
IE 11 | ✓ | ✓ | ✓ | ✓ | ✓ |
IE 10 | ✓ | ✓ | ✓ | ✓ | |
IE 9, 8 | ✓ | ✓ | ✓ | ||
Safari 10 and up | ✓ | ✓ | ✓ | ✓ | ✓ |
Safari 9 | ✓ | ✓ | ✓ | ✓ | |
Android Browser 4.4 | ✓ | ✓ | ✓ | ✓ |
It's probably not a surprise that Internet Explorer 8, 9, and 10 have limited support for onerror. But you might be surprised that Safari only added support for the error object in in Safari 10 (released in 2016). Additionally, older mobile handsets that still use the stock Android browser (now replaced with Chrome Mobile), are still out there and do not pass the error object.
Without the error object, there is no stack trace property. This means that these browsers cannot retrieve valuable stack information from errors caught by onerror.
Polyfilling window.onerror with try/catch
But there is a workaround — you can wrap code in your application inside a try/catch and catch the error yourself. This error object will contain our coveted stack
property in every modern browser.
Continue reading %Capture and Report JavaScript Errors with window.onerror%
by Ben Vinegar via SitePoint
No comments:
Post a Comment