Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I get $log to log the source code location of my call, instead of theirs?

Tags:

angularjs

Here's something typical resulting from using $log:

If I would just use console.log, I would see the source code location of my call in the console. When using $log, I see the location of their log call, which is useless to me.

Is there any way to get a more useful result?

like image 908
Oliver Salzburg Avatar asked Apr 07 '15 12:04

Oliver Salzburg


1 Answers

We just dropped using $log completely. It really doesn't provide any benefit that we could see.

Instead, we're now simply using console, but with a small twist:

/**
 * @see https://github.com/h5bp/html5-boilerplate/blob/master/src/js/plugins.js
 */

var method;

var methods = [
    "assert", "clear", "count", "debug", "dir", "dirxml", "error",
    "exception", "group", "groupCollapsed", "groupEnd", "info", "log",
    "markTimeline", "profile", "profileEnd", "table", "time", "timeEnd",
    "timeStamp", "trace", "warn"
];
var methodsLength  = methods.length;
var console = ( window.console || {} );

while( methodsLength-- ) {
    method = methods[ methodsLength ];

    // Only stub undefined methods.
    if( !console[ method ] ) {
        console[ method ] = Function.prototype;
    }
}

angular
    .module( "util" )
    .constant( "console", console );

We're not overriding window.console as the original html5-boilerplate code does. Instead, we're just creating a new service and inject that whenever we want to use console.


Original answer

I'm not quite sure if this is where I want to go with the issue, but @BenjaminGruenbaum suggested to just use decorators and change how $log works.

This is what it looks like. And Chrome apparently even recognizes the pattern and I can click right through to the source code location.

enter image description here

So here is my proof-of-concept code:

var app = angular.module( "myApp", []);

app.config( function( $provide ) {
  $provide.decorator( "$log", function( $delegate ) {
    var originalLog = $delegate.log;
    $delegate.log = function() {
      var stack = new Error().stack;
      var location = analyzeStack( stack, 1 );
      [].push.call( arguments, location );
      originalLog.apply( this, arguments );
    };
    return $delegate;
  } );
} );

app.controller( "ApplicationController", function( $log ) {
  $log.log( "Hello World" );
} );

/**
 * Take a stack trace and extract a location identifier from it.
 * The location identifier represents the location from where the logger was invoked.
 * @param {String} stack The traced stack
 * @param {Number} [stackIndex=2] The element of the stack you want analyzed.
 * @returns {String} A location identifier for the location from where the logger was invoked.
 */
function analyzeStack( stack, stackIndex ) {
    stackIndex = stackIndex || 2;

    /**
     * Group 1: Function name (optional)
     * Group 2: File name
     * Group 3: Line
     * Group 4: Column
     */
    var callSitePattern = new RegExp( /at (?:(.*) )?\(?(.*):(\d+):(\d+)\)?/g );
    var sites = stack.match( callSitePattern );

    // The method that invoked the logger is located at index 2 of the stack
    if( sites && stackIndex <= sites.length ) {
        var callSiteElementPattern = new RegExp( /at (?:(.*) )?\(?(.*):(\d+):(\d+)\)?/ );
        // Pick apart
        var callSiteElements = sites[ stackIndex ].match( callSiteElementPattern );
        var functionName, fileName, line, column;
        // Assume either 4 (no function name) or 5 elements.
        if( callSiteElements.length == 5 ) {
            functionName = callSiteElements[ 1 ];
            fileName = callSiteElements[ 2 ];
            line = callSiteElements[ 3 ];
            column = callSiteElements[ 4 ];
        } else {
            functionName = "(unnamed)";
            fileName = callSiteElements[ 1 ];
            line = callSiteElements[ 2 ];
            column = callSiteElements[ 3 ];
        }

        return functionName + "@" + fileName + ":" + line + ":" + column;
    }
    return null;
};

I've also put it into a plunkr.

like image 200
Oliver Salzburg Avatar answered Oct 23 '22 02:10

Oliver Salzburg