Effective error reporting with EmberJS and Bugsnag

An important part of a professional application is catching and reporting bugs. In Candidio, we report bugs from our EmberJS application to Bugsnag. Bugsnag has great documentation and a good amount of language support with their first-party notifier libraries. We're quite happy with their service and look forward to using them for the forseeable future.

Raygun recently detailed how to send error reports to their service, so I thought I'd pass along how we accomplished a similar feat with Bugsnag.

Bugsnag.js

Get Bugsnag's Javascript notifier with Bower using the command bower install --save bugsnag. Import the bugsnag.js source into your app in Brocfile.js with app.import('bower_components/bugsnag/src/bugsnag.js');.

In .jshintrc add "Bugsnag": true to the predef hash, so you don't get squawked at every time you use the Bugsnag global variable.

Initializer

Create a new initializer at app/initializers/bugsnag.js on the command line with the ember g initializer bugsnag command. In the initialize function, we're going to declare the Bugsnag API key, the releaseStage (current environment), and the stages we want to notify Bugsnag from. In Candidio, we only send Bugsnag errors from our staging environment and the production site.

Bugsnag.apiKey = config.APP.BUGSNAG.API_KEY;
Bugsnag.releaseStage = config.environment;
Bugsnag.notifyReleaseStages = ["staging", "production"];

Routing Errors

The first type of error we want to report is routing and edge cases, which we'll catch with Ember.onerror. In this error report and the following, we're sending along the current path, rather than letting Bugsnag grabbing the URL from location.href. I've found that in the context of EmberJS development, this is more useful.

Ember.onerror = function(error) {
  Bugsnag.context = appController.get('currentPath');
  Bugsnag.notifyException(error);
  console.error(error.stack); // Still send the error to the console
};

Promise/AJAX Errors

We should also catch errors from rejected Promises, which we can do similarly with the RSVP.on('error') method.

Ember.RSVP.on('error', function (error) {
  Bugsnag.context = appController.get('currentPath');
  Bugsnag.notifyException(error);
});

Ember Logger Errors

Let's also send any errors logged by Ember's internal logger service and make sure they still get sent to the console.

Ember.Logger.error = function (message, cause, stack) {
  var currentRoute = appController.get('currentPath');
  Bugsnag.context = currentRoute;
  Bugsnag.notifyException(new Error(message), null, { cause: cause, stack: stack });
  console.error(stack);
};

Bonus: Javascript Sourcemaps

Sourcemaps are enabled by default on the ember-cli development build, but to get a sourcemap on every build, including production, add the following sourcemaps hash in Brocfile.js. Bugsnag will get the sourcemap declaration at the bottom of our concatenated source and give you a much improved stack track over the minified source.

// Brocfile.js
var app = new EmberApp({
  ...
  sourcemaps: {
    "enabled": true,
    "extensions": ["js"]
  }
});

Gist of the Bugsnag integration

Other useful details you can send to Bugsnag


Updated March 18, 2015: We should still log out appropriate errors to the console. Thanks to Ben Holmes for the tip!