The Errors
plugin automatically captures JavaScript and other errors from
your web application.
This plugin has a corresponding Header Snippets that helps capture errors prior to Boomerang loading.
For information on how to include this plugin, see the Building tutorial.
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. NoteBOOMR.plugins.AutoXHR
is required if using this.- Any calls to
window.console.error
- JavaScript runtime errors that happen during a callback for
addEventListener
(disabled by default, enabled via
)monitorEvents
- JavaScript runtime errors that happen during a callback for
setTimeout
andsetInterval
(disabled by default, enabled via
)monitorTimeout
- Manually sent errors via
BOOMR.plugins.Errors.send
- Functions that threw an exception that were wrapped via
BOOMR.plugins.Errors.wrap
- Functions that threw an exception that were run via
BOOMR.plugins.Errors.test
- JavaScript runtime errors captured via the
unhandledrejection
global event handler (disabled by default, enabled via
)monitorRejections
- JavaScript runtime warnings captured via the
Reporting API
(disabled by default, enabled via
)monitorReporting
All of the options above 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. Due to the lack
of error detail on some older browsers, some errors may be reported more than once.
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.plugins.Errors.send
: Immediately sends an error.BOOMR.plugins.Errors.wrap
: Wraps a function with error trackingBOOMR.plugins.Errors.test
: Runs the function and captures any errors.
Error callback
You can specify an
function that
the Errors plugin will call any time an error is captured on the page.onError
If your onError
function returns true
, the error will be captured.
If your onError
function does not return true
, the error will be ignored.
Example:
BOOMR.init({
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 capturing of errors after page load, you need to set
to sendAfterOnload
true
. If set,
errors that happen after the page load will be sent at most once every
(which defaults to 1 second)
on a new beacon.sendInterval
Example:
BOOMR.init({
Errors: {
sendAfterOnload: true,
sendInterval: 5000
}
});
How Many Errors to Capture
The Errors
plugin will only capture up to
(defaults to 10) distinct
errors on the page.maxErrors
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.
You can increase (or decrease) maxErrors
. For example:
BOOMR.init({
Errors: {
maxErrors: 20
}
});
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.
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
(via the
option)
and monitorTimeout
addEventListener
(via the
option)
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
monitorEvents
BOOMR.plugin.Errors.wrap(function)
.
Note that enabling
or
monitorTimeout
can have side-effects and has caused
compatibility issues with JavaScript code on some sites. Enabling those options is only
recommended after verifying there are no problems.monitorEvents
Why is Boomerang in my 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 onconsole.error
if error tracking is turned onsetTimeout
andsetInterval
(if error tracking is turned on (with
)monitorTimeout
addEventListener
andremoveEventListener
(if error tracking is turned on (with
)monitorEvents
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://mysite.com/boomerang.js: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://mysite.com/boomerang.js: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://mysite.com/boomerang.js: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
BOOMR_plugins_errors_onrejection
Side Effects
Enabling wrapping through
and
monitorEvents
may trigger some side effects:monitorTimeout
- Boomerang's monitoring code will be run first for every callback, which will add minimal (though non-zero) overhead.
- In browser console logs, errors that are triggered by other libraries that have been wrapped will now look like they come from Boomerang instead, as Boomerang is now on the bottom of the call stack.
- Browser developer tools such as Chrome's Performance and Profiler tabs may be confused about JavaScript CPU attribution. In other words, they may think Boomerang is the cause of more work than it is.
- Chrome Lighthouse may be confused about JavaScript CPU attribution, due to the same reasons as above.
- WebPagetest may be confused about JavaScript CPU attribution, due to the same reasons as above.
- There are some cases where JavaScript applications may have compatibility issues with the wrapping. Some
notable cases include:
- Use of the global
window.event
object, see issue. - Other libraries that wrap
setTimeout
,addEventListener
, etc such ashistory.js
. - Pages that use the
<base href="...">
tag.
- Use of the global
For more details, you can read this article.
Beacon Parameters
err
: The compressed error data structurehttp.initiator = error
(if not part of a Page Load beacon)
The compressed error data structure is a JSURL encoded JSON object.
Each element in the array is a compressed representation of a JavaScript error:
n
: Count (if the error was seen more than once)f[]
: An array of framesf[].l
: Line numberf[].c
: Colum numberf[].f
: Function namef[].w
: File name (if origin differs from root page)f[].wo
: File name without origin (if same as root page)
s
: Source:1
: Error was triggered by the application2
: Error was triggered by Boomerang
v
: Via1
: Application (BOOMR.plugins.Errors.send
)2
: Global exception handler (window.onerror
)3
: Network (XHR) error4
: Console (console.error
)5
: Event handler (addEventListener
)6
:setTimeout
orsetInterval
t
: Type (e.g.SyntaxError
orReferenceError
)c
: Code (for network errors)m
: Error messagx
: Extra datad
: Timestamp (base 36)
Methods
-
init(config)
-
Initializes the plugin.
Parameters:
Name Type Description config
object Configuration
Properties
Name Type Argument Description Errors.onError
function <optional>
Callback to fire when an error occurs
Errors.monitorGlobal
boolean <optional>
Monitor
window.onerror
Errors.monitorNetwork
boolean <optional>
Monitor XHR errors
Errors.monitorConsole
boolean <optional>
Monitor
console.error
Errors.monitorEvents
boolean <optional>
Monitor event callbacks (from
addEventListener
). NOTE: Enabling this may cause compatibility issues with certain sites. Verifications should be run before enabling in production.Errors.monitorTimeout
boolean <optional>
Monitor
setTimout
andsetInterval
. NOTE: Enabling this may cause compatibility issues with certain sites. Verifications should be run before enabling in production.Errors.monitorRejections
boolean <optional>
Monitor unhandled promise rejections.
Errors.monitorReporting
boolean <optional>
Monitor Reporting API warnings.
Errors.sendAfterOnload
boolean <optional>
Whether or not to send errors after the page load beacon. If set to false, only errors that happened up to the page load beacon will be captured.
Errors.sendInterval
boolean <optional>
If
sendAfterOnload
is true, how often to send the latest batch of errors.Errors.maxErrors
number <optional>
Maximum number of errors to track per page.
Returns:
BOOMR.plugins.Errors
The Errors plugin for chaining -
is_complete()
-
This plugin is always complete (ready to send a beacon)
Returns:
Type: boolean
true
-
is_supported()
-
Determines if Error tracking is initialized.
Returns:
Type: boolean
true
-
send(error)
-
Sends an error
Parameters:
Name Type Description error
Error | String Error object or message
-
test(fn, that, args)
-
Runs the function, watching for exceptions
Parameters:
Name Type Description fn
function Function
that
object Target object
args
Array.<object> Arguments
-
wrap(fn, that, via)
-
Wraps the function in an exception handler that will automatically report exceptions.
Parameters:
Name Type Description fn
function Function
that
object Target object
via
number Via (optional)
Returns:
Type: function
Wrapped function