Skip to content

Instantly share code, notes, and snippets.

@eoftedal
Created December 9, 2014 19:48
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save eoftedal/25c3b33ed1a8c56f16f5 to your computer and use it in GitHub Desktop.
Save eoftedal/25c3b33ed1a8c56f16f5 to your computer and use it in GitHub Desktop.
Performance logging in Chrome
function perfLog(fun, context) {
var context = context || this;
var realFun = context[fun.name];
context[fun.name] = function() {
console.time(fun.name);
var result = realFun.apply(context, arguments);
console.timeEnd(fun.name);
return result;
}
}
//How to use
perfLog(eval);
perfLog(document.body.appendChild, document.body);
@lillesand
Copy link

Cool stuffs! I like the simple fix of taking in context to redefine the function within the context.

@mikaelbr
Copy link

mikaelbr commented Dec 9, 2014

Cool idea to do logging on existing methods and native APIs! This AOP-ish way of doing it can also easily be extended to have a debug-flag or regex-pattern where can filter out different log-statments to easier drill down on specific log-messages (all though you can have this functionality in for instance Developer Tools in Chrome).

In many cases, though, in the current JS implementation this wouldn't give expected result. Actually, you might end up overriding a lot of methods and re-naming them as empty string. Let's say we have an object literal:

var o = {
   foo: function () {
      // some implementation
   }
};

The name of o.foo in this case would be "" (empty string). This is due to the function being name-less (anonymous). Doing this, however, would work:

var o = {
   foo: function foo () {
      // some implementation
   }
};

Never fear, ECMAScript 6 to the rescue! In the new spec this is handled much better, as name is much more formalised (see this article: http://bocoup.com/weblog/whats-in-a-function-name/). This means that the code above would work as expected, and even:

var foo = function () { };
foo.name; //=> "foo"

Until then it might be a good idea to add a warning to the function:

function perfLog(fun, context) {
    context = context || this;
    if (!fun.name) {
        console.warn('No name found. Dropping performance logging for function:\n', fun.toString());
        return void 0;
    }
    var realFun = context[fun.name];
    return context[fun.name] = function() {
        console.time(fun.name);
        var result = realFun.apply(context, arguments);
        console.timeEnd(fun.name);
        return result;
    }
}

//How to use
perfLog(eval);
perfLog(document.body.appendChild, document.body);

That being said, this is probably not a side-effect you would want in production code, as it can affect things way outside your own code base and third party code. 😄

Maybe a more boring approach, but yet traditional:

function perfLog(fn) {
    var name = fn.name || "anon function";
    return function() {
        console.time(name);
        var result = fn.apply(this, arguments);
        console.timeEnd(name);
        return result;
    }
}

document.body.appendChild = perfLog(document.body.appendChild);

This might not look as smooth, but it works with anonymous methods/functions 👔

> var o = { foo : function () { console.log(this.bar);}, bar : "Hello" };
< undefined
> o.foo = perfLog(o.foo);
< function () {
        console.time(name);
        var result = fn.apply(this, arguments);
        console.timeEnd(name);
        return result;
    }
> o.foo();
   "Hello"
   "anon function: 0.320ms"
< undefined

@eoftedal
Copy link
Author

eoftedal commented Dec 9, 2014

Wow @mikaelbr. That's awesome feedback! Thanks!

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