120

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?

0

6 Answers 6

72

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.

Sign up to request clarification or add additional context in comments.

5 Comments

Does it handle return values of the function properly?
@SunnyShah No it doesn't: jsfiddle.net/Shawn/WnJQ5 But this one does: 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 @SunnyShah Fixed. Just needed to add a return to the innermost function.
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
This solution does not use jQuery
27

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);
    };
}());

3 Comments

RangeError: Maximum call stack size exceeded. Any alternative for node js
It would be good if we restrict this only over a set of functions. Running this on a webpage basically means, all the methods from libraries will also get logged.
One possible solution could be using this.name to distinguish the functions by name if they have it of course
12

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.

2 Comments

Can you also do this without using instances and classes? Specifically speaking in node.js?
@Revadike this should help: stackoverflow.com/a/28708700/5284370
3

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);

Comments

-3

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.

Comments

-12

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); 
}

1 Comment

correct or not, it does not answer the question. at all. (incase anyone was STILL confused)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.