Skip to content

Instantly share code, notes, and snippets.

@pmuellr
Created September 18, 2009 16:16
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save pmuellr/189144 to your computer and use it in GitHub Desktop.
Save pmuellr/189144 to your computer and use it in GitHub Desktop.
have your JavaScript methods trace their execution
//------------------------------------------------------------------------------
// JavaScript function tracer for "classes"
//------------------------------------------------------------------------------
// from: pmuellr@muellerware.org
// home: http://gist.github.com/189144
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// define our function tracer
//------------------------------------------------------------------------------
FunctionTracer = {};
//------------------------------------------------------------------------------
// logging function, default is to write to console
//------------------------------------------------------------------------------
FunctionTracer.logFunction = function(message) { console.log(message); };
//------------------------------------------------------------------------------
// print time in messages?
//------------------------------------------------------------------------------
FunctionTracer.printTime = false;
//------------------------------------------------------------------------------
// print arguments in messages?
//------------------------------------------------------------------------------
FunctionTracer.printArgs = false;
//------------------------------------------------------------------------------
// return a registered function
// example: say you have a function named "foo". You can trace it via:
// foo = FunctionTracer.traceFunction(foo, "foo");
//------------------------------------------------------------------------------
FunctionTracer.traceFunction = function(func, name) {
if (!FunctionTracer.startTime) FunctionTracer.startTime = new Date().getTime();
if (!FunctionTracer.indent) FunctionTracer.indent = "";
//--------------------------------------------------------------------------
// best effort printing an argument as a string
//--------------------------------------------------------------------------
function arg2string(argument) {
if (argument === null) return "null";
if (argument === undefined) return "undefined";
try {
return JSON.stringify(argument);
}
catch(e) {
return argument.toString();
}
}
//--------------------------------------------------------------------------
// this function creates a wrapper for each function which traces itself,
// then invokes the original function
//--------------------------------------------------------------------------
function getWrapper(func, name) {
if (typeof func != "function") throw new Error(name + " is not a function");
return function() {
var oldIndent = FunctionTracer.indent;
FunctionTracer.indent += " ";
var message = oldIndent + name;
if (FunctionTracer.printTime) {
var time = new Date().getTime() - FunctionTracer.startTime;
time = "" + time;
while (time.length < 8) time = " " + time;
message = time + ": " + message;
}
if (!FunctionTracer.printArgs) {
message += "()";
}
else {
message += "(";
for (var i=0; i<arguments.length; i++) {
message += arg2string(arguments[i]);
if (i != arguments.length - 1) {
message += ", ";
}
}
message += ")";
}
try {
(FunctionTracer.logFunction)(message);
return func.apply(this, Array.prototype.splice.call(arguments, 0));
}
catch(e) {
(FunctionTracer.logFunction)(message + "; exception: " + e);
throw e;
}
finally {
FunctionTracer.indent = oldIndent;
}
}
}
//--------------------------------------------------------------------------
// main function logic
//--------------------------------------------------------------------------
(FunctionTracer.logFunction)("installed tracer: " + name + "()");
return getWrapper(func, name);
}
//------------------------------------------------------------------------------
// register functions in an object to trace themselves
// example: say you have a 'class' called FooBar, that has a bunch of methods
// in it by virtual of having functions defined on FooBar.prototype. You can
// trace those methods via:
// FunctionTracer.traceFunctionsInObject(FooBar.prototype, "FooBar.");
//------------------------------------------------------------------------------
FunctionTracer.traceFunctionsInObject = function(object, prefix) {
//--------------------------------------------------------------------------
// process the functions in the object
//--------------------------------------------------------------------------
var functions = {};
var getters = {};
var setters = {};
var isES5 = this.__lookupGetter__;
for (var property in object) {
//----------------------------------------------------------------------
// ignore inherited properties
//----------------------------------------------------------------------
if (!object.hasOwnProperty(property))
continue;
//----------------------------------------------------------------------
// check for getters/setters
//----------------------------------------------------------------------
var getter = undefined;
var setter = undefined;
if (isES5) {
getter = object.__lookupGetter__(property);
setter = object.__lookupSetter__(property);
}
var name = prefix + property;
//----------------------------------------------------------------------
// found a getter/setter?
//----------------------------------------------------------------------
if (getter || setter) {
if (getter) {
getters[property] = FunctionTracer.traceFunction(getter, name + "<get>");
}
if (setter) {
setters[property] = FunctionTracer.traceFunction(setter, name + "<set>");
}
}
//----------------------------------------------------------------------
// found a function?
//----------------------------------------------------------------------
else {
var func = object[property];
if (typeof func != "function") continue;
functions[property] = FunctionTracer.traceFunction(func, name);
}
}
//--------------------------------------------------------------------------
// install the wrappers; didn't do this in the original loop since we are
// in some sense mutating the object while iterating on it's properties,
// typically not a safe thing to do
//--------------------------------------------------------------------------
for (var property in functions) {
object[property] = functions[property];
(FunctionTracer.logFunction)("installed tracer: " + prefix + property + "()");
}
for (var property in getters) {
object.__defineGetter__(property, getters[property]);
(FunctionTracer.logFunction)("installed tracer: " + prefix + property + "<get>()");
}
for (var property in setters) {
object.__defineSetter__(property, setters[property]);
(FunctionTracer.logFunction)("installed tracer: " + prefix + property + "<set>()");
}
}
@paulur
Copy link

paulur commented Apr 25, 2010

Hi Patrick,

The idea of this piece of code is cool and I'd really like to use it in my work! I've tried run your code to trace some functions, but there was nothing coming out. Just wondering whether there is anything wrong of the way of using this function? Here's my code:

<html>
<head>
<script type="text/javascript" src="function-tracer.js"></script>
<script type="text/javascript">
function Apple (type) {
    this.type = type;
    this.getInfo = function(info) {
        var msg= info + this.type + ' apple';
        console.log(msg);
    };  
}

FunctionTracer.traceFunctionsInObject(Apple.prototype, "Apple.");
</script>

</head>

<body>
    <FORM>
    name: <INPUT type= "textfield" onclick="var apple = new Apple('big'); apple.getInfo('The info: ')"><br>
    </FORM>
</body>
</html>

Thanks,
Paul

@pmuellr
Copy link
Author

pmuellr commented Apr 25, 2010

Your code is setting tracing up for functions in the Apple.prototype object, but that object doesn't have any functions in it. To trace your "getInfo" function, you would need to call traceFunctionsInObject() for every instance of Apple you create, or you could move "getInfo" into Apple.prototype, and then your invocation of traceFunctionsInObject() would work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment