KoenvE
KoenvE

Reputation: 157

Disabling console access for specific Javascript files

In my current project with lots of dependencies I need a way to disable console access for specific libraries so that those files can't use any of the console functionality.

I could of course disable console functionality by simply finding and replacing it in the library bundle, but as this project has a lot of dependencies that would make updating libraries a huge hassle.

I'm aware that I can disable console functionality by overwriting it with an empty function block:

console.log = function(){};

But that disables the console functionality for the entire project. So im looking for an implementation, or a line of code with which I can disable console functionality for a specific file or code block.

Upvotes: 11

Views: 1406

Answers (3)

molamk
molamk

Reputation: 4116

Write a white-listing "middleware" for console.log

// Preserve the old console.log
const log = console.log;

// Used a dictionary because it's faster than lists for lookups
const whiteListedFunctions = {"hello": true};

// Whitelisting "middleware". We used the function's name "funcName"
// as a criteria, but it's adaptable
const isWhitelisted = callerData => callerData.funcName in whiteListedFunctions;

// Replacing the default "console.log"
console.log = arg => {
  const stack = new Error().stack.split("at")[2].trim().split(' ');
  const fileParts = stack[1].substr(1, stack[1].length - 2).split(':');

  const callerData = {
    funcName: stack[0],
    file: fileParts.slice(0, fileParts.length - 2).join(':'),
    lineColNumber: fileParts.slice(fileParts.length - 2).join(':')
  };
  
  if (isWhitelisted(callerData)) {      // Filtering happens here
    log(arg);
  }
};

// Define the calling functions
function hello() { console.log("hello"); }
function world() { console.log("world"); }

hello(); // => Prints hello
world(); // => Doesn't print anything

Method explanation

  • You can do this by creating a whitelist (or blacklist) that will contain your filtering criteria. For example it may contain the name of the functions that call console.log or maybe the file name, or even the line and column numbers.
  • After that you create your whitelisting "middleware". This will take the caller function data and decide if it can log stuff or not. This will be done based on the previously defined whitelist. You can choose your preferred criteria in this "middleware".
  • Then you actually replace console.log by overriding with your new logger. This logger will take as an argument the message to log (maybe multiple arguments?). In this function you also need to find the data relating to the caller function (which wanted to call console.log).
  • Once you have the caller data, you can then use your whitelisting middleware to decide if it can log stuff

Getting information about the caller function

This part is a little "hacky" (but it got the job done in this case). We basically create an Error and check its stack attribute like this new Error().stack. Which will give us this trace

Error at console.log.arg [as log] (https://stacksnippets.net/js:25:7) at hello (https://stacksnippets.net/js:41:11) at https://stacksnippets.net/js:48:1

After processing (split, map, etc...) the trace we get the caller function data. For example here we have

  • The caller function's name: hello
  • The file name: https://stacksnippets.net/js
  • The line and column number: 41:11 (watch out for minifiers)

This bit was inspired by VLAZ's answer in How to disable console.log messages based on criteria from specific javascript source (method, file) or message contents, so make sure to check it out. Really good and thorough post.

Note

To make sense of the trace we can do new Error().stack.split("at")[INDEX].trim().split(' ') where INDEX is the position of the function call you want to target in the stack trace. So if you want to get a different "level" that the one used in this example, try changing INDEX

Upvotes: 2

Nino Filiu
Nino Filiu

Reputation: 18493

Yes, you can disable console logs from files based on their path! Here's a solution:

// in ./loud-lib.js
module.exports = {
    logsSomething: () => console.log('hello from loud-lib')
}
// in ./silent-lib.js
module.exports = {
    logsSomething: () => console.log('hello from silent-lib')
}
// in ./index.js
const loud = require('./loud-lib');
const silent = require('./silent-lib');

// save console.log
const log = console.log;
// redefinition of console.log
console.log = (...params) => {
    // define regexp for path of libraries that log too much
    const loudLibs = [/loud-lib/];
    // check if the paths logged in the stacktract match with at least one regexp
    const tooLoud = !!loudLibs.find(reg => reg.test(new Error().stack));
    // log only if the log is coming from a library that doesn't logs too much
    if (!tooLoud) log(...params);
};

loud.logsSomething();
silent.logsSomething();
$ node ./index.js
hello from silent-lib

This is based on the fact that new Error() produces a stack trace that identifies from which file is the error coming from (recursively).

Based on this observation, you can define an array of regular expression that match the name of libraries you don't want to hear logs from. You can get really specific and creative with the re-definition of console.log, but I kept it simple.

However, be aware of this (especially when using Webpack): if you bundle all your JS assets into one single bundle.js, the stacktrace will always point to bundle.js, thus logging everything. You'll have to go further from my code, for example by using stack-source-map, but I don't have sufficient details on your project to deliver a solution. I hope the ideas above are sufficient for you.

Upvotes: 0

Silvio Biasiol
Silvio Biasiol

Reputation: 953

Just redefine the console to log over a condition, your condition of course will be a check over which library is accessing the function:

// Your condition, could be anything
let condition = true;
/* Redefine the console object changing only the log function with your new version and keeping all the other functionalities intact
*/
let console = (old => ({
    ...old,
    log: text => { if (condition) old.log(text) }
}))(window.console)

// Redefine the old console
window.console = console;

console.log('hello!')

Hope it helped :)

Upvotes: -1

Related Questions