0

I'm writing an error-logging module to add Raygun logging to an AngularJS application. The standard method is to register a decorator for $exceptionHandler like so:

$provide.decorator("$exceptionHandler", ['$delegate', function($delegate) {
    return function (exception, cause) {
      Raygun.send(exception);
      $delegate(exception, cause);
    }
  }])

However, this has an order dependence in that the decorator must be installed before the DI framework first loads $exceptionHandler. If I'm wrapping this code in a module, the order dependence is even less obvious, and it's easy for the client to initialize things in the wrong order and have no idea why Raygun.send isn't getting called. (I was that client for a day and a half. In my defense, this is my first experience with Angular.)

I'd like to persuade Angular's DI framework to load my error-logging module before $exceptionHandler, with the obvious caveat that I'm hosed if any exceptions are thrown really early in bootstrapping. I thought of trying to register my module as a dependency of $exceptionHandler, but haven't been able to figure out how to do that yet -- also, it's not actually true that $exceptionHandler depends upon my code. Is there an idiomatic way to do this that I'm missing?

Matt Olson
  • 409
  • 5
  • 13

1 Answers1

0

Angular instantiates $exceptionHandler and all other ng- services during its own config phase, during its own ng module instantiation. Simply move your code to a config of the ng module:

angular.module('ng')
.config(['$provide', function($provide) {
    $provide.decorator('$exceptionHandler', ['$delegate', function($delegate) {
        return function (exception, cause) {
            Raygun.send(exception);
            $delegate(exception, cause);
        }
    }]);
}]);

This configuration will be placed in the queue immediately after $exceptionHandler gets instantiated.

However, config/run errors are not delegated to $exceptionHandler or any angular logger, they are simply uncaught, propagating to the end of the stack. You can capture these exceptions, but you must manually bootstrap angular. Remove the ng-app attribute, and instead run this code:

angular.element(document).ready(function() {
    try {
        angular.bootstrap(document, ['yourModule']);
    } catch (exception) {
        Raygun.send(exception);
        throw exception;
    }
});

With both errors caught passing though the $exceptionHandler and caught during bootstrapping, there shouldn't be an error that doesn't get reported.

Example plunker: http://plnkr.co/edit/XsiMNA7GIbHzXyjEioxy?p=preview
Open your console and uncomment one of the errors in script.js on line 9 or 15. You should see angular's regular error handling as well as a dummy Raygun.send logging warnings to the console.

jgawrych
  • 3,322
  • 1
  • 28
  • 38