Suppose I want to include some calls to console.log for some legitimate production reason, say for something like a unit test harness. Obviously I would not want this to throw a premature exception if the browser doesn't have a console, or if no console is present.
What's the best way to create a simple log function to log stuff to the console, or silently fail without error if no console is present?
The accepted answer to the question linked above:
var log = Function.prototype.bind.call(console.log, console);
log.apply(console, ["this", "is", "a", "test"]);
Can this log function be called normally on IE, and the use of apply here is just to show it's possible? And, I assume from the linked question that this will fail if IE's console is closed when it runs, so log won't work even after the console opens, correct? If that's wrong, can someone explain how it works?
This ycombinator article seems relevant. Are they are talking about the same IE behavior as the question linked above?
Function.prototype.apply.apply(console.log, [console, arguments]);
Works both on IE9 broken console.log, and regular console.log from other vendors.
Same hack as using Array.prototype.slice to convert arguments into a real array.
This works nicely in my chrome console.
function echo(){
Function.prototype.apply.apply(console.log, [console, arguments]);
}
Simplified:
function echo(){
Function.apply.call(console.log, console, arguments);
}
Add a check and return:
function echo(){
return window.console && console.log &&
Function.apply.call(console.log, console, arguments);
}
The example above looks adequate to me. I don't have IE on hand to test it, though. Is this a reasonable approach for safely wrapping console.log?
More questions
Following the link in nav's answer below, we see the code:
Function.prototype.call.call(console.log, console,
Array.prototype.slice.call(arguments));
What is the purpose of converting arguments to an array in this case? I guess it must fail in some browser if you don't do this? And, opera weird behavior and console-less browsers aside, shouldn't something like this pretty much work for every other browser as well? And does prototype serve a purpose in the above examples, or are we just being pedantic... Function.call.call or Object.call.call or for that matter isNaN.call.call seem to work just as well as Function.prototype.call.call.
The problem with wrappers is that they will obfuscate file name and line number of the source of the log message.
Simple IE7 and below shim that preserves Line Numbering for other browsers:
/* console shim*/
(function () {
var f = function () {};
if (!window.console) {
window.console = {
log:f, info:f, warn:f, debug:f, error:f
};
}
}());
Sorry, there was a bug in my post. Don't know how I missed it.
The PROPER way to create a global console object, if it does not exist:
if (typeof console === "undefined"){
console={};
console.log = function(){
return;
}
}
Can this log function be called normally on IE, and the use of apply here is just to show it's possible?
Yes, and yes. That particular example was aimed squarely at the "is it a real function" part of the linked question.
And, I assume from the linked question that this will fail if IE's console is closed when it runs, so log won't work even after the console opens, correct?
Correct. As explained in my answer on that question, the console object is not exposed until the first time the developer tools are opened for a particular tab. Most developers use a console shim, in which case the Function#bind approach becomes a little obsolete because you may as well use the Function#apply.apply method.
What is the purpose of converting arguments to an array in this case?
There isn't one, it's redundant. Unless it's a custom log implementation, in which case the developer may have a reason to convert an arguments object to an array.
And does prototype serve a purpose in the above examples, or are we just being pedantic...
Well, yes and no. Some developer may have unwittingly changed Function.call to a custom function or value. Of course, they could break Function.prototype.call too, but this is far less likely to happen by accident.
I like to use:
'console' in window && console.log("Boom!");
It works in all browsers and is easy to understand.
Try using the code snippet below... (this is my preferred approach because it makes you independent of window.console)
var logger = (function (c) {
"use strict";
var m = {
log: function (a, t) {
if (!c) { return; /* return or call your custom function here */ }
var l = c.log,
f = t === undefined ? l : (this.__dict__[t] || l);
f.apply(c, a)
},
__dict__: {
"trace": c.trace,
"debug": c.debug,
"info": c.info,
"warn": c.warn,
"error": c.error
}
};
return {
trace: function () { m.log(arguments, "trace"); },
debug: function () { m.log(arguments, "debug"); },
info: function () { m.log(arguments, "info"); },
warn: function () { m.log(arguments, "warn"); },
error: function () { m.log(arguments, "error"); },
log: function () { m.log(arguments, undefined); }
};
}(window.console))
So you may now try these in your code and see the result
logger.info("Hello");
logger.trace("Hello");
logger.debug("Hello");
logger.warn("Hello");
logger.error("Hello");
logger.log("Hello");
As a slight variation on Chris' answer, simply define 'log' as a property of 'console' with an empty function:
if (typeof console === "undefined") {
console = {
log: function () {
return;
}
};
}
Paul Irish has a nice light wrapper/replacement for console.log().
http://paulirish.com/2009/log-a-lightweight-wrapper-for-consolelog/
Advantages:
Prevent errors if a console isn’t around (i.e. IE)
Maintains a history of logs, so you can look in the past if your console is added afterwards (e.g. firebug lite)
Light & simple.
Very quick to type -- log() or window.log().
Use Consolation
My ridiculously overengineered console:
prevents errors if there's no console
prevents logging in production if you left console.log statements in your code
supports console.error, console.group, and all such other methods
still gives you backtraces to your log statements
It's amazing.
But really, you just shouldn't leave console statements lying around in your code.
Behold and tremble! Presenting: Consolation.js
coffeescript:
empty_function = ->
return
if !window.console?
window.console = {}
for fn in ['log', 'info', 'warn', 'debug', 'error']
if (typeof window.console[fn] isnt 'function')
window.console[fn] = empty_function
js:
(function() {
var empty_function, fn, i, len, ref;
empty_function = function() {};
if (window.console == null) {
window.console = {};
ref = ['log', 'info', 'warn', 'debug', 'error'];
for (i = 0, len = ref.length; i < len; i++) {
fn = ref[i];
if (typeof window.console[fn] !== 'function') {
window.console[fn] = empty_function;
}
}
}
}).call(this);
My solution is a little different. I create a standard shortcut for all console.log calls: In my case kag(whatever I want to report in the console).
I test for IE, if IE I send the results to an alert box. If Chrome then displays in the console. This also means IE will always work even in console is closed:
Code:
var iever = getInternetExplorerVersion();
if(iever>-1){
function kag(params){
alert(params);
}
} else {
var kag = console.log.bind(console, "REPORT: ");
}
function getInternetExplorerVersion() {
var rv = -1;
if (navigator.appName == 'Microsoft Internet Explorer') {
var ua = navigator.userAgent;
var re = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})");
if (re.exec(ua) != null){
rv = parseFloat( RegExp.$1 );
}
}
return rv;
}
Related
Excerpt of my Chrome DevTools Console:
Navigated to https://twitter.com/
console.log.toString();
"function (){}"
Navigated to http://linuxfr.org/
console.log.toString();
"function log() { [native code] }"
I managed to inject code in the main frame of twitter.com, using a Chrome Extension, but I had a hard time understanding why my code seemed to not run. It appears that my code is running fine, except console.log produces exactly nothing!
Question: is there a way to call the "now gone" console.log "native code"?
(remarks about that kind of JavaScript "WAT" behavior retracted, sort of)
As this is a Chrome Extension, you set your content script to run_at: "document_start" - you can(theoretically) "grab" console.log / error / dir / info etc before twitter gets it's grubby hands on them
actually, KNOWING that twitter only replaces log, warn, info, error, you can simply do:
var _console = ['log', 'warn', 'info', 'error'].reduce(function(result, key) {
result[key] = console[key].bind(console);
return result
}, {});
then you can use _console.log and friends
As far as I know, it depends on how deep replacement was done.
For example if it was like this
console.log = function () {};
The original native log function is still in prototype. You can access it using __proto__ property or Object.getPrototypeOf method.
console.log = null
Object.getPrototypeOf(console).log
//log() { [native code] }
Also you can just delete replacement from original console object
delete console.log
//true
console.log
//log() { [native code] }
But all above code won't work if I replace log function like this
console.__proto__.__proto__.log = function () {}
And unfortunately I don't know workaround for this case.
Based on the discussion above and #JaromandaX and JavaScript: The Definitive Guide below is one simple polyfill.
function log() {
if (location.hostname === 'twitter.com'){
Object.getPrototypeOf(console).log.apply(console, arguments);
} else {
console.log.apply(console, arguments);
}
}
I am a lazy developer... I like to make shortcuts, so I bound console.info to c
/*core_functions.js*/
/*==================================================
Bind C to be alert on mobile console.log in desktop
================================================== */
window.c = false;
if (typeof console === "object" && typeof console.error === "function" && !SP.isMobile) {
c = function (msg) {
console.info(msg);
};
} else {
c = function (msg) {
debug(msg);
};
}
/*somefile.js*/
c(anObject);
I have been using this for quite some time and something has always annoyed me. It displays the file in which the function is defined rather than where the function is called from:
core_functions.js:40 Object {stadiumname: "Stadium 3", pitch: Object, lights: Object, seats: Object, scoreboards: Object…}
Can I reference where the function is being called from? Or am I forever stuck with this minor annoyance?
You are seeing that line number because it really is where console.info is called. A way I can think to avoid that is by calling the real console method rather than proxying with an intermediate function.
It seems to me you want to have an abstraction layer to handle your logging using the native console or a custom one.
In that case you could try something like this:
var c = (typeof console === 'object') ? console : alternative;
And then use it as:
c.log('Hello World');
That way your alternative object could be one with the methods you are considering on using, for instance:
var alternative = {
log: function() {
window.alert.apply(window, arguments);
}
};
This way you won't get your c() function but, I think, you'll end up with a more flexible solution. By exposing the same interface that console has you could even add these logging facilities to any code running in your app (that is, 3rd party code using native console)
window.console = (typeof window.console === 'object') ? window.console : alternative
or if you are feeling very lazy that day :)
window.console = window.console || alternative
This is no perfect technique at all but if you are only using it while developing it might help.
Also, I never really participate in Stack Overflow so I'm very sorry if I'm ignoring any rule or etiquette :)
I use the following to get backtraces, you might be able to do something with it.
arguments.callee.caller.toString()
I have a JS script that works fine in all browsers.
But for everybody's surprise, in I.E. it does not work at the first try.
If, after I load my page, I press F12 (open the i.e. debugger) and refresh my page,
it works fine! Just like the others browsers! But for this work, i have to press F12.
Does i.e.'s debugger do something when we open it?
I cant find a solution!
Thanks in advance.
When you don't have the debugger open, IE considers there to be no such thing as console.log, and gives you errors for calling an undefined function. When you hit F12, then you get the console, and so console.log is no longer undefined.
You can workaround by putting this at the top of your code:
if (!window.console) window.console = {};
if (!window.console.log) window.console.log = function () { };
Rather than editing out console.log from your code, this will merely make the browser do nothing if the console doesn't exist, by defining them as a 'do nothing' function if they are undefined.
If you're looking to shrink down your js file size (particularly important for mobile usage), you will eventually want to remove verbose logging on your release version.
Do you have something like console.log() in your script? This might explain, since there is no console until you press F12
Extended version from previous post
if (!('console' in window)) {
var stub = function() { ; };
window.console = {
log : stub,
info : stub,
warn : stub,
error : stub,
assert : stub
};
}
I'm posting this new one that install stub only if needed
/**
* On IE console is not set if not opened and debug doesn't exists
*/
(function() {
if (!('console' in window)) { window.console = {}; }
var kind = ['log', 'info', 'warn', 'error', 'assert', 'debug'];
var stub = function() { ; };
for (var i = 0; i < kind.length; i++) {
if (kind[i] in window.console) { continue; }
window.console[kind[i]] = stub;
}
})();
I currently have this JS statement everywhere in my code:
window.console && console.log("Foo");
I am wondering if this is costly at all, or has any negative side-effects in production.
Am I free to leave client-side logging in, or should it go?
EDIT: In the end, I suppose the best argument I (and anyone else?) can come up with is that there is a possibly non-negligible amount of extra data transferred between the server and the client by leaving logging messages left in. If production code is to be fully optimized, logging will have to be removed to reduce the size of javascript being sent to the client.
Another way of dealing with this is to 'stub' out the console object when it isn't defined so no errors are thrown in contexts that do not have the console i.e.
if (!window.console) {
var noOp = function(){}; // no-op function
console = {
log: noOp,
warn: noOp,
error: noOp
}
}
you get the idea... there are a lot of functions defined on the various implementations of the console, so you could stub them all or just the ones you use (e.g. if you only ever use console.log and never used console.profile, console.time etc...)
This for me is a better alternative in development than adding conditionals in front of every call, or not using them.
see also: Is it a bad idea to leave "console.log()" calls in your producton JavaScript code?
You should not add development tools to a production page.
To answer the other question: The code cannot have a negative side-effect:
window.console will evaluate to false if console is not defined
console.log("Foo") will print the message to the console when it's defined (provided that the page does not overwrite console.log by a non-function).
UglifyJS2
If you are using this minifier, you can set drop_console option:
Pass true to discard calls to console.* functions
So I would suggest to leave console.log calls as they are for a most trickiest part of the codebase.
If minification is part of your build process, you may use it to strip out debug code, as explained here with Google closure compiler: Exclude debug JavaScript code during minification
if (DEBUG) {
console.log("Won't be logged if compiled with --define='DEBUG=false'")
}
If you compile with advanced optimizations, this code will even be identified as dead and removed entirely
Yes. console.log will throw an exception in browsers that don't have support for it (console object will not be found).
Generally yes, its not a great idea to expose log messages in your production code.
Ideally, you should remove such log messages with a build script before deployment; but many (most) people do not use a build process (including me).
Here's a short snippet of some code I've been using lately to solve this dilemma. It fixes errors caused by an undefined console in old IE, as well as disabling logging if in "development_mode".
// fn to add blank (noOp) function for all console methods
var addConsoleNoOp = function (window) {
var names = ["log", "debug", "info", "warn", "error",
"assert", "dir", "dirxml", "group", "groupEnd", "time",
"timeEnd", "count", "trace", "profile", "profileEnd"],
i, l = names.length,
noOp = function () {};
window.console = {};
for (i = 0; i < l; i = i + 1) {
window.console[names[i]] = noOp;
}
};
// call addConsoleNoOp() if console is undefined or if in production
if (!window.console || !window.development_mode) {
this.addConsoleNoOp(window);
}
I'm pretty sure I took much of the above addConsoleNoOp f'n from another answer on SO, but cannot find right now. I'll add a reference later if I find it.
edit: Not the post I was thinking of, but here's a similar approach: https://github.com/paulmillr/console-polyfill/blob/master/index.js
var AppLogger = (function () {
var debug = false;
var AppLogger = function (isDebug) {
debug = isDebug;
}
AppLogger.conlog = function (data) {
if (window.console && debug) {
console.log(data);
}
}
AppLogger.prototype = {
conlog: function (data) {
if (window.console && debug) {
console.log(data);
}
}
};
return AppLogger;
})();
Usage:
var debugMode=true;
var appLogger = new AppLogger(debugMode);
appLogger.conlog('test');
Don't overcomplicate things! I personally use console.log all the time during development, it's just such a timesaver. For production i just add a single line of code (in the "production profile" in my case) that disable all logs:
window.console.log = () => {};
done ;)
This monkey patches window.console and replace the log function with an empty function, disabling the output.
This is good enough for me in most cases. If you want to go "all the way" and remove console.logs from your code to decrease bundle size, you have to change the way your js is bundled (e.g. drop console.logs with minifier or something)
Also I think you CAN actually make a strong point for leaving them in - even in production. It doesn't change anything for a normal user but can really speed up understanding weird "exotic-browser" problems. It's not like it's backend-logs that may contain critical information. It's all on the frontend anyway, not showing a log message because you are scared to reveal something a user shouldn't know really is only "security by obscurity" and should make you think why this information is even available on the frontend in the first place. Just my opinion.
Yes, its good practice to use console.log for javascript debugging purpose, but it needs to be removed from the production server or if needed can be added on production server with some key points to be taken into consideration:
**var isDebugEnabled="Get boolean value from Configuration file to check whether debug is enabled or not".**
if (window.console && isDebugEnabled) {
console.log("Debug Message");
}
Above code block has to be used everywhere for logging in order to first verify whether the console is supported for the current browser and whether debug is enabled or not.
isDebugEnabled has to be set as true or false based on our
environment.
TL;DR
Idea: Logging objects precludes them from being Garbage Collected.
Details
If you pass objects to console.log then these objects are accessible by reference from console of DevTools. You may check it by logging object, mutating it and finding that old messages reflect later changes of the object.
If logs are too long old messages do get deleted in Chrome.
If logs are short then old messages are not removed, if these messages reference objects then these objects are not Garbage Collected.
It's just an idea: I checked points 1 and 2 but not 3.
Solution
If you want to keep logs for sake of client-side troubleshooting or other needs then:
['log', 'warn', 'error'].forEach( (meth) => {
const _meth = window.console[meth].bind(console);
window.console[meth] = function(...args) { _meth(...args.map((arg) => '' + arg)) }
});
If the workflow is done using the right tools such as parcel/webpack then it's no longer a headache, because with the production build console.log is being dropped. Even few years earlier with Gulp/Grunt it could've been automated as well.
Many of the modern frameworks such as Angular, React, Svelte, Vue.js come with that setup out-of-the-box. Basically, you don't have to do anything, as long as you deploy the correct build, i.e. production one, not development which will still have console.log.
I basically overwrite the console.log function with the one what has knowledge of where the code is being run. Thus i can keep using console.log as I do always. It automatically knows that I am in dev/qa mode or in production. There is also a way to force it.
Here is a working fiddle.
http://jsfiddle.net/bsurela/Zneek/
Here is the snippet as stack overflow is intimated by people posting jsfiddle
log:function(obj)
{
if(window.location.hostname === domainName)
{
if(window.myLogger.force === true)
{
window.myLogger.original.apply(this,arguments);
}
}else {
window.myLogger.original.apply(this,arguments);
}
},
I know this is quite an old question and hasn't had much activity in a while. I just wanted to add my solution that I came up with which seems to work quite well for me.
/**
* Logger For Console Logging
*/
Global.loggingEnabled = true;
Global.logMode = 'all';
Global.log = (mode, string) => {
if(Global.loggingEnabled){
switch(mode){
case 'debug':
if(Global.logMode == 'debug' || Global.logMode == 'all'){
console.log('Debug: '+JSON.stringify(string));
}
break;
case 'error':
if(Global.logMode == 'error' || Global.logMode == 'all'){
console.log('Error: '+JSON.stringify(string));
}
break;
case 'info':
if(Global.logMode == 'info' || Global.logMode == 'all'){
console.log('Info: '+JSON.stringify(string));
}
break;
}
}
}
Then I typically create a function in my scripts like this or you could make it available in a global script:
Something.fail = (message_string, data, error_type, function_name, line_number) => {
try{
if(error_type == undefined){
error_type = 'error';
}
Global.showErrorMessage(message_string, true);
Global.spinner(100, false);
Global.log(error_type, function_name);
Global.log(error_type, 'Line: '+line_number);
Global.log(error_type, 'Error: '+data);
}catch(error){
if(is_global){
Global.spinner(100, false);
Global.log('error', 'Error: '+error);
Global.log('error', 'Undefined Error...');
}else{
console.log('Error:'+error);
console.log('Global Not Loaded!');
}
}
}
And then I just use that instead of console.log like this:
try{
// To Do Somehting
Something.fail('Debug Something', data, 'debug', 'myFunc()', new Error().lineNumber);
}catch(error){
Something.fail('Something Failed', error, 'error', 'myFunc()', new Error().lineNumber);
}
This question already has answers here:
How to quickly and conveniently disable all console.log statements in my code?
(38 answers)
Closed 2 years ago.
I have many console.log (or any other console calls) in my code and I would like to use them only
when my app is in some kind of "debug mode".
I can't seem to use some kind of logger function and internally use console.log because then I wouldn't know what line fired it. Maybe only with a try/catch, but my logs are very general and I don't want try/catch in my code.
What would you recommend?
I would probably abuse the short-circuiting nature of JavaScript's logical AND operator and replace instances of:
console.log("Foo.");
With:
DEBUG && console.log("Foo.");
Assuming DEBUG is a global variable that evaluates to true if debugging is enabled.
This strategy avoids neutering console.log(), so you can still call it in release mode if you really have to (e.g. to trace an issue that doesn't occur in debug mode).
Just replace the console.log with an empty function for production.
if (!DEBUG_MODE_ON) {
console = console || {};
console.log = function(){};
}
Clobbering global functions is generally a bad idea.
Instead, you could replace all instances of console.log in your code with LOG, and at the beginning of your code:
var LOG = debug ? console.log.bind(console) : function () {};
This will still show correct line numbers and also preserve the expected console.log function for third party stuff if needed.
Since 2014, I simply use GULP (and recommend everyone to, it's an amazing tool), and I have a package installed which is called stripDebug which does that for you.
(I also use uglify and closureCompiler in production)
Update (June 20, 2019)
There's a Babel Macro that automatically removes all console statements:
https://www.npmjs.com/package/dev-console.macro
One more way to disable console.log in production and keep it in development.
// overriding console.log in production
if(window.location.host.indexOf('localhost:9000') < 0) {
console.log = function(){};
}
You can change your development settings like localhost and port.
This Tiny wrapper override will wrap the original console.log method with a function that has a check inside it, which you can control from the outside, deepening if you want to see console logs and not.
I chose window.allowConsole just as an example flag but in real-life use it would probably be something else. depending on your framework.
(function(cl){
console.log = function(){
if( window.allowConsole )
cl(...arguments);
}
})(console.log)
Usage:
// in development (allow logging)
window.allowConsole = true;
console.log(1,[1,2,3],{a:1});
// in production (disallow logging)
window.allowConsole = false;
console.log(1,[1,2,3],{a:1});
This override should be implement as "high" as possible in the code hierarchy so it would "catch" all logs before then happen. This could be expanded to all the other console methods such as warn, time, dir and so on.
Simple.
Add a little bash script that finds all references to console.log and deletes them.
Make sure that this batch script runs as part of your deployment to production.
Don't shim out console.log as an empty function, that's a waste of computation and space.
This code works for me:
if(console=='undefined' || !console || console==null) {
var console = {
log : function (string) {
// nothing to do here!!
}
}
}
The newest versions of chrome show which line of code in which file fired console.log. If you are looking for a log management system, you can try out logeek it allows you to control which groups of logs you want to see.
// In Development:
var debugMode = true
// In Prod:
var debugMode = false
// This function logs console messages when debugMode is true .
function debugLog(logMessage) {
if (debugMode) {
console.log(logMessage);
}
}
// Use the function instead of console.log
debugLog("This is a debug message");
console can out put not just log but errors warnings etc.
Here is a function to override all console outputs
(function () {
var method;
var noop = function noop() { };
var methods = [
'assert', 'clear', 'count', 'debug', 'dir', 'dirxml', 'error',
'exception', 'group', 'groupCollapsed', 'groupEnd', 'info', 'log',
'markTimeline', 'profile', 'profileEnd', 'table', 'time', 'timeEnd',
'timeStamp', 'trace', 'warn'
];
var length = methods.length;
var console = (window.console = window.console || {});
while (length--) {
method = methods[length];
console[method] = noop;
}
}());
Refer to detailed post here
https://stapp.space/disable-javascript-console-on-production/