log every method?

Instead of having to create a custom logger for TRACE, e.g. following what methods were called and what classes were instantiated, is there an easy way to make all methods under a class log themselves? This is for a node.js application.

class MyClass

  constructor: () ->
    console.log 'MyClass:constructor'

  doThat: () ->
    console.log 'MyClass:doThat'

exports.MyClass = MyClass

myClass = new MyClass()
myClass.doThat()

If I had my way, you'd see 4 log messages instead of 2 (thus having to write less code to trace what's happening).

I recently needed to implement something like this to trace some complicated OO-recursive stuff. Basically, i wanted to make a method "traceable" without polluting it too much; so maybe the solution could be applied here too.

First, add a function that makes other functions traceable:

Function::trace = do ->
  makeTracing = (ctorName, fnName, fn) ->
    (args...) ->
      console.log "#{ctorName}:#{fnName}"
      fn.apply @, args
  (arg) ->
    for own name, fn of arg 
      @prototype[name] = makeTracing @name, name, fn

Then, to use it, just add a @trace before each method you want to trace:

class MyClass
  @trace methodA: ->
    @methodB 42

  @trace methodB: ->
    console.log "method b called with #{n}"

Or add @trace only once and then indent all the traceable methods one more level:

class MyClass
  @trace 
    methodA: ->
      @methodB 42

    methodB: (n) ->
      console.log "method b called with #{n}"

As you can see trace is kind of abusing CoffeeScript's syntax. method: -> 'foo' inside a class MyClass is interpreted a method definition. But @trace method: -> 'foo' is interpreted a calling the trace function of MyClass (which is a Function instance, to which we've added the trace function) passing it a literal object with one method key. In JavaScript it would be something like this.trace({method: function() {return 'foo';}}).

The trace function will just take that object and iterate it's keys (the method names) and values (the methods) and add functions to the MyClass prototype that log their calls and in turn call the original methods.

Anyway, the output of (new MyClass).methodA() will be:

MyClass:methodA
MyClass:methodB
method b called with 42

This solution doesn't work for constructors though, as they are not just normal methods.

You can get quite fancy with this. You can also log the arguments passed to each method, the return value, and even add indentation for nested calls if you so desire (the resulting traces can then be very helpful if you need to debug a complex problem =D).


Update: as a more interesting example, here's a mini-version of the typical composite pattern example, geometrical figures and groups of figures: http://jsfiddle.net/2YuE7/ with a more interesting tracing function. All figures understand the move method. If we have this composite figure and call move on it:

f = new Composite [
  new Rectangle 5, 10, 3, 4
  new Composite [
    new Circle 0, 0, 2
    new Circle 0, 0, 4
    new Circle 0, 0, 6
  ]
]

f.move 2, 3

The trace output is:

(Composite[Rectangle[5,10,3,4],Composite[Circle[0,0,2],Circle[0,0,4],Circle[0,0,6]]]).move(2, 3)
| (Rectangle[5,10,3,4]).move(2, 3)
| -> Rectangle[7,13,3,4]
| (Composite[Circle[0,0,2],Circle[0,0,4],Circle[0,0,6]]).move(2, 3)
| | (Circle[0,0,2]).move(2, 3)
| | -> Circle[2,3,2]
| | (Circle[0,0,4]).move(2, 3)
| | -> Circle[2,3,4]
| | (Circle[0,0,6]).move(2, 3)
| | -> Circle[2,3,6]
| -> Composite[Circle[2,3,2],Circle[2,3,4],Circle[2,3,6]]
-> Composite[Rectangle[7,13,3,4],Composite[Circle[2,3,2],Circle[2,3,4],Circle[2,3,6]]]

If you want to do so something with plain old javascript, I created a service to which you can pass an object which will return an object with the same API that logs each method. See the full example with the polyfill applied at http://jsfiddle.net/mcgraphix/pagkoLjb

The basic idea is:

var api = {
   notAMethod: "blah",
   foo: function() {
    console.log("in foo", arguments);
   },

   bar: function(arg) {
    this.foo(arg);
    return this.notAMethod;
   }
}; 

//wrap the api with a logging version
//to watch property changes and keep them in sync, you need the polyfill for Object.watch
//     from https://gist.github.com/eligrey/384583

var createLogger = function(api) {
     var loggingApi = {};

            for (var prop in api) {
                if (typeof api[prop] !== 'function') {
                   loggingApi[prop] = api[prop]; 
                   loggingApi.watch(prop, function(prop, oldVal, newVal){
                       api[prop] = newVal
                       return newVal;
                   });


                } else {

                    loggingApi[prop] = function() {
                        console.log(prop + "() called with args: ", arguments);
                        var returnVal = api[prop].apply(api, arguments);
                         console.log(prop + "() returned: " + returnVal);
                        return returnVal;
                    }
                }
            }
      return loggingApi;
 };
 api.foo('Shhhh... don\'t log me') //no logging
 createLogger(api).foo(); //with logging