Wrapping around window.console I'm working on an old application written in JavaScript. It's absolutely littered with console.* messages some of which are useful for development purposes.

At the very least I'd like to switch these off when a production flag is set. I would also like to stub off any unsupported console methods (trace on IE for example). Note: Assume that we will always have ES5 support.

Concerns:


I call into the initializer as soon as the constructor is instantiated. I like the idea of having a separate initializer method because it can be swapped out. I don't however like the idea that subsequent calls can be made. Any thoughts on this? I've seen it used a lot in Backbone's source.
As you can see I've tried to keep the production flag separate from the general 'enabled' flag. When in production I noop all of the methods instead of wrapping them (or nooping them if unsupported). Perhaps there's a better way of catching all calls to console (like a jasmine style spy) in production?
My hope with wrapping each supported method was to have a way of injecting pre-conditions/formatting before it's fired off. I also use this as an opportunity to check flags like enable. Is this over-contrived?




var Logger = function () {
    this._methods = [
        'assert', 'clear', 'count', 'debug', 'dir', 'dirxml',
        'error', 'exception', 'group', 'groupCollapsed', 'groupEnd',
        'info', 'log', 'markTimeline', 'profile', 'profiles',
        'profileEnd', 'show', 'table', 'time', 'timeEnd',
        'timeline', 'timelineEnd', 'timeStamp', 'trace', 'warn'
    ];

    this.initialize.apply(this, arguments);
};

var console = window.console, p = Logger.prototype;

p.initialize = function (options) {
    var noop = function () {};

    this.options = options || {};

    this._enabled = !this.options.production;

    this._methods.forEach(function (method) {
        if (this.options.production) {
            this[method] = noop;
        } else {
            this[method] = console[method] ? this._wrap(method) : noop;
        }
    }.bind(this));
};

p._wrap = function (method) {
    return function () {
        if (this._enabled) {
            return console[method].apply(console, arguments);
        }

        return false;
    };
};

p.enable = function () {
    if (!this.options.production) {
        this._enabled = true;
    }
};

p.disable = function () {
    this._enabled = false;
};
Interesting question,

I spent a bit of time coding different approaches before writing this. Initially I just was going to point to my SO answer but then I saw you have some more requirements that are not met.

Concern #1

I dislike libraries where I have to initialize stuff outside of the constructor, the constructor should initialize an object so that it is usable! Why give me a half baked object that I cannot use? The idea of being able to swap out a separate initializer is typical YAGNI.

Concern #2

In my mind, the caller should decide whether to enable or disable (possibly based on a production flag or not), for your code to take care of both concerns independantly seems overkill.

Concern #3

It seems over-contrived to me ;)

I did like though the idea of fool-proofing all console methods for an ancient legacy code base, and kept that with 2 simple methods to turn of or on all output in my approach:

function logController(){

  var methods = [
    'assert', 'clear', 'count', 'debug', 'dir', 'dirxml',
    'error', 'exception', 'group', 'groupCollapsed', 'groupEnd',
    'info', 'log', 'markTimeline', 'profile', 'profiles',
    'profileEnd', 'show', 'table', 'time', 'timeEnd',
    'timeline', 'timelineEnd', 'timeStamp', 'trace', 'warn'
  ];
  //The one acceptable time we can pollute the global namespace ;]
  console = console || {};

  function stop(){
    var _method;
    methods.forEach( function (method){
      _method = '_' + method;
      console[_method] = console[_method] || console[method];  
      console[ method] = function noLog(){};
    });
  }

  function start(){
    var _method;
    methods.forEach( function (method){
      _method = '_' + method;
      console[method] = console[_method] || console[method] || function noLog(){};
    });    
  }
  start();

  return {
    start: start,
    stop: stop
  };
}

flow = logController();
console.log( 'Normal logging, next is 123' );
console.log(  123 );
console.log( 'Start logging (which is default state), next is 123' );
flow.start();
console.log(  123 );
console.log( 'Stop logging (which is default state), next is "start logging again"' );
flow.stop();
console.log(  123 );
flow.start();
console.log( 'Started logging (which is default state), next is 123' );
console.log(  123 );


I will keep the exercise to make this work with AMD/UMD in your capable hands ;)