Error tracking
Applications can sometimes produce errors, but because they run in a browser, developers are unaware that they’re occurring. In addition, users don’t always report errors, which adds to the complexity of troubleshooting them.
The error instrumentation subscribes to the browser’s window.onerror
and onunhandledrejection
events to collect them, extract the stack trace if available, and report them to the Grafana Faro Web SDK core API
using the faro.api.pushError()
function.
Tracking errors helps you to:
- Analyze anomalies that occur in your application, such as crashes and incorrect behavior
- Monitor that your application is working as expected
- Detect issues with external systems that your application is using
The error instrumentation should always be enabled as it is the only way to automatically capture errors. You can, however, disable this instrumentation if you are tracking errors by other means.
After you set it up, Faro Web SDK automatically handles and reports all unhandled exceptions and rejected promises.
Unhandled errors are errors that are not identified in a try/catch
block and are picked up by the global error handler.
To auto track errors, Faro registers two error handlers:
onError
: to catch unhandled exceptionsonunhandledrejection
: to catch errors from rejected JavaScript Promises that don’t have rejection handlers attached
The Faro data model for errors is a structured format containing the line number of the script file where the error occurred.
Faro errors are stored as logs, identified by the kind=error
label.
For more information about Loki labels, refer to Labels.
Report errors manually
You can send errors manually by using the faro.api.pushError
function.
The function allows you to configure the behavior of the error api
to inject extra attributes and more.
The following sections provide available options as well as some examples for how to use the API
.
Push error parameters
Name | Description |
---|---|
value | A JavaScript error |
options | Configure the behavior of the push API or inject additional data to change the signal data. |
Push error options
Name | Description |
---|---|
stackFrames | A list of stack frames |
type | The type of error |
context | Includes additional error context |
skipDedupe | Pushes the signal even if it is identical to the previous one |
spanContext | Provides a custom spanContext to the signal |
timestampOverwriteMs | The timestamp represents the exact moment when the respective API was called. In most cases, this precision is sufficient. The timestampOverwriteMs option allows you to pass an adjustment timestamp that can be used instead. |
Examples
// Import the global faro instance
import { faro } from '@grafana/faro-web-sdk';
// Send custom errors with faro
// 1. Simple error generated at some point in your application
// 2. Namespaced error
// Supposed this error is generated somewhere in your app
const error = new Error('I am supposed to fail');
// ...
// And at some point you want to pipe it to Faro
faro.api.pushError(error);
// Different type
const error = new Error('I am an error generated by network requests');
// ...
faro.api.pushError(error, {
type: 'network',
});
// with additional contextual attributes
faro.api.pushError(error, {
type: 'network',
context: {
message: 'React error boundary',
componentStackTrace: {...}
},
});
Exclude errors
Ignore errors from tracking. Ignored errors can either be defined as strings or regular expression patterns.
initializeFaro({
url: 'https://my-domain.my-tld/collect/{app-key}',
app: {
name: 'my-app',
},
instrumentations: [...getWebInstrumentations()],
ignoreErrors: ['My Error', /.*other-error.*/],
});
Handle special cases
Sometimes Faro can’t infer all relevant error information due to browser restrictions or other edge cases.
Enhance third party script errors with stack traces
When errors happen in third-party scripts, web browsers restrict access for security reasons and replace the contents of the errors with a generic “script error” message. This prevents Faro from inferring the relevant data to construct a stack trace and other diagnostics.
If you trust the resources, you can instruct web browsers to reveal the error messages of third-party scripts.
Add crossorigin="anonymous"
to the respective script tags to instruct the browser to set the mode to cors
and the credentials mode to same-origin
for that specific request.
<!-- https://mysite.com:443 -->
<script src="http://mysite.com/myscript.js" crossorigin="anonymous"></script>
The server’s HTTP response, which delivers the script to the client, needs to have the Access-Control-Allow-Origin
header set:
Access-Control-Allow-Origin: https://mysite.com:443
Handle other edge cases
There can be even more cases where Faro can’t derive relevant data from errors, such as in uncontrolled environments or when using libraries which are outside of your control.
Some examples:
throw undefined
throw ''
- Enriching errors while they bubble up to a global error handler which re-throws the error.
To handle such cases, you can instruct Faro to preserve the initial error to be read in the beforeSend
function.
This function allows you to mutate or drop items before they are sent to the Faro collector.
If enabled, Faro adds the originalError
property to the Faro exception payload which holds a
reference to the original JavaScript error.
You can use the original to update the respective Faro exception with more data to make such errors more useful.
Faro automatically removes the original error before sending the data to prevent unnecessary requests to increase sizes.
Example
// ... other imports
import { ExceptionEventExtended } from '@grafana/faro-web-sdk';
initializeFaro({
url: 'https://my-domain.my-tld/collect/{app-key}',
app: {
name: 'my-app',
},
// instruct Faro to preserve the original error
preserveOriginalError: true,
// Update the Faro exception
beforeSend(item) {
if (item.type === 'exception') {
const _item = item as TransportItem<ExceptionEventExtended>;
const originalError = _item.payload.originalError;
if (originalError?.name == null) {
_item.payload.value = 'Uncontrolled error, name was undefined';
// ...
}
}
return item;
},
});