Menu
Grafana Cloud

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:

  1. onError: to catch unhandled exceptions
  2. onunhandledrejection: 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

NameDescription
valueA JavaScript error
optionsConfigure the behavior of the push API or inject additional data to change the signal data.

Push error options

NameDescription
stackFramesA list of stack frames
typeThe type of error
contextIncludes additional error context
skipDedupePushes the signal even if it is identical to the previous one
spanContextProvides a custom spanContext to the signal
timestampOverwriteMsThe timestamp represents the exact moment when the respective API was called. In most cases, this precision is sufficient. The timestampOverwriteMsoption allows you to pass an adjustment timestamp that can be used instead.

Examples

typescript
// Import the global faro instance
import { faro } from '@grafana/faro-web-sdk';
typescript
// 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.

ts
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.

html
<!-- 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:

htaccess
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

ts
// ... 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;
  },
});