How to replace $log implementation

4,486 views
Skip to first unread message

Steven Pack

unread,
Feb 14, 2013, 11:45:49 PM2/14/13
to ang...@googlegroups.com
Hi All,

I have angularjs app running inside the IE webbrowser control hosted in a WinForms app. I have my own $logger service, which I use in my controllers which communicates to the host app to write to a log file.

Today, I had an issue where an error was raised inside angular from this function.

 function beginPhase(phase) {
      if ($rootScope.$$phase) {
        throw Error($rootScope.$$phase + ' already in progress');
      }

      $rootScope.$$phase = phase;
    }

That invoked the $exceptionHandler, which in turn invoked $log, which does:

var console = $window.console || {}

and then happily wrote the error message to nowhere. It took ages to debug because the error was getting swallowed.

So, how should I replace the implementation of $log? Or $exceptionHandler? Any other suggestions?

Clint Checketts

unread,
Feb 15, 2013, 12:04:27 AM2/15/13
to ang...@googlegroups.com
You can decorate providers

Here is an example where I make $log alert as well as write to console: http://plnkr.co/edit/imqmvUfk4oWWuwg75sP1?p=preview

Pertinent code:

app.config(function($provide) {
  $provide.decorator('$log', function($delegate, $sniffer) {
    var _log = $delegate.log; //Saving the original behavior
       
    $delegate.log = function(msg){ //replacing the original behavior and including the original
      _log(msg);
      alert(msg);
    };
       
        return $delegate;
    });
});


--
You received this message because you are subscribed to the Google Groups "AngularJS" group.
To unsubscribe from this group and stop receiving emails from it, send an email to angular+u...@googlegroups.com.
To post to this group, send email to ang...@googlegroups.com.
Visit this group at http://groups.google.com/group/angular?hl=en-US.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Clint Checketts

unread,
Feb 15, 2013, 5:41:38 PM2/15/13
to ang...@googlegroups.com
I've updated the plunkr to also show how you can replace the $exceptionHandler completely http://plnkr.co/edit/imqmvUfk4oWWuwg75sP1?p=preview

This is much more straight forward.

app.factory('$exceptionHandler',function(){
    return function(exception, cause){
        alert(JSON.stringify(exception), cause);
    };
});


Steven Pack

unread,
Feb 17, 2013, 6:53:31 PM2/17/13
to ang...@googlegroups.com
Yep, that is much more straight forward and is exactly what I was after.

Thanks.

Oto Dočkal

unread,
Feb 17, 2013, 7:20:56 PM2/17/13
to ang...@googlegroups.com
+1

Dne pátek, 15. února 2013 23:41:38 UTC+1 Clint Checketts napsal(a):

Clint Checketts

unread,
Feb 17, 2013, 8:29:05 PM2/17/13
to ang...@googlegroups.com
I think we should get the documentation updated to show this, since it even talks about 'the default implementation', implying it can or should be overridden.

I'll look into how those docs can be updated and create a pull request.

ThomasBurleson

unread,
Mar 6, 2013, 9:23:09 AM3/6/13
to ang...@googlegroups.com
That $exceptionHandler override is VERY nice. 

Yes, plz document... meanwhile I will incorporate that idea into my own codebase.

Lance Vick

unread,
Mar 21, 2013, 10:02:13 AM3/21/13
to ang...@googlegroups.com
This example seems to work flawlessly in practice... however it breaks unit testing with angular-mock so something is not mapping 1/1... any ideas?


Example: 

## app/logging.js

angular.module('app').config(
  function($provide) {
    $provide.decorator
      ( '$log'
      , function($delegate, $sniffer) {

          var _log = $delegate.log
          $delegate.log = function(msg){
            _log.apply(null,arguments)
            //any logic for catching $log.log messages here
          }

          return $delegate
        }
      )
  }
)

## app/controllers/ApplicationController.js

angular.module('app').controller
  ( 'ApplicationController'
  , function
    ( $scope
    , $log
    ) {

      // this works normally
      $log.log('testing logging')

    }
  )

## test/spec/controllers/main.js

describe('Application'
  , function () {
      beforeEach(module('app'))
      describe('ApplicationController'
      , function () {
          it('should be available'
          , inject(
            function($rootScope, $controller){
              var ctrl = $controller
                ( "ApplicationController"
                , { $scope: $rootScope.$new() }
                )
              expect(ctrl).toBeDefined();
            }
          )
        )
      }
    )
  }
)

## testacular start --single-run

INFO [testacular]: Testacular server started at http://localhost:8081/
INFO [launcher]: Starting browser PhantomJS
INFO [PhantomJS 1.8 (Linux)]: Connected on socket id pQeU7945i4k148Z0K_lr
PhantomJS 1.8 (Linux) Application ApplicationController should be available FAILED
        TypeError: 'undefined' is not an object (evaluating '$log.info.logs.push')
            at /home/lrvick/testapp/app/lib/bower-angular-mocks-latest/angular-mocks.js:306
            at /home/lrvick/testapp/app/js/logging.js:21
            at /home/lrvick/testapp/app/js/controllers/ApplicationController.js:11
            at invoke (/home/lrvick/testapp/app/lib/angular/index.js:2871)
            at instantiate (/home/lrvick/testapp/app/lib/angular/index.js:2881)
            at /home/lrvick/testapp/app/lib/angular/index.js:4770
            at /home/lrvick/testapp/test/spec/controllers/main.js:12
            at invoke (/home/lrvick/testapp/app/lib/angular/index.js:2871)
            at workFn (/home/lrvick/testapp/app/lib/bower-angular-mocks-latest/angular-mocks.js:1783)

Jaime J. Febres Velez

unread,
Oct 11, 2013, 12:27:32 AM10/11/13
to ang...@googlegroups.com
I am experiencing this very same issue when trying to enrich the log service using a decorator, did you manage to find a workaround for this?

Mike Alsup

unread,
Oct 11, 2013, 11:56:04 AM10/11/13
to ang...@googlegroups.com
angular-mocks adds a logs property to each logging method and expects that prop to be there later when you use the method.  So not only do you need to replace the original logging method, you also need to add the property that angular-mocks expects.  Here's a working example:

angular.module('logger', []).config(function($provide) {
    $provide.decorator('$log', function($delegate) {
        // decorate all the common logging methods
        ['log', 'debug', 'info', 'warn', 'error'].forEach(function(o, i) {
            $delegate[o] = docorateLogger($delegate[o]);
            $delegate[o].logs = []; // this keeps angular-mocks happy
        });
        return $delegate;

        function docorateLogger(originalFn) {
            return function() {
                var args = Array.prototype.slice.call(arguments);
                // push date into message
                args.unshift(new Date().toISOString());
                originalFn.apply(null, args);
            };
        }
    });
});








ThomasBurleson

unread,
Oct 11, 2013, 3:31:41 PM10/11/13
to ang...@googlegroups.com
You guys should check out this 

ThomasBurleson

unread,
Oct 11, 2013, 3:33:54 PM10/11/13
to ang...@googlegroups.com
Btw, the LogDecorator accounts for the angular-mocks issue... so it works with both!

Mike Alsup

unread,
Oct 11, 2013, 7:35:47 PM10/11/13
to ang...@googlegroups.com
That article is a great tutorial on how to use decorators.  It's a bit too verbose for my liking, but still rock solid.  Although, I do not see where it is accounting for the mocks issue described above.


ThomasBurleson

unread,
Oct 12, 2013, 8:28:17 AM10/12/13
to ang...@googlegroups.com
Reply all
Reply to author
Forward
0 new messages