Saturday, April 16, 2011

Lazy Logging on Javascript (2)

A few weeks ago, I wrote about how to do lazy logging in Javascript by passing functions to the logging functions. These functions would actually do the CPU intensive evaluation (if needed) and return something that would subsequently be printed on the screen. The key thing to notice is that the item(s) to be printed would be computed ONLY if they were actually going to be printed (using the current logging level and the 1st parameter to the logging function).

I noticed that a lot of code had started looking quite ugly.
log_it("DEBUG", function() {
  return [ some_var1.toString(), some_var2.toString() ];
});

Note: Any expensive computation that should be performed MUST go inside the toString() method of some_var1 and some_var2.

I have instead decided to solve it by making a function do what the caller was earlier doing:
// toString() Promise
function tsp() {
  var args = arguments;
  return {
    toString: function() {
      return Array.prototype.slice.call(args).map(function(v) {
        return v.toString();
      }).join(' ');
    }
  };
}

As a result of this modification, all code that does logging now looks like this:
log_it("DEBUG", tsp(some_var1, some_var2));

The reason why this works is because log_it() is going to call toString() (if required) on each of its arguments before printing them.

Much neater eh?

3 comments:

gramki said...

is in't it simpler to overwrite log_it? Any way you are assuming that there is a toString which the log_it/tsp is going to use.

log_it("DEBUG", tsp(some_var1, some_var2)) can then be easily converted to

log_it_lazy("DEBUG", some_var1, some_var2)

dhruv said...

Yes, that's a fair point. I should have expressed the example more clearly. The problem I was trying to solve was formatted output.

So, tsp is actually sprintfd()

Typical usage looks like this:

log_it("DEBUG", sprintfd("Hello %s. Welcome to %s. XML: %s", name, city, xml_node));

No idea how to accomplish this w/o the tsp() proxy function.

* sprintfd() is like a delayed/on-demand sprintf() that evaluates its arguments (and performs formatted output) only when toString() is called on its return value

Hope this makes things clearer.

dhruv said...

Also, I forgot to mention that log_it_lazy() would mean that I can only do what log_it_lazy() does (say formatted printf style output) and nothing more. If I wanted to switch to another style of formatted output, log_it_lazy() would have to change. With the decoupled way however, I am free to change the formatting function any time later w/o affecting other users - and hence keep the API the same.