Errors
The Boomerang Errors
plugin automatically captures JavaScript and other errors from your web application.
Sources of errors
When the Errors
plugin is enabled, the following sources of errors are captured:
- JavaScript runtime errors captured via the
onerror
global event handler XMLHttpRequest
responses that were not successful- Any calls to
window.console.error
- JavaScript runtime errors that happen during a callback for
addEventListener
- JavaScript runtime errors that happen during a callback for
setTimeout
andsetInterval
- Manually sent errors via
BOOMR.plugin.Errors.send(message|exception)
- Functions that threw an exception that were wrapped via
BOOMR.plugin.Errors.wrap(function)
- Functions that threw an exception that were run via
BOOMR.plugin.Errors.test(function, arg1, arg2)
These are all enabled by default, and can be manually turned off.
Supported browsers
The Errors
plugin can be enabled for all browsers, though some older browsers may not be able to capture the full breadth of sources of errors.
Notable browsers:
- Internet Explorer <= 8: Does not support capturing
XMLHttpRequest
errors.
Manually sending errors
Besides automatically capturing errors from onerror
, XMLHttpRequest
, console.error
or event handlers such as setTimeout
, you can also manually send errors.
There are three ways of doing this as follows:
BOOMR.plugin.Errors.send()
: Immediately sends an error.BOOMR.plugin.Errors.wrap()
: Wraps a function with error trackingBOOMR.plugin.Errors.test()
: Runs the function and captures any errors.
Dealing with script error
When looking at JavaScript errors, you will likely come across the generic error message: Script error.
Script Error.
is the message that browsers send to the window.onerror
global exception handler when the error was triggered by a script loaded from a different (cross) origin. window.onerror
is used by Boomerang so that it gets notified of all unhandled exceptions.
The Script Error.
string is given instead of the real error message and does not contain any useful information about what caused the error. In addition, there is no stack associated with the message, so it's impossible to know where or why the error occurred.
Browsers mask the real error message for cross-origin scripts due to security and privacy concerns - they don't want to leak sensitive information in the message or stack. The only thing that window.onerror
knows for cross-origin scripts is that an error occurred, not where or why.
Example
For an example of where you'd see Script Error.
, consider the following code that lives on website.com
:
<html>
<head>
<title>website.com</title>
</head>
<body>
<script>
window.onerror = function(message, url, line, column, error) {
console.log("window.onerror: " + message);
console.log((error && error.stack) ? error.stack : "(no stack)");
};
</script>
<script src="my-script.js"></script>
<script src="https://anothersite.com/my-script.js"></script>
</body>
</html>
Assume my-script.js
is the same file being served from both website.com
and anothersite.com
:
function runCode() {
a = b + 1;
}
runCode();
When my-script.js
is loaded from website.com
, it will be executed twice:
-
First on the same-origin, where we'll see the full error message followed by the stack:
window.onerror: Uncaught ReferenceError: b is not defined ReferenceError: b is not defined at runCode (my-script.js:2) at my-script.js:5
-
Then, it will be loaded from
https://anothersite.com/my-script.js
, which will be considered cross-origin and onlyScript Error.
will be logged:window.onerror: Script error. (no stack)
As you can see, browser shares the full details of the exception when it's served from the same origin as the website, but if it's served from any other origin, it will be considered cross-origin and no details will be shared.
Note that while the browser only shares Script Error.
to window.onerror
for cross-origin scripts, if you have browser developer tools open, the browser will show you the full error message in the Console. This is
because there aren't any security or privacy concerns for a developer looking at their own machine's information.
The screenshot below shows what the Chrome Developer tools look like for the above
code:
-
The first (same-origin) error message is written to the
console.log()
(in black) followed by the browser developer tools logging the same error (in red). -
The second (cross-origin) error only shows
Script error.
in theconsole.log()
(in black) with no stack, but the browser developer tools show the full message
and stack (in red):
When You'll See Script Error
Unfortunately Script Error.
will be shown in many legitimate use-cases,
such as:
-
When serving your website's JavaScript from a CDN (since it will be coming
from a different origin) -
When loading a library such as jQuery or Angular from their CDN, i.e. Google's Hosted Libraries or cdnjs
-
When a third-party script loads from another domain
The good news is that in many of these cases, there are changes you can make to ensure the full error message and stack are shared with window.onerror
.
Fixing Script Error
To ensure a cross-origin script shares full error details with window.onerror
, you'll need to do two things:
-
Add
crossorigin="anonymous"
to the<script>
tagThe
crossorigin="anonymous"
attribute tells the browser that the script should be fetched without sending any cookies or HTTP authentication -
Add the
Access-Control-Allow-Origin
(ACAO) header to the JavaScript file's response.The
Access-Control-Allow-Origin
header is part of the Cross Origin Resource Sharing (CORS) standard.The ACAO header must be set in the JavaScript's HTTP response headers.
An example header that sets ACAO for all calling origins would be:
Access-Control-Allow-Origin: *
If both conditions are true, cross-origin JavaScript files will report errors to window.onerror
with the correct error message and full stack.
The biggest challenge to getting this working is that (1) is within the site's control while (2) can only be configured by the owner of the JavaScript. If you're loading JavaScript from a third-party, you will need to encourage them to add the ACAO header if it's not already set. The good news is that many CDNs and third-parties set the ACAO header already.
Workarounds for Third Parties that aren't sending ACAO
One way to help monitor for errors coming from third-party scripts that aren't setting ACAO (and aren't within your control) is by manually wrapping calls to any of the third-party script's functions in a try {} catch {}
.
try {
// calls a cross-origin script that doesn't have ACAO
runThirdPartyCode();
} catch (e) {
// report on error with e.message and e.stack
}
If runThirdPartyCode()
causes any errors, the catch {}
handler will get the full details of the exception.
Unfortunately this won't work for functions that are executed in the third-party script as a result of browser events or callbacks (since you're not wrapping them).
When using Boomerang to monitor JavaScript errors, Boomerang automatically wraps some of the built-in browser APIs such as setTimeout
, setInterval
and addEventListener
with a minimal-overhead wrapper. It does this to help ensure as many cross-origin exceptions as possible have full stack details. You may also do this manually via BOOMR.plugin.Errors.wrap(function)
.
Why Boomerang is in the error stack
When looking at error reports, you may find errors that have a function in boomerang.js
(or /boomerang/
) on the stack. Why is that? Is Boomerang causing errors on your site?
One of the ways that Boomerang is able to monitor and measure your site's performance is by wrapping itself around some of the core browser APIs. Boomerang only does this in a few places, if absolutely necessary -- namely, when the browser doesn't provide a native "monitoring" interface for something that needs to be tracked.
One example is for XMLHttpRequests
, as there are no browser APIs to monitor when XHRs load. To monitor XHRs, Boomerang swaps in its own window.XMLHttpRequest
object, wrapping around the native methods. When an XHR is created (via .open()
), the lightweight Boomerang wrapper is executed first so it can log a start timestamp. When the XHR finishes (via a readyState
change), Boomerang can log the end timestamp and report on the XHR's performance.
Examples of Boomerang wrapping native methods include:
XMLHttpRequest
if the XHR instrumentation is turned onsetTimeout
andsetInterval
if error tracking is turned onconsole.error
if error tracking is turned onaddEventListener
andremoveEventListener
if error tracking is turned on
All of these wrapped functions come into play when you see an error stack with a boomerang.js
function in it.
Often, the boomerang.js
function will be at the bottom of the stack (the first function called). This does not mean Boomerang caused the error, merely that the monitoring code was running before the error occurred. The actual
error happens towards the top of the stack -- the function that ran and threw the exception.
Let's look at some examples:
Cannot read property 'foo' of undefined at thirdPartyTwo (https://thirdparty.com/core.js:1:100)
at thirdPartyOne (https://thirdparty.com/core.js:1:101)
at runThirdParty (https://thirdparty.com/core.js:1:102)
at xhrCallback (http://website.com/site.js:2:200)
at XMLHttpRequest.send (https://c.go-mpulse.net/boomerang/XXXXX-XXXXX-XXXXX-XXXXX-XXXXX:3:300)
In the above example, Boomerang is monitoring XMLHttpRequests
. An XHR was loaded on the site, and during the XHR callback, an exception was thrown. Even though /boomerang/
is listed here, the error was caused by code in the XHR callback (xhrCallback
eventually calling thirdPartyTwo
).
Here's a second example:
Reference error: a is not defined at setTimeout (http://website.com/site.js:1:200) at BOOMR_plugins_errors_wrap (http://c.go-mpulse.net/boomerang/XXXXX-XXXXX-XXXXX-XXXXX-XXXXX:3:300) at onclick (http://website.com/site.js:1:100)
In the above example, JavaScript Error Reporting is enabled and an exception was thrown in a setTimeout()
on the website. You can see the BOOMR_plugins_errors_wrap
function is near the top of the stack, but this is merely the error tracking code. All it did was wrap setTimeout
to help ensure that cross-origin exceptions are caught. It was not the actual cause of the site's error.
Here's a third example:
Error: missing argument 1 at BOOMR.window.console.error (https://c.go-mpulse.net/boomerang/XXXXX-XXXXX-XXXXX-XXXXX-XXXXX:3:300)
at u/< (https://website.com/site.js:1:100)
at tp/this.$get</< (https://website.com/site.js:1:200)
at $digest (https://website.com/site.js:1:300)
at $apply (https://website.com/site.js:1:400)
at ut (https://website.com/site.js:1:500)
at it (https://website.com/site.js:1:600)
at vp/</k.onload (https://website.com/site.js:1:700)
In the above example, JavaScript Error Reporting is enabled and has wrapped console.error
. The minified function u/<
must be logging a console.error
, which executes the Boomerang wrapper code, reporting the error.
In summary, if you see Boomerang functions in error stacks similar to any of the ones listed below, it's probable that you're just seeing a side-effect of the monitoring code:
BOOMR_addError
BOOMR_plugins_errors_onerror
BOOMR_plugins_errors_onxhrerror
BOOMR_plugins_errors_console_error
BOOMR_plugins_errors_wrap
BOOMR.window.console.error
Configuration options
Enabling
Check the Collect Javascript Errors checkbox in the mPulse Configure Web App dialog box on the Beacons tab.
Enabling with config overrides
The Errors
plugin is disabled by default, but can be enabled via Config Overrides:
window.BOOMR_config = window.BOOMR_config || {};
BOOMR_config.Errors = {
enabled: true,
monitorTimeout: false,
monitorEvents: false
};
Sources
Each of the sources of errors can be turned off manually via the following monitor*
options:
monitorGlobal
enables theonerror
global event handlermonitorNetwork
enables monitoringXMLHttpRequest
responses (Automatically Instrument XHR also needs to be enabled)monitorConsole
enables monitoring calls towindow.console.error
monitorEvents
enables monitoringaddEventListener
callbacksmonitorTimeout
enables monitoringsetTimeout
andsetInterval
callbacks
For example:
window.BOOMR_config = window.BOOMR_config || {};
BOOMR_config.Errors = {
monitorGlobal: true, // onerror
monitorNetwork: true, // XHRs
monitorConsole: true, // window.console.error
monitorEvents: true, // addEventListener
monitorTimeout: true, // setTimeout, setInterval
};
Error callback
You can specify an onError
function that the Errors
plugin will call any time an error is captured on the page.
If your onError
function returns true
, the error will be captured. If your onError
function does not return true
, the error will be ignored.
For example:
window.BOOMR_config = window.BOOMR_config || {};
BOOMR_config.Errors = {
onError: function(err) {
if (err.message && err.message.indexOf("internally handled")) {
return false;
}
return true;
}
};
When to send errors
By default, errors captured during the page load will be sent along with the page load beacon. Errors that happen after the page load will not be captured or sent.
To enable this feature, check the Send Error Beacon if JavaScript Errors happen after the Page Load checkbox under the Collect Javascript Errors feature in the mPulse Configure Web App dialog box on the Beacons tab. The Send Interval (in milliseconds) text box controls the interval that errors occurring after the page load are sent on a new beacon.
Alternatively, to enable capturing of errors after page load via Config Overrides, set sendAfterOnload
to true
. If set, errors that happen after the page load will be sent at most once every sendInterval
(which defaults to 1000 milliseconds) on a new beacon.
For example:
window.BOOMR_config = window.BOOMR_config || {};
BOOMR_config.Errors = {
sendAfterOnload: true,
sendInterval: 5000
};
How many errors to capture
The Errors
plugin will only capture up to maxErrors
(defaults to 10
) distinct errors on the page.
Please note that duplicate errors (those with the same function name, stack, and so on) are tracked as single distinct error, with a count of how many times it was seen.
Enter the desired value in the Maximum Number of Unique Errors to Track per Page text box under the Collect Javascript Errors feature in the mPulse Configure Web App dialog box on the Beacons tab.
Alternatively, maxErrors
can be increased (or decreased) via Config Overrides. For example:
window.BOOMR_config = window.BOOMR_config || {};
BOOMR_config.Errors = {
maxErrors: 2
};
Updated about 2 years ago