Adding Console.Log to Every Function Automatically

Adding console.log to every function automatically

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.

Adding console.log to every function automatically - part 2

If you read the code, you can see that fn is being called with arguments, and that is what you want in your function. So just add it to args :

withFn.apply(this, Array.from(args).concat([arguments]));

Can I override the Javascript Function object to log all function calls?

The obvious answer is something like the following:

var origCall = Function.prototype.call;
Function.prototype.call = function (thisArg) {
console.log("calling a function");

var args = Array.prototype.slice.call(arguments, 1);
origCall.apply(thisArg, args);
};

But this actually immediately enters an infinite loop, because the very act of calling console.log executes a function call, which calls console.log, which executes a function call, which calls console.log, which...

Point being, I'm not sure this is possible.

How to get Javascript Function Calls/Trace at Runtime

I can't think of a great way to intercept all function calls globally to insert logging (though there is a decent workaround in the update section below).

Instead, how about only adding logging to functions in a certain namespace that you care about? You can do this with the following setup code:

var functionLogger = {};

functionLogger.log = true;//Set this to false to disable logging

/**
* Gets a function that when called will log information about itself if logging is turned on.
*
* @param func The function to add logging to.
* @param name The name of the function.
*
* @return A function that will perform logging and then call the function.
*/
functionLogger.getLoggableFunction = function(func, name) {
return function() {
if (functionLogger.log) {
var logText = name + '(';

for (var i = 0; i < arguments.length; i++) {
if (i > 0) {
logText += ', ';
}
logText += arguments[i];
}
logText += ');';

console.log(logText);
}

return func.apply(this, arguments);
}
};

/**
* After this is called, all direct children of the provided namespace object that are
* functions will log their name as well as the values of the parameters passed in.
*
* @param namespaceObject The object whose child functions you'd like to add logging to.
*/
functionLogger.addLoggingToNamespace = function(namespaceObject){
for(var name in namespaceObject){
var potentialFunction = namespaceObject[name];

if(Object.prototype.toString.call(potentialFunction) === '[object Function]'){
namespaceObject[name] = functionLogger.getLoggableFunction(potentialFunction, name);
}
}
};

Then, for whatever namespaceObject you want to add logging to, you just call:

functionLogger.addLoggingToNamespace(yourNamespaceObject);

Here's a fiddle to see it in action.

UPDATE

Note that you can call functionLogger.addLoggingToNamespace(window); to add logging to all global functions at the time of the call. Also, if you really want, you can traverse the tree to find any functions and update them accordingly. The one downfall of this method is that it only works on functions that exist at the time. Thus, it's still not the greatest solution, but it's a LOT less work than adding logging statements by hand :)

how can I log every method call in node.js without adding debug lines everywhere?

I'm guessing this is a web app, in which case if you are using connect you can use a logger middleware that logs the user and the URL path, which is probably sufficient. Otherwise, you are going to have to do some metaprogramming along the lines of wrapping each function in a wrapper function to do the logging.

function logCall(realFunc, instance) {
return function() {
log.debug('User: ' + instance.user_id + ' method ' + realFunc.name);
return realFunc.apply(instance, arguments);
};
}

For this to work, your class method's must be named functions, not anonymous.

function sendMessage() {
//code to send message
//can use `this` to access instance properties
}
function MyClass(userId) {
this.userId = userId; //or whatever
this.sendMessage = logCall(sendMessage, this);
//repeat above line for each instance method you want instrumented for logging
}

How to log every call to member functions of a JavaScript object?

You can wrap all the functions on the instance. For instance, assuming obj is the object you want to watch:

function wrapObjectFunctions(obj, before, after) {
var key, value;

for (key in obj) {
value = obj[key];
if (typeof value === "function") {
wrapFunction(obj, key, value);
}
}

function wrapFunction(obj, fname, f) {
obj[fname] = function() {
var rv;
if (before) {
before(fname, this, arguments);
}
rv = f.apply(this, arguments); // Calls the original
if (after) {
after(fname, this, arguments, rv);
}
console.log( /*...*/ );
return rv;
};
}
}

(arguments in the above, if you're not familiar with it, is a magic pseudo-array provided by JavaScript which contains the arguments that the function was called with. I know it looks like pseudo-code, but it isn't.)

Live Example:

function Thing(name) {
this.name = name;
}
Thing.prototype.sayName = function () {
console.log("My name is " + this.name);
};

var t = new Thing("Fred");
console.log("Before wrapping:");
t.sayName(); // My name is Fred, with no extra logging

console.log("----");

wrapObjectFunctions(
t,
function(fname) {
console.log("LOG: before calling " + fname);
},
function(fname) {
console.log("LOG: after calling " + fname);
}
);

console.log("After wrapping:");
t.sayName(); // My name is Fred, with no extra logging

function wrapObjectFunctions(obj, before, after) {
var key, value;

for (key in obj) {
value = obj[key];
if (typeof value === "function") {
wrapFunction(obj, key, value);
}
}

function wrapFunction(obj, fname, f) {
obj[fname] = function() {
var rv;
if (before) {
before(fname, this, arguments);
}
rv = f.apply(this, arguments); // Calls the original
if (after) {
after(fname, this, arguments, rv);
}
console.log(/*...*/);
return rv;
};
}
}


Related Topics



Leave a reply



Submit