The State of Client side JavaScript Error Reporting

airbrake-man-jsEffectively handling browser-generated errors, like much of web development, has long been a difficult, error-prone and highly arcane practice. Browser differences, inline event handlers, frameworks and plugins without canonical error-handling attachment points, and callback hell have all contributed to the bruxism that plagues front-end devs.

Airbrake exists to make devs lives easier, and not even the browser is exempt from our efforts. We’ve taken a long hard look at the state of tooling out there, and delivered tooling that we feel does just the right amount of heavy lifting while allowing developers to integrate the tool in ways that make sense for their environment.

Easy things aren’t easy

For years, most browsers have supported the window.onerror event to varying degrees. Scripts run inline have fired this error, as have errors triggered by missing resources including scripts and images.

Unfortunately, errors handled this way lack what is arguably the most important piece of information an error can carry, its stack trace.

A number of error-handling tools hook into window.onerror and some even attempt to attach additional information to errors propagated this way. In general, we’ve taken a different approach to these problems, insisting that errors be handled closer to their source.

What we do differently

The Airbrake client-side error notifier takes a slightly different approach to dealing with errors from any of the tools that have come before it.

Simple, convenient API

Airbrake’s client, like most JavaScript error-handlers provides a single API call to report an error. Ours looks something like this.

try {
  myProblematicFunction();
} catch(theError) {
  Airbrake.push({ error: theError });
}

This construction appears over and over again in code where we care about errors. In fact, it occurs so often that we provide a convience wrapper for exactly this pattern.

Airbrake.try(myProblematicFunction);

Asynchronous all the way down

Page-load times and overall application responsiveness have been pressing topics amongst web developers for years. We wanted to create a tool that slotted easily into existing applications without modifying existing tooling or introducing delays into page-load.

A critical component to achieving this is providing an API that can be used immediately by your application’s code, even though the full notifier has not been downloaded.

We accomplish this first by providing a shim implementation of the API that supports the push and try calls. This shim is inspired by a technique pioneered by Google Analytics. First, the shim object is created as a native JavaScript Array. Next, the try method is added to the shim, though its implementation doesn’t differ from the canonical version, since try simply delegates to push.

In order to load the canonical implementation, we use the defer attribute on the remote script tag to ensure its download and execution doesn’t interfere with your application’s code.

Attribute-driven configuration

Calls to the Airbrake Reporting API need to carry information that uniquely identifies your account and application. Some reporting tools ask developers to configure specific reporting URLs. Even those that don’t use an imperative approach to configuration, typically providing API methods like setAPIKey and the like.

Although the Airbrake reporter does support configuration methods like this, they are only used by the notifier itself as it bootstraps.

Instead, canonical configuration information is stored and read as data-attributes on a script DOM node, separating the configuration of the notifier from code that actually uses its services.

Modular design

Rather than creating a monolithic error-reporting application with lots of functionality baked right in, we elected to break this thing apart into well-defined components. This means that each piece of functionality can be (and is) testable and tested. It also means that as older browsers are no longer supported, code dealing with their eccentricities can be sunsetted and removed. Additionally, as new browser features are introduced, there is a clear point for their integration.

Best of breed tools

Handling errors in JavaScript isn’t new territory, and there are some battle-tested tools already out there for smoothing out some of the differences in error-handling between browsers. One such tool is TraceKit. One of its nicest features is normalizing stack traces. The Airbrake notifier integrates this tool so that you can benefit from the bumps and scrapes suffered by developers before you.

We use the excellent browserify to wrap and expose our tools to the notifier, including a few really wonderful npm modules.

Sourcemaps Support

Probably the most exciting feature we were able to bring to this notifier is SourceMaps support. What this means is that as long as your concatenated, minified JavaScript includes a sourceMappingURL directive, reported errors will use your original file names and line numbers, even in browsers that don’t support SourceMaps natively in their developer tools.

Cross-domain reporting

In order to avoid mixed content warnings, the Airbrake-js notifier is obtained via https, and communicates with the Airbrake API servers via https as well. Since we’re still supporting older browsers, errors are reported using a JSONP GET request. Once non-CORS browser support is removed, we’ll be able to swap out this transport mechanism for a simpler XHR mechanism that can handle larger payloads and won’t block the JavaScript interpreter.

airbrake-man-js-2Future

The state of error-reporting is changing for the better. Not only are tools like Airbrake-js making it simpler to learn about the errors your users are experiencing, but browser vendors are providing new tools to allow for even less-obtrusive integrations. The modular design and unit-tested code we’re launching with will allow us to rapidly integrate these new tools into our notifier, as well as to refine existing features.

Specifically, we’re really excited about the proposals around window.getLastError, allowing the notifier to extract useful information when errors occur even if the code in question hasn’t been wrapped in a try/catch block.

We’re also excited to see new frameworks launching with error-reporting in mind, such as Ember’s onerror, and Angular’s$exceptionHandler. Frameworks providing these easy attachment points remove the need to instrument client code manually, potentially saving developers even more heavy lifting.

Try it out

The Airbrake-js notifier is available for all paid accounts. We think you’ll be pleased to get a clearer view about just what’s happening with your application everywhere it runs; in the browser and, not just on the server. Give it a spin today and let us know what you think.

Sign up for an account here. 

5 thoughts on “The State of Client side JavaScript Error Reporting

  1. No need to clutter your code with loads of try wrappers like this suggests. There’s a much better way. It turns out that in a typical web app, a handful of operations account for the vast majority of new execution contexts:

    – setTimeout/setInterval
    - event handlers
    - ajax response handlers

    Wrap those entry points (along with a single top-level wrapper which can be done as a build step) and you’re almost entirely covered. If you use jQuery along with TraceKit, this plugin does it for you: https://github.com/getsentry/raven-js/blob/master/plugins/jquery.js

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>