Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

angularjs $log - show line number

I use angularjs $log in chrome, but it shows the line like: angular.js:9037. I want to show the line number where I call this method. (Show my js name and the correct line). Does anyone know how to do it? Angular doesn't have this feature.

like image 906
Awakening Avatar asked Dec 23 '13 07:12

Awakening


4 Answers

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

like image 144
Rob Avatar answered Nov 20 '22 15:11

Rob


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.

like image 33
floatingLomas Avatar answered Nov 20 '22 17:11

floatingLomas


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; 
}
like image 2
abhishek Avatar answered Nov 20 '22 17:11

abhishek


The line number comes from the runtime. You can not set it in general case.

But not all is lost. In places where the line number is really important you can use a different call. Remember to inject the $window and then:

$window.console.log("test1");

You loose some things this way like formatting, cross browsers filler code etc, but you do get line numbers correct for free without any per runtime specific code to do so.

like image 1
stride Avatar answered Nov 20 '22 15:11

stride