5

The usual suggestion (Capturing javascript console.log?) for a js console.log interceptor falls short on two counts, both serious:

(i) all calls in the actual browser console now appear to come from the same line - where the new function is defined - rather then where the call is done

(ii) the intercepted arguments are not yet formatted the way console.log does (i.e. the % replacement goodies, %o, in particular):

(function() {
  function send_stuff(){ /* Deal with console arguments. */ }
  
  var oldLog=console.log;
  console.log=function(msg) {
    oldLog.apply(this,arguments);
    send_stuff(Array.prototype.slice.call(arguments).join());
  }
}())

console.log("test");
console.log("Hello %s", "Bob");
console.log("Here is an object %o", { stuff: "thing" });

Maybe something better (e.g. something able to capture the actual buffer content of console.log) has been devised.

Edit: To clarify the multiple arguments failure: besides expanding the file/line info, console.log also does some clever replacements - a-la-printf - of % sequences. Of particular interest is the %o sequence, where a deep-ish dump of the subsequent object(s) is expanded, and put to good use by the browser's console window. This cannot be replicated, unless one is willing to basically reimplement its logic (unnecessarily and , in all likelyhood, unefficiently)

Cheers,

Community
  • 1
  • 1
Alien Life Form
  • 1,884
  • 1
  • 19
  • 27
  • Why do you say the multi-args does not work ? It should be supported with this script, you are using the `arguments` object. – Quentin Roy Oct 19 '15 at 11:29
  • 1
    For browser console, I don't think there is any way to solve that.. console.log *is* called here. However, you can sometimes inspect the trace and find where your newly defined command has been called. – Quentin Roy Oct 19 '15 at 11:36
  • On the multi args thing, see my edit. – Alien Life Form Oct 19 '15 at 13:30
  • Did you actually experienced this? You do not need to replicate this behaviour. `msg` is useless on your script and `oldLog.apply(this, arguments)` should reapply the arguments exactly the way they have been received (except that you may want to use `console` instead of `this`). I don't really understand why it would not work.. Can you provide a snippet ? – Quentin Roy Oct 19 '15 at 13:51
  • 1
    Though obviously `send_stuff` will receive this arguments before this transformation happens, so there you may need to apply it. – Quentin Roy Oct 19 '15 at 14:04
  • That is the indeed the snippet, as taken from the linked reply. I used both console and this - it makes no real difference, all nessages appear to come from line 5 of the file - where .apply is called. No real surprise here - the call is happening a stack frame deeper than console.log expects, so popping said frame (if possible) would take care of (i). Wether (ii) can be solved I know not. – Alien Life Form Oct 19 '15 at 14:32
  • 4
    *unless one is willing to basically reimplement its logic* - oh well: `Console.prototype.log = function() { this._stdout.write(util.format.apply(this, arguments) + '\n'); };` – Bartek Banachewicz Oct 19 '15 at 15:30
  • So we just have to do `send_stuff(util.format.apply(this, arguments))`. Maybe we can also execute the transformation once for `console.log` and `send_stuff` though I would be careful with this. We are not sure how browsers deal with `console.log`'s arguments. @BartekBanachewicz, where did you find this code? – Quentin Roy Oct 19 '15 at 16:22
  • So - although I did not mention it in the question - would util.format be available in a cordova environment? And, would it perform %o expansion? – Alien Life Form Oct 19 '15 at 16:47
  • @QuentinRoy it's straight from Node.js source. The implementations in browsers may differ, I guess. – Bartek Banachewicz Oct 19 '15 at 17:11
  • @BartekBanachewicz Apparently, util is a node package. It is not defined in browser's global scope. – Quentin Roy Oct 20 '15 at 08:56

1 Answers1

-1

It looks like the behavior of console.log changed a little bit and now the masks are working but you are still getting the line of oldLog.apply(this,arguments); in the console. One thing you could do is to use console.error instead, this is going to show the call stack that you can use to see what line originally called your modified log method.

Another solution is to throw an error to get the line number from the call stack, like this

(function () {

  function origin() {
    try {
      throw Error('');
    } catch (err) {
      return err.stack.split('\n').slice(-1)[0];
    }
  }

  function send_stuff() { /* Deal with console arguments. */ }

  var oldLog = console.log;
  console.log = function (msg) {
    oldLog.call(this, ...arguments, origin());
    send_stuff(Array.prototype.slice.call(arguments).join());
  };

})();

console.log("test");
console.log("Hello %s", "Bob");
console.log("Here is an object %o", {
  stuff: "thing"
});
Rodrigo5244
  • 5,145
  • 2
  • 25
  • 36