I ran across something the other day at work, and figured it would be good for a quick little blog post. Raise your hand if you’re accustomed to using console.log() (or the other more exotic console{} methods) in your development process. Now raise your hand if you’ve ever left that code in, either intentionally or not, when your code was packaged/built for production.
My first exposure to this phenomenon was actually back in the days when I wrote Actionscript pretty regularly. The analog to console.log() in Flash is trace(). As you would work your way through complicated debugging, code tended to get very littered with crazy trace() statements all over the place. Now, it’d be great if we were all disciplined enough to clean up our code before pushing to production, but let’s be honest — many times we just don’t.
The thing about trace() is that the Flash IDE has an option called “Omit trace statements” which prevents these statements from being included in the SWF. The problem of course was that with this option defaulted to on, even your testing SWF in the IDE suppressed these helpful debugging messages. So you had to remember to turn it on when you were ready to push your SWF out to the real world. But if you did, things worked well, because your SWF was properly stripped of such needless baggage.
For many of us, however, we don’t have IDE’s that “build” and “compile” our JavaScript before we push to production, and even if we do, I’ve not heard of many of them removing console.log() statements. In fact, this actually seems like a perfect feature for minifiers/compressors, but alas, as far as I know (could be mistaken?) I am not aware of any that do so.
So what?
What’s the big deal about leaving trace() and console.log() statements in production code? First, as @zoompf has noted, there’s run-time performance to consider. A single console.log() statement in your code is unlikely to ever slow down your page any noticeable amount to your users. But if you have a console.log() statement inside a for-loop that does animation, and a user has Firebug installed and turned on, they’ll probably see a little lag in that animation loop as all those unnecessary messages are sent to the console.
Next, there’s something to be said for how console.log() statements can, under certain circumstances, make potential hack/security holes or “private” information in your code base more obvious to spying eyes. Consider for instance a console.log() statement that echoes out a randomly generated session-id or the response from a cross-domain JSON-P call or an XHR call. Those are generally pieces of data that would be a bit harder for someone to get their hands on, but if you use console.log() to echo them out, all the user has to do is turn on Firebug and boom, they’ll see those messages.
There’s actually quite a few other scenarios (albeit slightly fringe) under which we could assert that console.log() statements are perhaps not the best idea for production code. But let’s not dive too deep down that rabbit hole for now. I’ll leave such hackery as an exercise for the reader.
Then of course is the concern that not all browsers have a console{} object available to the page’s JavaScript. I’d wager most people who run Firefox have Firebug installed, so they do. Chrome and Safari seem to ship with them defined by default (even if you don’t have them open).
Opera on the other hand, to the best of my knowledge, has no such API defined, at least not by default. So console{} code in Opera will likely cause a JS error. The same is generally true of IE. I happen to run IE installed with the Companion.JS plugin installed, so that’s not true for me specifically, but most IE users don’t have a console.
Surprised
You may be surprised to see that console.log() makes it into the wild, even on some of the biggest sites on the internet. I just cruised through several dozen of the Alexa Top 100 with my Firebug open and just watched as the log messages flew by.
Admittedly, most of what I saw was just lots of Ajax GET/POST requests. But there are several big sites which still let console.log() statements show up in their production code. Some of the sites I found console{} usage on include Twitter, Facebook (especially the new Facebook “like” button on other sites), Linked In, ESPN, CNN, and CNET.
I saw a direct correlation between sites that were heavy on Ajax usage and the propensity for console{} statements to be used, which makes sense (the more sophisticated the site, the more likely it is that developers need to debug it!). It was also more likely that console was used in the profile or login sections of those sites, which arguably don’t get much usage compared to the rest of the site. But the troublesome thing is that there may be an easy temptation to console.log() something very sensitive, like console.log(password_entered).
By contrast, sites like Ebay and IMDB are amazingly low on Ajax’iness compared to sites like Twitter, and thus I was pleasantly surprised to not run across any such usage there.
Now, I doubt that any of the sites I mentioned are actually exposing anything of any importance via console{} statements. For instance, ESPN has “touch is not enabled” as the first console message when you visit the site. Clearly they’re testing for browser capability and just forgot to remove the log of that message. But the fact that they obviously have a process in place which does not properly filter out such cruft means that at some point they may accidentally expose something they would otherwise not want to (or their users wouldn’t want!).
Fix the console
So, we all agree we need to use console{} statements as a helpful tool in development/debugging. But I think it’s clear that such usage in production code is probably not the best idea. How do we resolve this conflict?
The best idea is to use a build process of some sort that safely strips out all your console{} statements. But, until such a time as we all have access to that (like all the minifiers/compressors do it for us), we need another option.
I’m sure thousands of developers/sites have attempted to solve these issues themselves in various ways. For instance, the other day I was made aware of @cowboy‘s (Ben Alman) great JavaScript Debug tool. Ben’s tool takes the approach of wrapping console{} with another object and adding a bunch of logic and usefulness to it. To take advantage, you have to include this tool, and use it instead of bare bones console{} statements. If you’re looking for a more full featured console{}, this is a great place to start.
I however often need a more stripped down and simple approach. I don’t need bells and whistles, I just need to be able to:
- reliably use console{} statements in development environments
- silence the console{} message logging in production environments automatically
- normalize different console{} API’s (yeah, they don’t all agree!) across browsers
- prevent errors if the host browser doesn’t support console{}
Here’s one simple way to accomplish those goals:
console.js:
(function(global){
var prod = global.location.href.match(/^http:\/\/(www\.)?mysite.com/i) !== null,
api = ["log","debug","info","warn","error","assert","dir","dirxml",
"trace","group","groupCollapsed","groupEnd","time","timeEnd",
"profile","profileEnd","count","exception","table"],
log, i, len
;
if (typeof global.console == "undefined" || !global.console) {
try { global.console = {}; } catch (err) { }
}
log = (!prod && typeof global.console.log != "undefined") ?
global.console.log :
function(){}
;
for (i=0, len=api.length; i<len; i++) {
if (prod || typeof global.console[api[i]] == "undefined" ||
!global.console[api[i]])
{
try { global.console[api[i]] = log; } catch (err) { }
}
}
})(window);
Note: make sure to adjust the regular expression so it matches the production URL/domain of your site. For most people, this should be a handy distinction between their code running in production and running in a dev environment on some other domain.
If that’s not true for you, you can devise a slightly different flag check, such as including a boolean global variable/constant called “DEVELOPMENT” only in your dev environment, and then testing for that. In either case, make it as easy and dummy-proof as possible to have this console{} masking code switch between dev and prod behavior automatically. That’s key.
console.finish()
Again, this is just one of a million different ways to approach this issue. But I think this basic code does a good job of the bare minimum normalization to help make console{} usage more reliable, more performant, and possibly even more secure. Whatever you do, take care when you let your console{} statements out into the wild. You never know what they might be capable of without your supervision.


Great work Kyle! We have added a performance check for this, #338 “JavaScript Debugging Function (console.log),” to Zoompf’s web performance scanner.
It’s funny you mention ActionScript 2 because it should how other web technologies address this issue. I always loved that “remove trace() statements” feature. It reinforces that you are executing a publishing process and as part of the process code needed only during the development phase get stripped. Leaving debugging information in Flash objects using ActionScript 3 is even worse than AS2 since AS3 compiles to an assembly language stored in the SWF. If you leave on debugging, the assembler has to add a ton of instructions and meta data to correlate AVM instructions with the original line of source.
nice find!
Billy
Dojo has shipped a version of Firebug Lite for ages, so I am very accustomed to that, however, the build system strips console.*() calls with a simple arg (stripConsole=all) so no, this has never been a problem really. That build can additionally strip based on #ifdef like pragmas, so you have the option of removing not only a console.log() call, but an entire block of verbose debugging/error inspecting code before deployment, too.
Thanks for pointing that out, Pete. I am less familiar with Dojo, sounds like you guys have this stuff well covered over there. Hopefully others will take the hint, too.
For reference, Opera support console.log, warn and error, but not the other APIs.
Also why are you try-catching that stuff. Has it ever thrown anywhere?
And this is redundant/unnecessary:
– typeof global.console == “undefined” || !global.console
Good ideas though. I always use a single point for debugging so I can turn all the debug off at once.
function debug(msg) { // return; // do stuff here }I’ve had many times where I had JS errors thrown when I tried to use console.log() in Opera. I’ll put up a simple test case showing so.
The try/catch may not be strictly necessary, but I’ve found that “console” can be kind of weird. Depending on how it’s implemented in a browser, it may be some sort of special host object (or ActiveX object in IE) and in some cases it may not respond well to being overwritten.
Actually, the dual check is unfortunately necessary for IE6. The reason is, in IE6, you can’t do:
var a = {}; if (!a.b) { a.b = "c"; }This throws an error in IE6 but works fine in other browsers. So you have to check for undefined. And the reason you also need to check for ! is because simply being defined doesn’t mean that it wasn’t set to something like false (yes, I’ve seen this before). So I do a basic check to make sure it’s some sort of truthy value (not null, not false) that can at least be assigned properties.