Innovation in the Log space, yawn…
Steve and I had a discussion yesterday about loggers. I know what you're thinking: hasn't log been a solved problem for years now? Plus it's boring ;)
That's why usually, when a discussion starts on the subject, I tend to carefully not listen. But because it's Steve, and because he has some specific requirement for the next Hibernate Core version, I decided to ignore my own rule.
It turned out to be much more interesting than what I expected. Here are some news for people, like me, that stopped listening to the log crowd when the rule was use log4j and when you can't use commons-logging.
I was pleasantly surprised by slf4j. I know, it's yet another facade and the name is awful. But this project has 2 features that really caught my attention.
Parameterized log
Isn't it very annoying to have to do
if ( log.isDebugEnabled() ) {
log.debug("My dummy " + object1 + " and expensive " + object2 + " concatenation");
}
because the object's toString() method are expensive?
slf4j solves that by using parameterized logs
log.debug("My dummy {} and expensive {} concatenation", object1, object2);
Very elegant and just as efficient as the previous form. Now because slf4j supports JDK 1.3, the API cannot use varargs, which means that for 3 or more parameters you will have to write
log.debug("My dummy {} and expensive {} concatenation {}", new Object[] { object1, object2, object 3 });
instead of the much more elegant
log.debug("My dummy {} and expensive {} concatenation {}", object1, object2, object3);
Damn slow movers! I guess most of the time you have 1 or 2 arguments so the pain should be minimal, or you could write you own facade, sigh
Static binding
Once I understood what it meant, I liked it. Basically to switch from one underlying logger to another, you will replace slf4j-mylogger1.jar by slf4j-mylogger2.jar: the slf4j engine is statically bound to an implementation.
OMG! This means I cannot change my logger implementation by hot deploying a config file! Oh, wait a minute, it's useless anyway.
The good side is that classloader hells are behind us.
The Ultimate Uber Cool solution
The ultimate solution is actually what Gavin came up with in Seam. So instead of doing
private static final Log log = LogFactory.getLog(CreateOrderAction.class);
public Order createOrder(User user, Product product, int quantity) {
if ( log.isDebugEnabled() ) {
log.debug("Creating new order for user: " + user.username() +
" product: " + product.name()
+ " quantity: " + quantity);
}
return new Order(user, product, quantity);
}
you end up with
@Logger private Log log;
public Order createOrder(User user, Product product, int quantity) {
log.debug("Creating new order for user: #{user.username} product: #{product.name} quantity: #0", quantity);
return new Order(user, product, quantity);
}
Notice the parameterized logs, the log injection (yes the framework is smart enough to guess the category, doh!), and the contextual parameters injection.
But such solution is not accessible to library developers until someone decides to push that into the JDK.
OK I'm done for logs for another 5 years time :)
Comments