Reputation: 19831
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
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
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
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
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
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
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
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
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