104

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.

Sean Anderson
  • 27,963
  • 30
  • 126
  • 237
  • 1
    http://stackoverflow.com/questions/1114187/is-it-a-bad-idea-to-leave-firebug-console-log-calls-in-your-producton-javascri – Zach Lysobey Apr 12 '13 at 20:32

13 Answers13

52

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?

Community
  • 1
  • 1
craigb
  • 16,827
  • 7
  • 51
  • 62
  • 7
    Good idea. Just some fixes needed. Visual Studio JS debugger throws at the first console.log = noOp() because console object itself is not defined. I did it like this: console = { log: noOp, warn: noOp, error: noOp }. Also notice that you don't want to put () after noOp - you want to assign the function itself and not its return value. Tested on Visual Studio JS debugger and IE9 - now it works fine. – JustAMartin Mar 27 '13 at 09:53
  • 3
    FYI, if you are already using jQuery, they provide a noop function: `$.noop`. – crush Jun 29 '15 at 20:00
40

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).
Rob W
  • 341,306
  • 83
  • 791
  • 678
  • 48
    I agree with you in principle. But, I think we would all agree that implementing logging which doesn't fire except during debug mode is necessary for quality server-side code. Nobody goes through and removes their logging for a production release -- the program determines what level of logging is necessary and reacts accordingly. I would hope there's something akin to this for client-side programming... even just setting an "isDebug" variable, if need be. Why would I want to burden the next developer by necessitating going back and re-adding logging for problematic areas in the future? – Sean Anderson Nov 03 '11 at 21:35
  • 1
    @SeanAnderson Debugging messages at a production website looks shabby. The best practice for distributing code at a production environment is to remove debugging code, then minify it. – Rob W Nov 03 '11 at 21:40
  • 12
    Shabby how? I would argue that saying the developer console is part of the production website is like saying the log files are part of an application. Yes, they are both generated by code, but there should be some level of understanding that logs have to be left somewhere. Whether the statements inside the logs are user-friendly or not is another matter, though. – Sean Anderson Nov 03 '11 at 21:43
  • 1
    Logs of web applications should be handled server-side. Only include log-messages at the client if they're really meaningful (e.g. for bug reporting *at a production website?*). It's not a big deal to strip all debugging code before minimizing + copying the files to a production site. – Rob W Nov 03 '11 at 21:51
  • 4
    How would one automatically strip debugging code before minimizing? There are a large amount of forms that a client-side log message could take. – Sean Anderson Nov 03 '11 at 22:03
  • 6
    You could mark every piece of debug code, for example by prefixing/postfixing a line of debugging code by a comment. Example: `/*DEBUG:start*/console.log("Foo");/*DEBUG:end*/`. Then, use a RegExp to remove all occurrences of `/*DENUG-start*/[\S\s]*?/*DEBUG-end*/`. The remaining white-space characters will be removed by the minimizer. – Rob W Nov 03 '11 at 22:06
  • 1
    `window.console` will evaluate to `undefined` if undefined (which is falsey) – Zach Lysobey Apr 02 '13 at 18:43
  • It does seem to affect speed according to the first comment to this [blog](https://shellycloud.com/blog/2014/11/five-functions-of-the-console-object-you-didnt-know) – John Jul 28 '15 at 14:42
  • OMG JavaScript! – Gayan Weerakutti Apr 07 '17 at 05:37
  • 2
    Has anyone heard of the developers console? What if we just debug with that fantastic piece of engineering instead of placing console.whatever around the code? /*DEBUG:start*/ => wtffff is this? I would fire you if I knew you.. – chesscov77 Apr 17 '17 at 19:10
  • 8
    Unless this answer says WHY you shouldn't do it, it's really not useful. We're just supposed to blindly accept that it's bad with no evidence or reasons? – ESR Sep 21 '17 at 07:03
  • 1
    @Juan there's plenty of reasons to use console logging in development, particularly logging errors and warnings for people that are not working on the code or are not frontend engineers. – theflowersoftime Oct 08 '19 at 19:49
  • 2
    @ThomasMcCabe I agree, if there is a need to display a message or error that the user is supposed to see in the console, then use it. But if you use it while writing a function to "test" the value of certain variables because you don't know how to put a breakpoint, then there is a problem. Even worse if you commit and forget to remove it. – chesscov77 Oct 21 '19 at 01:09
  • 1
    > "But if you use it while writing a function to "test" the value of certain variables because you don't know how to put a breakpoint, then there is a problem." - I missed this argument so this statement seems presumptive. > "Even worse if you commit and forget to remove it." - Use tooling that doesn't allow you to do this. Tons of it exists. What other mistakes are getting shipped if you're just using your eyes? – theflowersoftime Jul 07 '20 at 03:44
28

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.

terales
  • 3,116
  • 23
  • 33
  • 3
    If you're using _grunt_ and _uglify_, the same option is also available (seems uglify is based upon UglifyJS2): https://github.com/gruntjs/grunt-contrib-uglify#turn-off-console-warnings – Tilt Jul 05 '16 at 08:51
  • 2
    The question is "should I", not "how do I". – ESR Sep 21 '17 at 07:04
  • You should leave console.errors in . If someone in production has a problem, then you can easily diagnose it. Don't strip console.errors! – Oliver Watkins Feb 05 '19 at 10:59
  • If there will be a need for them then you can just set `drop_console` to `false`, observe them and hide them again. – terales Feb 10 '19 at 02:19
16

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

Community
  • 1
  • 1
wutz
  • 3,204
  • 17
  • 13
6

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. Everything you CAN even display through console.log is obviously already available on the frontend side anyway! NOT showing a log message because you are scared to reveal security-details or something like that 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.

Mario B
  • 2,102
  • 2
  • 29
  • 41
5

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

Zach Lysobey
  • 14,959
  • 20
  • 95
  • 149
5

Yes. console.log will throw an exception in browsers that don't have support for it (console object will not be found).

MK_Dev
  • 3,291
  • 5
  • 27
  • 45
3
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');
dynamiclynk
  • 2,275
  • 27
  • 31
1

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.

Zach Lysobey
  • 14,959
  • 20
  • 95
  • 149
shanky
  • 376
  • 1
  • 18
1

TL;DR

Idea: Logging objects precludes them from being Garbage Collected.

Details

  1. 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.
  2. If logs are too long old messages do get deleted in Chrome.
  3. 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)) }
});
ilyaigpetrov
  • 3,657
  • 3
  • 30
  • 46
1

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.

Daniel Danielecki
  • 8,508
  • 6
  • 68
  • 94
0

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);
    }
},
Bhavin
  • 427
  • 3
  • 15
0

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);
}
Kyle Coots
  • 2,041
  • 1
  • 18
  • 24