118

Is there a way to make any function output a console.log statement when it's called by registering a global hook somewhere (that is, without modifying the actual function itself) or via some other means?

JRL
  • 76,767
  • 18
  • 98
  • 146
  • 1
    excellent question, I'd love to know if this is possible but I'm pretty sure it isn't... Maybe add a feature request for it to be added in the js engine for your favourite browser? :-) – Endophage Feb 17 '11 at 21:13
  • Perfect question, I need something similar like this – mjimcua Feb 19 '15 at 10:46

6 Answers6

71

Here's a way to augment all functions in the global namespace with the function of your choice:

function augment(withFn) {
    var name, fn;
    for (name in window) {
        fn = window[name];
        if (typeof fn === 'function') {
            window[name] = (function(name, fn) {
                var args = arguments;
                return function() {
                    withFn.apply(this, args);
                    return fn.apply(this, arguments);

                }
            })(name, fn);
        }
    }
}

augment(function(name, fn) {
    console.log("calling " + name);
});

One down side is that no functions created after calling augment will have the additional behavior.

Wayne
  • 59,728
  • 15
  • 131
  • 126
  • Does it handle return values of the function properly? – SunnyShah Oct 15 '12 at 19:02
  • 2
    @SunnyShah No it doesn't: http://jsfiddle.net/Shawn/WnJQ5/ But this one does: http://jsfiddle.net/Shawn/WnJQ5/1/ although I'm not sure it will work in ALL cases... The difference is changing `fn.apply(this, arguments);` to `return fn.apply(this, arguments);` – Shawn Jul 15 '13 at 19:49
  • 2
    @Shawn @SunnyShah Fixed. Just needed to add a `return` to the innermost function. – Wayne Jul 15 '13 at 19:58
  • works almost fine but i get an error with this jquery:call :if (jQuery.isFunction(lSrc)) and it says :TypeError: jQuery.isFunction is not a function – fekiri malek Sep 11 '16 at 00:56
  • 4
    This solution does not use jQuery – Wayne Sep 11 '16 at 03:58
25

As to me, this looks like the most elegant solution:

(function() {
    var call = Function.prototype.call;
    Function.prototype.call = function() {
        console.log(this, arguments); // Here you can do whatever actions you want
        return call.apply(this, arguments);
    };
}());
Sergey Mell
  • 7,780
  • 1
  • 26
  • 50
11

Proxy Method to log Function calls

There is a new way using Proxy to achieve this functionality in JS. assume that we want to have a console.log whenever a function of a specific class is called:

class TestClass {
  a() {
    this.aa = 1;
  }
  b() {
    this.bb = 1;
  }
}

const foo = new TestClass()
foo.a() // nothing get logged

we can replace our class instantiation with a Proxy that overrides each property of this class. so:

class TestClass {
  a() {
    this.aa = 1;
  }
  b() {
    this.bb = 1;
  }
}


const logger = className => {
  return new Proxy(new className(), {
    get: function(target, name, receiver) {
      if (!target.hasOwnProperty(name)) {
        if (typeof target[name] === "function") {
          console.log(
            "Calling Method : ",
            name,
            "|| on : ",
            target.constructor.name
          );
        }
        return new Proxy(target[name], this);
      }
      return Reflect.get(target, name, receiver);
    }
  });
};



const instance = logger(TestClass)

instance.a() // output: "Calling Method : a || on : TestClass"

check that this actually works in Codepen


Remember that using Proxy gives you a lot more functionality than to just logging console names.

Also this method works in Node.js too.

Soorena
  • 4,352
  • 5
  • 30
  • 42
2

If you want more targeted logging, the following code will log function calls for a particular object. You can even modify Object prototypes so that all new instances get logging too. I used Object.getOwnPropertyNames instead of for...in, so it works with ECMAScript 6 classes, which don't have enumerable methods.

function inject(obj, beforeFn) {
    for (let propName of Object.getOwnPropertyNames(obj)) {
        let prop = obj[propName];
        if (Object.prototype.toString.call(prop) === '[object Function]') {
            obj[propName] = (function(fnName) {
                return function() {
                    beforeFn.call(this, fnName, arguments);
                    return prop.apply(this, arguments);
                }
            })(propName);
        }
    }
}

function logFnCall(name, args) {
    let s = name + '(';
    for (let i = 0; i < args.length; i++) {
        if (i > 0)
            s += ', ';
        s += String(args[i]);
    }
    s += ')';
    console.log(s);
}

inject(Foo.prototype, logFnCall);
Peter Tseng
  • 13,613
  • 4
  • 67
  • 57
-2

Here's some Javascript which replaces adds console.log to every function in Javascript; Play with it on Regex101:

$re = "/function (.+)\\(.*\\)\\s*\\{/m"; 
$str = "function example(){}"; 
$subst = "$& console.log(\"$1()\");"; 
$result = preg_replace($re, $subst, $str);

It's a 'quick and dirty hack' but I find it useful for debugging. If you have a lot of functions, beware because this will add a lot of code. Also, the RegEx is simple and might not work for more complex function names/declaration.

hexicle
  • 2,121
  • 2
  • 24
  • 31
-11

You can actually attach your own function to console.log for everything that loads.

console.log = function(msg) {
    // Add whatever you want here
    alert(msg); 
}
Henrik Albrechtsson
  • 1,707
  • 2
  • 17
  • 17