patternjavascriptMinor
Wrapping around window.console
Viewed 0 times
aroundwindowconsolewrapping
Problem
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] = conso
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] = conso
Solution
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
I will keep the exercise to make this work with AMD/UMD in your capable hands ;)
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 ;)
Code Snippets
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 );Context
StackExchange Code Review Q#70403, answer score: 4
Revisions (0)
No revisions yet.