最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

How to disable console.log messages based on criteria from specific javascript source (method, file) or message contents - Stack

programmeradmin2浏览0评论

I am working on project that uses quite a few js libraries and one of them is outputting awful lot into console, it is polluting the airwaves so bad that it makes it hard to debug....

I know how to disable logging completely by overriding console.log with this,

(function (original) {
    console.enableLogging = function () {
        console.log = original;
    };
    console.disableLogging = function () {
        console.log = function () {};
    };
})(console.log);

but how do it do that per source(file/url) of where message originated?

I am working on project that uses quite a few js libraries and one of them is outputting awful lot into console, it is polluting the airwaves so bad that it makes it hard to debug....

I know how to disable logging completely by overriding console.log with this,

(function (original) {
    console.enableLogging = function () {
        console.log = original;
    };
    console.disableLogging = function () {
        console.log = function () {};
    };
})(console.log);

but how do it do that per source(file/url) of where message originated?

Share Improve this question edited May 23, 2017 at 12:30 CommunityBot 11 silver badge asked Sep 22, 2016 at 9:14 Matas VaitkeviciusMatas Vaitkevicius 61.4k36 gold badges248 silver badges276 bronze badges 12
  • I guess you need a solution, where the JS file does not define a module of some standard or has an IIFE to protect the global namespace? – Sirko Commented Sep 22, 2016 at 9:20
  • How to determine which console.log shoud be repaced ? – passion Commented Sep 22, 2016 at 9:26
  • Do you want externally controlled (e.g., using some sort of configuration) or literally per-file controlled (e.g., change that in each file)? – VLAZ Commented Sep 22, 2016 at 10:28
  • @vlaz I can't really change all files they get loaded from third party, but I could do tweaks after loading I guess, as this is only for while I am debugging... If I would have access then just replace console.log -> \\console.log :) – Matas Vaitkevicius Commented Sep 22, 2016 at 10:32
  • OK, so I am guessing you have third party stuff that logs information you are not interested in and you want to disable that. Would you like to disable all third party logging or only some of them? – VLAZ Commented Sep 22, 2016 at 10:38
 |  Show 7 more comments

6 Answers 6

Reset to default 10 +50

Preamble

The beginning discusses how stuff works in general. If you just care for the code, skip Introduction and scroll to the Solution heading.

Introduction

Problem:

there is a lot of console noise in a web application. A significant amount of that noise is coming from third party code which we do not have access to. Some of the log noise might be coming from our code, as well.

Requirement:

reduce the noise by stopping the log. Some logs should still be kept and the decision about those should be decoupled from the code that is doing the logging. The granularity needed is "per-file". We should be able to choose which files do or do not add log messages. Finally, this will not be used in production code.

Assumption: this will be ran in a developer controlled browser. In that case, I will not focus on backwards compatibility.

Prior work:

First off logging can be enabled/disabled globally using this

(function (original) {
    console.enableLogging = function () {
        console.log = original;
    };
    console.disableLogging = function () {
        console.log = function () {};
    };
})(console.log);

(code posted in the question but also here for reference)

  • However, that does not allow for any granularity.
  • This could be modified to work on only specific modules but that cannot be done for third party code.
  • A mixed approach would be to disable logging globally but enable it in each of our modules. Problem there is that we have to modify each of our files and we will not get some potentially useful external messages.

A logging framework can be used but it might be an overkill. Although, to be honest, that's what I'd go for, I think, but it may need some integration into the product.

So, we need something light-weight-ish that has some configuration and does not need to be pretty.

Proposal:

The Loginator (title subject to change)

Let's start with the basics - we already know we can override the global log function. We'll take that and work with it. But first, let's recognise that the console object supports more than just .log. There could be various logging functions used. So-o-o, let's disable all of them.

Silence everything

//shorthand for further code. 
function noop() {}

const savedFunctions = Object.keys(console)
  .reduce((memo, key) => {
    if(typeof console[key] == "function") {
      //keep a copy just in case we need it
      memo[key] = console[key];
      //de-fang any functions 
      console[key] = noop;
    }
    
    return memo;
  }, 
  {});

console.log("Hello?");
console.info("Hello-o-o-o?");
console.warn("Can anybody hear me?");
console.error("I guess there is nobody there...");

savedFunctions.log("MUAHAHAHA!")

This can obviously be improved but it showcases how any and ll logging can be stopped. In reality, console.error should probably be left and console.warn might be also useful. But this is not the be-all-and-end-all solution.

Next, since we can override console functionality...why not supply our own?

Custom logging

const originalLog = console.log;
console.log = function selectiveHearing() {
  if (arguments[0].indexOf("die") !== -1) {
    arguments[0] = "Have a nice day!";
    }
  return originalLog.apply(console, arguments)
}

console.log("Hello.");
console.log("My name is Inigo Montoya.");
console.log("You killed my father.");
console.log("Prepare to die.");

That is all the tools we need to roll our own mini-logging framework.

How to do selective logging

The only thing missing is to determine which file something is coming from. We just need a stack trace.

// The magic
console.log(new Error().stack);

/* SAMPLE:

Error
    at Object.module.exports.request (/home/vagrant/src/kumascript/lib/kumascript/caching.js:366:17)
    at attempt (/home/vagrant/src/kumascript/lib/kumascript/loaders.js:180:24)
    at ks_utils.Class.get (/home/vagrant/src/kumascript/lib/kumascript/loaders.js:194:9)
    at /home/vagrant/src/kumascript/lib/kumascript/macros.js:282:24
    at /home/vagrant/src/kumascript/node_modules/async/lib/async.js:118:13
    at Array.forEach (native)
    at _each (/home/vagrant/src/kumascript/node_modules/async/lib/async.js:39:24)
    at Object.async.each (/home/vagrant/src/kumascript/node_modules/async/lib/async.js:117:9)
    at ks_utils.Class.reloadTemplates (/home/vagrant/src/kumascript/lib/kumascript/macros.js:281:19)
    at ks_utils.Class.process (/home/vagrant/src/kumascript/lib/kumascript/macros.js:217:15)
*/

(Relevant bit copied here.)

True, there are some better ways to do it but not a lot. It would either require a framework or it's browser specific - error stacks are not officially supported but they work in Chrome, Edge, and Firefox. Also, come on - it's literally one line - we want simple and don't mind dirty, so I'm happy for the tradeoff.

Solution

Putting it all together. Warning: Do NOT use this in production

(function(whitelist = [], functionsToPreserve = ["error"]) {
  function noop() {}

  //ensure we KNOW that there is a log function here, just in case
  const savedFunctions = { log: console.log }
        
  //proceed with nuking the rest of the chattiness away
  Object.keys(console)
    .reduce((memo, key) => {
      if(typeof console[key] == "function" && functionsToPreserve.indexOf(key) != -1 ) {
        memo[key] = console[key];
        console[key] = noop;
      }
    
      return memo;
    }, 
    savedFunctions); //<- it's a const so we can't re-assign it. Besides, we don't need to, if we use it as a seed for reduce()
  
  console.log = function customLog() {
    //index 0 - the error message
    //index 1 - this function
    //index 2 - the calling function, i.e., the actual one that did console.log()
    const callingFile = new Error().stack.split("\n")[2];
    
    if (whitelist.some(entry => callingFile.includes(entry))) {
      savedFunctions.log.apply(console, arguments)
    }
  }

})(["myFile.js"]) //hey, it's SOMEWHAT configurable

Or a blacklist

(function(blacklist = [], functionsToPreserve = ["error"]) {
    function noop() {}

    //ensure we KNOW that there is a log function here, just in case
    const savedFunctions = {
        log: console.log
    }

    //proceed with nuking the rest of the chattiness away
    Object.keys(console)
        .reduce((memo, key) => {
                if (typeof console[key] == "function" && functionsToPreserve.indexOf(key) != -1) {
                    memo[key] = console[key];
                    console[key] = noop;
                }

                return memo;
            },
            savedFunctions); //<- it's a const so we can't re-assign it. Besides, we don't need to, if we use it as a seed for reduce()

    console.log = function customLog() {
        //index 0 - the error message
        //index 1 - this function
        //index 2 - the calling function, i.e., the actual one that did console.log()
        const callingFile = new Error().stack.split("\n")[2];

        if (blacklist.some(entry => callingFile.includes(entry))) {
            return;
        } else {
            savedFunctions.log.apply(console, arguments);
        }
    }

})(["myFile.js"])

So, this is a custom logger. Sure, it's not perfect but it will do the job. And, hey, since the whitelisting is a bit loose, it could be turned to an advantage:

  • to whitelist a bunch of files that share a substring, say, all myApp can include myApp1.js, myApp2.js, and myApp3.js.
  • although if you want specific files, you can just pass the full name, including extension. I doubt there would be a bunch of duplicate filenames.
  • finally, the stack trace will include the name of the calling function, if any, so you can actually just pass that and that will whitelist on per-function basis. However, it relies on the function having a name and it's more likely for function names to clash, so use with care

Other than that, there can certainly be improvements but that is the basis of it. The info/warn methods can also be overriden, for example.

So, this, if used, should only be in dev builds. There are a lot of ways to make it not go into production, so I won't discuss them but here is one thing I can mention: you can also use this anywhere if you save it as a bookmarklet

javascript:!function(){function c(){}var a=arguments.length<=0||void 0===arguments[0]?[]:arguments[0],b=arguments.length<=1||void 0===arguments[1]?["error"]:arguments[1],d={log:console.log};Object.keys(console).reduce(function(a,d){return"function"==typeof console[d]&&b.indexOf(d)!=-1&&(a[d]=console[d],console[d]=c),a},d),console.log=function(){var c=(new Error).stack.split("\n")[2];a.some(function(a){return c.includes(a)})&&d.log.apply(console,arguments)}}(["myFile.js"]);

This is it minified (although I passed it through Babel first, to use ES5 minification) and still configurable, to an extent, as you can change the very end where you can pass the whitelist. But other than that, it will work the same and is completely decoupled from the codebase. It will not run at pageload but if that's needed you can either use this as a userscript (still decoupled) or include it before other JS files in dev/debug builds only.

A note here - this will work in Chrome, Edge and Firefox. It's all the latest browsers, so I assume a developer will use at least one of them. The question is tagged as Chrome but I decided to widen the support. A Chrome only solution could work slightly better but it's not really a big loss of functionality.

I've found these settings in recent (2020-2023) Chrome DevTools console to be helpful:

  1. sidebar icon: user messages. I like this option. Only see the messages from "my" code.

  2. gear icon: Select context only. "Selected context" refers to iframes versus the parent document.

  3. gear icon: Hide network. "Network" refers to HTTP traffic.

I was as troubled as you. This is my approach. https://github.com/jchnxu/guard-with-debug

Simple usage:

localStorage.debug = [
    'enable/console/log/in/this/file.ts',
    'enable/console/log/in/this/folder/*',
    '-disable/console/log/in/this/file.ts',
    '-disable/console/log/in/this/folder/*',
    
    // enable all
    '*',
].join(',');

The benefit: it's zero-runtime.

Disclaimer: I am the author of this tiny utility

It work in chrome: ...index.html

<html>
<body>
<script>
    (function(){
        var original = console.log;
        console.log = function(){
            var script = document.currentScript;
            alert(script.src);
            if(script.src === 'file:///C:/Users/degr/Desktop/script.js') {
                original.apply(console, arguments)
            }
        }
    })();
    console.log('this will be hidden');
</script>
<script src="script.js"></script>
</body>
</html>

...script.js

console.log('this will work');

Console.log does not work from index.html, but work from script.js. Both files situated on my desctop.

If it's an option to modify file, you can set a flag at top of file for disabling logs for that:

var DEBUG = false;
DEBUG && console.log("cyberpunk 2077");

To disable logs for all js files, put it once at top of any js file:

var DEBUG = false;
if (!DEBUG) {
   console.log = () => {};
 }

This is not pretty but will work.
Put something like this in your file before the <script> tag of the "bad" library :

<script>function GetFile(JSFile) {      
    var MReq = new XMLHttpRequest();        
    MReq.open('GET', JSFile, false);    
    MReq.send();
    eval(MReq.responseText.replace(/console.log\(/g,"(function(){})("));            
}</script>

Then replace the tag

<script src="badLib.js">

With:

GetFile("badLib.js")

Only for short time debugging.

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论