sprugman
sprugman

Reputation: 19831

Intercept calls to console.log in Chrome

I have a script that I can't change that makes a lot of console.log calls. I want to add another layer and respond if the calls contain certain strings. This works in Firefox, but throws an "Illegal invocation" error in Chrome on the 4th line:

var oldConsole = {};
oldConsole.log = console.log;
console.log = function (arg) {
    oldConsole.log('MY CONSOLE!!');
    oldConsole.log(arg);
}

Any ideas how to get around that? I also tried cloning the console...

Upvotes: 42

Views: 26355

Answers (8)

NerdyDeeds
NerdyDeeds

Reputation: 469

Since we're all just having fun here, might I offer up:

var console = (function(csl=window.console){
    let consoleFns = Object.fromEntries(Object.keys(console).map(key=>[
            key, function(...text) {
                csl[key].apply(csl, ['Look ma!', ...text])
            }
        ]
    ));

    // Any additional method-specific code lives here
    return consoleFns;
}());

This carries the added benefit of maintaining ALL valid console functions, while providing the additional functionality (in this heavily-contrived example, adding "Look ma!" before each output.

The reason I'm setting the object constructed from the map to a consoleFns variable instead of simply returning the map's output, a la

return Object.fromEntries(Object.keys(console).map(key=>{

is so one can replace the

// Any additional method-specific code lives here

with something along the lines of

consoleFns.error = ()=>{}                         // Render console.error ineffective
consoleFns.warn  = consoleFns.log                 // Treat warn the same as log
consoleFns.flibbityGibbit = function(...text) {   // Prefix flibbityGibbit messages with "WoO-hOo, wOo-HoO!" in a lovely shade of violet
    csl.log.apply(csl, ["%cWoO-hOo, wOo-HoO!", "font-weight:bold; color: rebeccapurple;", ...text]);
}

Although if your only objective is to tweak the output text, you can certainly simply return it as above.

Upvotes: 0

yorg
yorg

Reputation: 664

To fully intercept the console, we can override every methods :

const bindConsole=function(onMessage){
    Object.keys(console)
    .filter(type=>typeof(console[type])==='function')// *1
    .forEach(type=>{
        let _old=console[type];
        console[type] = function (...args) {
            _old.apply(console,args);
            onMessage(type,args);// *2
        };
    });
};

For old browsers :

var bindOldConsole=function(onMessage){
    for(var k in console){// *1
        if(typeof(console[k])=='function')(function(type){
            var _old=console[type];
            console[type] = function () {
                _old.apply(console,arguments);
                onMessage(type,arguments);
            };
        })(k);
    }
};

  • *1 Looks like console has only methods but better be sure.

  • *2 You may block cyclic calls to console from onMessage by replacing this line with :

if(!isCyclic())onMessage(type,args);

// es6. Not sure concerning old browsers :(
const isCyclic=function (){
    let erst=(new Error()).stack.split('\n');
    return erst.includes(erst[1],2);
};

Upvotes: 1

marrco
marrco

Reputation: 183

Since I cannot comment (yet) on @ludovic-feltz answer, here is his answer corrected to allow string interpolation in the console :

// define a new console
var console = (function(oldCons){
    return {
        log: function(...text){
            oldCons.log(...text);
            // Your code
        },
        info: function (...text) {
            oldCons.info(...text);
            // Your code
        },
        warn: function (...text) {
            oldCons.warn(...text);
            // Your code
        },
        error: function (...text) {
            oldCons.error(...text);
            // Your code
        }
    };
}(window.console));

//Then redefine the old console
window.console = console;

Upvotes: 0

NVRM
NVRM

Reputation: 13087

Can be simply:

console.log = (m) => terminal.innerHTML = JSON.stringify(m)
#terminal {background: black; color:chartreuse}
$ > <span id="terminal"></span>
<hr>        
<button onclick="console.log('Hello world!!')">3V3L</button>
<button onclick="console.log(document)">3V3L</button>
<button onclick="console.log(Math.PI)">3V3L</button>

Upvotes: 1

zzzzBov
zzzzBov

Reputation: 179046

You need to call console.log in the context of console for chrome:

(function () {
  var log = console.log;
  console.log = function () {
    log.call(this, 'My Console!!!');
    log.apply(this, Array.prototype.slice.call(arguments));
  };
}());

Modern language features can significantly simplify this snippet:

{
  const log = console.log.bind(console)
  console.log = (...args) => {
    log('My Console!!!')
    log(...args)
  }
}

Upvotes: 63

Ludovic Feltz
Ludovic Feltz

Reputation: 11916

I know it's an old post but it can be useful anyway as others solution are not compatible with older browsers.

You can redefine the behavior of each function of the console (and for all browsers) like this:

// define a new console
var console = (function(oldCons){
    return {
        log: function(text){
            oldCons.log(text);
            // Your code
        },
        info: function (text) {
            oldCons.info(text);
            // Your code
        },
        warn: function (text) {
            oldCons.warn(text);
            // Your code
        },
        error: function (text) {
            oldCons.error(text);
            // Your code
        }
    };
}(window.console));

//Then redefine the old console
window.console = console;

Upvotes: 9

Devnegikec
Devnegikec

Reputation: 631

With ES6 new spread operator you can write it like this

(function () {
  var log = console.log;
  console.log = function () {
    log.call(this, 'My Console!!!', ...arguments);
  };
}());

Upvotes: 3

rgthree
rgthree

Reputation: 7273

You can also use the same logic, but call it off the console object so the context is the same.

if(window.console){
  console.yo = console.log;
  console.log = function(str){
    console.yo('MY CONSOLE!!');
    console.yo(str);
  }
}

Upvotes: 5

Related Questions