angularjs $log - show line number angularjs $log - show line number google-chrome google-chrome

angularjs $log - show line number


In Chrome there is a feature called Blackboxing.You can use it to exclude / bypass (library) sources from your debug sessions or development workflow.

So if you blackbox angular the internals of the $log service get bypassed and the console prints the correct line number!

https://developer.chrome.com/devtools/docs/blackboxing


You can access it by applying a decorator to the $log service:

module.config(function logConfig($provide, $logProvider) {    $provide.decorator('$log', function ($delegate) {        var originalFns = {};        // Store the original log functions        angular.forEach($delegate, function (originalFunction, functionName) {            originalFns[functionName] = originalFunction;        });        var functionsToDecorate = ['debug', 'warn'];        // Apply the decorations        angular.forEach(functionsToDecorate, function (functionName) {            $delegate[functionName] = logDecorator(originalFns[functionName]);        });        return $delegate;    });    function logDecorator(fn) {        return function () {            var args = [].slice.call(arguments);            // Insert a separator between the existing log message(s) and what we're adding.            args.push(' - ');            // Use (instance of Error)'s stack to get the current line.            var stack = (new Error()).stack.split('\n').slice(1);            // Throw away the first item because it is the `$log.fn()` function,             // but we want the code that called `$log.fn()`.            stack.shift();            // We only want the top line, thanks.            stack = stack.slice(1, 2);            // Put it on the args stack.            args.push(stack);            // Call the original function with the new args.            fn.apply(fn, args);        };    }});

I do this as an includable module, but I believe it could be done within the app's .config() as well.

I built this (along with some additional logic) by gluing together a number of different sources online; I'm usually really good at keeping references to them, but I guess I didn't when I built this, so unfortunately I can't reference my inspiration. If someone replies with it, I'll put it in here.

NOTE 1: this is a slightly stripped-down version of what I actually use, so you'll have to double-check the logDecorator()s stack pushy-shifty magic, though it should work as presented.

NOTE B:MDN says that Error.prototype.stack is non-standard (requires IE10 and may not be supported on many mobile browsers) so you might want to look at augmenting this with something like stacktracejs to get the stack itself.


I have combined a couple of solutions from this page, as well others to build a simple demo in JSFiddle - to demonstrate use of $log service, enhancing it with decorators to add line number (line number from where $log call was made). I have also made a slightly more comprehensive solution in Plunker, demonstrating the use of $log service, enhancing it with decorators to add line number, caller file name and instance name. Hopefully, this will be useful to others.

JSFiddle URL - https://jsfiddle.net/abhatia/6qnz0frh/

This fiddle has been tested with following browsers:

  • IE 11 - (JSFiddle Javascript's first line's number is 72).
  • Firefox 46.0.1 - (JSFiddle Javascript's first line's number is 72).
  • Chrome 50.0.2661.94 m - (JSFiddle Javscript's first line's number is 71).

The results are good. But, please note that line number in Chrome will be off by 1, when compared to FF or IE, i.e. because JSFiddle's javascript's code first line number differs between FF/IE and Chrome, as listed above.

Plunker URL - https://embed.plnkr.co/YcfJ7V/

This plunk demonstrates the concept really well, with detailed explanation and also provides the console output with Angular's official example of default $log service, so the two could be contrasted. Plunk has also been tested with browsers listed above.

Below screenshot is the console output from the Plunk example above. There are 3 highlighted areas:

  • Red box shows console output using default $log service. $log functions invoked from controller.
  • Blue box shows console output using extended $log service. $log functions invoked from controller. You can see how the script name and line numbers are shown, as well as the controller name (used when instantiating $log).
  • Orange box contrasts console output from default and extend $log services.

Console log from plunker output

This will become very clear when you review the Plunk code.

Here is the getLineNumber function used in JSFiddle (slightly enhanced version is used Plunker example to return caller file name):

function getLineNumber(newErr, sliceIndex1, sliceIndex2){  var lineNumber = -1;  var lineLocation;  var stack = newErr.stack.split('\n').slice(2);  if (navigator.userAgent.indexOf("Chrome") > -1) {    stack.shift();  }  stack = stack.slice(sliceIndex1, sliceIndex2);  var stackInString = stack + '';  var splitStack;  if (navigator.userAgent.indexOf("Chrome") > -1) {    splitStack = stackInString.split(" ");  }  else {    splitStack = stackInString.split("@");  }  lineLocation = splitStack[splitStack.length - 1];   //console.log(lineLocation);  lineNumber = lineLocation.split(":")[2];  return lineNumber; }