Tuesday, November 6, 2007

Logging and (simple) closures

I've been wondering recently what certain Java APIs might be like had closures been available when those APIs were designed.

Take logging. Most Java code I've been in contact with over the last few years is littered with statements like this:

log.debug("Received a message '" + msg.getTitle() +
"' with " + entries.size() + " entries");

Fair enough, until someone realises that the code is performing badly because statements like this are being executed 10,000 times per second. So they duly configure their logging API so that it won't log debug messages. But the performance is still poor, and they run the code through a profiler only to see that 50,000 StringBuilders are being created each second, just for log.debug calls that no longer do anything.

Eventually some poor programmer ends up wrapping half the logging code in if statements, wishing that there was some way that log.debug() could check if it's going to do anything first and only then evaluate the expression being passed in.

if (log.isDebugEnabled() {
log.debug("Received a message '" + msg.getTitle() +
"' with " + entries.size() + " entries");
}

Of course there is. We could define an interface

interface MessageBuilder {
String getMessage();
}

and a log.debug() method which accepted that instead, only invoking its getMessage() method if isDebugEnabled() returns true. Unfortunately that's even clumsier in use:

log.debug(new MessageBuilder() {
public String getMessage() {
return "Received a message '" + msg.getTitle() +
"' with " + entries.size() + " entries";
}
});

Not only is it even more verbose, but now we need to make the msg and entries variables final, and you can bet that won't be convenient in all cases!

Try it with closures though:

log.debug({ => "Received a message '" + msg.getTitle() +
"' with " + entries.size() + " entries" });

There are slight variations in syntax depending on whose closures proposal you choose, but basically we're back to one line, no need to make the variables final, and the expression is evaluated only if it's required. Much better.

Closures, at least this kind, are actually about much more than this, but I have to wonder whether the various logging APIs would have been designed to take an interface like MessageBuilder had the resulting code not been so awkward, and how many other APIs were similarly influenced in their design.