9

I have wrapped the console API to provide granular logging levels as well as few other sugar features.

This works fine, the only problem is that firebug (or whatever other console) will always report the line number the log came from as the line the console API itself is invoked.

How would you suggest I make the console log the line number at which I call my wrapper function?

I would prefer a cross browser solution but failing that a firebug plugin could be a good start.

fyi I call my loging function like so:

db.log(db.LogLevel.WARN, "Blah Blah Blah");
Ollie Edwards
  • 14,042
  • 7
  • 28
  • 36
  • It would be better if you can explain what you are expecting elaborately. Do you want to get the line numbers of the invocation of you function? – Livingston Samuel Dec 02 '10 at 14:09

5 Answers5

3

Interesting problem... I may have a hack for you. I can't test this right now, but I think it might work.

We know that a regular function call won't work, so I started thinking about #defines in C and macros in various other languages. Unfortunately, javascript doesn't have this, but perhaps an eval hack will work. I'm expecting that eval will run the code as if it came from the same line - if not, bleh, ignore the rest of this answer.

My method works like this:

  1. Change your db.log function to point to eval (yes, ew)
  2. Instead of passing in your LogLevels as an argument, create functions for each of them that returns a string with console.log in it and a custom message.

It should look something like this:

db = {LogLevel: {}};
db.log = eval;
db.LogLevel.warn = function(message) {
   return "console.log('THIS IS A WARNING: " + message + "');";
};

You should be able to call it like this now:

db.log(db.LogLevel.warn("Blah blah blah"));
David Tang
  • 92,262
  • 30
  • 167
  • 149
  • Interesting idea, great thinking out of the box and almost brilliant. It falls down in that most browsers won't let you alias eval as a security feature. In IE it works as described. Calling eval in place could be a workaround, though I think this would start to erode the usefulness of wrapping the console in the first place. Great effort though. – Ollie Edwards Dec 14 '10 at 09:22
  • 1
    @Ollie Edwards - my fault for not testing it properly! I checked today though, in firefox and chrome, and while you're right that there are security restrictions, it appears that it is only if you change the **context** of `eval`. Therefore if you don't assign `log` to the `db` object (just create it globally), you can alias `eval` just fine. – David Tang Dec 15 '10 at 01:15
1
//trust me, this way rocks!  Auto prepend a logHead, yet keep correct line number displayed debug view.
//Output sample:
//  5/10 1:13:52.553  hi                                    a.js:100
//  5/10 1:13:52.553  err                                   b.js:200

    var Log = {
        debug : true,

        /*
         * log.d(logData1, logData2, ...)
         *  --> console.log( getLogHead(), logData1, logData2, ...)
         * 
         * @comment Using bind and property accesser
         * @see http://ejohn.org/blog/javascript-getters-and-setters/
         */
        get d() { 
            if ( !this.debug) return _emptyFunc;
            return console.log.bind( console, this._getLogHeader() );
        },

        /*
         * output error info
         */
        get e() { 
            return console.error.bind( console, this._getLogHeader() );
        },

        /**
         * get current time in 01/31 23:59:59.999 format
         */
        _getLogHeader : function () {

            var millisec = Date.now();
            this._dtNow.setTime( millisec );
            //toLocaleString is 2013/01/31 23:59:59
            return this._dtNow.toLocaleString().slice( 5 ) + '.' + ('000' + millisec).slice( -3 ) + ' ';
        },
        _dtNow: new Date(),
        _emptyFunc: function() {}
    };


    //enjoy it !
        Log.d('hi');
        Log.e('err');
osexp2000
  • 2,910
  • 30
  • 29
0

So this recently came up again so I decided to revisit it.

Now I'm older and wiser it's clear to me a much better solution then what I was trying to do is to call the console functions as they are but selectively replace them with dummy functions when the level is turned down. This gives me fine grained logging and accurate line number reporting. A few features have been lost from my previous solution but I think this is an acceptable compromise.

Here's a partial snip of my new logging lib which shows the main solution

...
levels : ["debug","info","warn","error"],

    init : function(minLevel) {
        var params = abm.getUrlParams();
        minLevel = params["debug"] || minLevel;

        //TODO: firebug lite
        window.console = window.console || {};

        var clear = false;
        for (var i=0; i<self.levels.length; i++) {
            var level = self.levels[i];
            originalFunctions[i] = originalFunctions[i] 
            || console[level] || fallback;

            if (level && (clear || level===minLevel)) {
                console[level] = originalFunctions[i];
                clear=true;
            } else {
                console[level] = suppressed(level);
            }
        }

    }
...

You can see the full thing here: https://github.com/antiBaconMachine/abm-log

Ollie Edwards
  • 14,042
  • 7
  • 28
  • 36
0

Here are two ways to wrap logging without losing context. The first is a little bit ugly from the caller's side. The second is only usable if you don't need the details of what was logged.

See the JSFiddle for a demo: http://jsfiddle.net/epQ95/1/

// logger method 1: allows for fully functional log-wrapping without losing context,
//                  but, it is very ugly from the caller's perspective.
var debug = function () {
    // do my extra special stuff
    window.console.log("logging to server 1: ", arguments);

    // do regular console logging, if possible
    if (window.console && window.console.log) {
        return window.console.log.apply.bind(window.console.log, window.console, arguments);
    } else {
        return function () {};
    }
};

// caller
debug("logger method", 1)();

// logger method 2: pretty calling, but you don't know what was logged,
//                  just that something definitely was.
var Logger = {};
Logger.__defineGetter__("debug", function () {
    // do my extra special stuff
    window.console.log("logging to server 2: don't know what was logged");

    // do regular console logging, if possible
    if (window.console && window.console.log) {
        return console.log.bind(window.console);
    } else {
        return function () {};
    }
});

// caller
Logger.debug("logger method", 2);
0

Typically using the debug() or error() instead of log() functions will cause line numbers to be displayed. I believe the Google Chrome console works similarly. (firebug reference)

Anthony Corbelli
  • 877
  • 4
  • 10
  • 1
    It's not a question of displaying line numbers, it's a question of displaying the RIGHT line number. All log functions display the line number at which the log function was called, in my case as I have wrapped this function it always displays the same line number. – Ollie Edwards Nov 09 '10 at 11:58
  • You're starting to leave the bounds of javascript capabilities - may I ask what you're looking to do? Troubleshoot? Debug? – Anthony Corbelli Nov 09 '10 at 18:39