; last updated - 3 minutes read

Like countless other developers, I decided to monkey-patch console.log to implement the logger of our Angular application. You may or may not like this approach, but it worked flawlessly. The first time it broke was when we deployed it to production today.

What had happened?

After a while, we observed that the application works fine if we ran it after opening the developer tools. Closing the developer tools made the application crash again.

Internet Explorer 8 and 9

A quick research in the internet showed a possible explanation. console.log is meant to be debug code. So the Microsoft developers considered it a good idea to create the console object only if the developer tools are open. Close them, and the entire console object is undefined again.

As a remedy, you can use this polyfill:

if(!window.console) { var console = { log : function(){}, warn : function(){}, error : function(){}, time : function(){}, timeEnd : function(){} } }

Excursion: Other browsers

It's a good idea to always add this polyfill. The console object isn't part of JavaScript. Instead, it's an extension of the DOM. Most browsers implement it. Node.js implements it, too. But there's no obligation to do so. There are a few browsers that don't. For instance, there's no console on Blackberry, Opera Mini and UC for Android.

Internet Explorer 10, 11, and Edge

Unfortunately, this polyfill didn't make any difference.

Since version 10, Internet Explorer always has a console object. If the developer tools are closed, calls to the console object are simply ignored. You can even monkey-patch the methods of the console object:

var JL = require('jsnlog').JL; console.log = JL().info;

You can even show the source code of console.log, console.error, console.warn and so on:

alert(console.info);

In IE11, this shows a native function if the developer tools are closed and a JavaScript function if they are open. The Edge browser always shows a native method.

Now here's the catch. You can copy the method to a variable, but you cannot use the variable to call the method. So the traditional approach of Monkey Patching (store the old function, intercept the function by overriding it and call the original code at the end of the overriding function) doesn't work. Quite the contrary, it results in an exception:

alert(console.info); // shows "native function" const original = console.info; alert(original); // shows "native function" console.info("You ain't gonna see me, but never mind"); // silently ignored original("I'm gonna crash in production!"); // may result in an exception

Wrapping it up

The annoying thing about this bug is that it doesn't leave any traces. Internet Explorer and Edge assume you don't need any logs in production. From a performance perspective, that's a great idea, but other than that, it's annoying. The error message disappears into Nirvana without further ado.

So either you follow the advice of most experienced JavaScript programmers: avoid Monkey Patching, and use a dedicated logger class instead. Or you redirect the console messages without logging them redundantly to the console object of the developer tools. That's what we did: Now every log message is sent to the server, using the awesome JSNLog library.


Comments