There are quite a few considerations which make logging "more complicated than it seems at first glance", (hence the multi-decade of bitter in-fighting!).
Separation of Concerns
At the end of the day, code 'sends log data' and that data 'ends up somewhere'. But where it ends up depends on the purpose for which it is being gathered. Greatly complicating the picture is the fact that modern software is composed of various components, and they all potentially have a need to log.
Let's consider a worst-case: all components use System.out#println(String)
. At least all the statements are in execution-order, but it might not be simple to discern which component generated each piece of output. And some components might be overly verbose for the context in which they are used.
Let's consider a next-worst-case: all components make their own arrangements for controlling their logging behaviour and destination. The admin might have to configure dozens of log systems for a single piece of software. Now the log statements are not together and they're out of order. Hopefully they all have a consistent timestamp strategy!
We would like something in-between: something whereby code can say 'log this' and an admin can control where it ends up.
An overly brief history
Enter Log4J v1, which tackled the problem with notions of 'levels', 'appenders', 'filters' 'layouts' and 'contexts' ... a conceptual architecture supported by a hierarchical 'logger namespace' (including a way to naturally leverage off Java package namespaces), plus a configuration mechanism for easy management.
That was all fine and good ... so long as all the components in the software relied on the same version! There was a time when these things were in flux. The main contribution of SLF4J was to 'harden' these concepts into a stable API from the point of view of the component-developer, without prejudicing the options of the admin for doing their part of the work. Libraries could rely on the SLF4J 'facade', expecting that they'd be talking to the 'implementation' just a few calls down the stack. Administrators could choose what suited them to compose the logs into a coherent record they cared about.
(It is even more complicated when your software runs in a container, and the container has its own logging needs, and you're not even the same app running in the container ... Tomcat's JULI logging - used for its own internal logging - 'gets out of the way' of apps running in classloader sub-contexts.)
Mysteriously scorning the work by Log4J, the Java Community Process decided to implement much-the-same conceptual architecture in java.util.logging
, with arguably less flexibility in the details. However, with j.u.l
being essentially a sub-set of the semantic richness of SLF4J, it was easy make SLF4J a facade to j.u.l
.
Apache's Commons Util Logging was probably not very necessary. Ceki's own Logback introduced management features which Log4J v1 did not have at the time - not least being an implementation of SLF4J and solving all those very-real classloader headaches, but also providing a competent implementation with some appealing features for the administrator.
Logging for different situations
But logging is done in a lot of different contexts. Whacking those messages out to super-slow I/O without unduly blocking the thread, and not paying the price of computing the log message unless it's needed, and producing coherent logs in multi-threaded contexts ... these things all matter. (Which is why java.util.logging
is not often used!).
Sometimes, the required optimisations will impact on the conceptual architecture, which in turn must impact on the developer-side API. For example, the opportunities presented by closures will definitely speed things up if the log message ends up being a no-op because of filtering. Either a SLF4J.next or some other API needs to be considered to make use of that feature, and Log4J2 need not be excluded from this decision. With the API portion being a conceptual super-set of that offered by SLF4J, it is simple to make it a facade either for SLF4J and those implementations under it ... or more direct bridges to what the administrator has preferred.
For the app-developer, it really doesn't matter, so long as ultimately, there is one logging facility chosen by the administrator, and all the components can get a log out to it. If the facility can accept messages via SLF4J and Log4J2-the-API, then that's great. Log4J2-the-implementation does just this. You can have your cake and eat it too: your application can enjoy the opportunities offered by Log4J2-the-API while still using libraries that are adequately catered-for by SLF4J. If the administrator scorns Log4J2-the-implementation (though it is hard to see why they would, from any angle), then they can use anything that already supports SLF4J without waiting for that logging implementation to support Log4J2-the-API. You can have your cake and eat it.
For library developers, it is more of an issue. The safe path is SLF4J because of its widespread adoption. If logging is critical to the success of your library ... particularly if it is multi-threaded, log statements are potentially expensive to produce, and it might be better to omit the processing if they will not ultimately be consumed, if there is a large volume of log statements to handle, performance is critical, and your users are likely to appreciate the benefits of Log4J2-the-implementation, then do Log4J2. But you're not stealing opportunities from your users by remaining with SLF4J, either. The admin can still use Log4J-the-implmenation if they wish.
The bottom line
If you want the features afforded by Log4J2 then go for them. If you don't need them, SLF4J is a mature, stable interface with a lot of support. SLF4J remains an important piece of the open-source infrastructure. Ceki has done the community a great service, for a lot of griping in return.
But rich APIs supported by competent implementations prevail in the end. Today's stability is tomorrows stagnation. The process of refinement keeps going. No need to get off the bus so long as it is going where you want to go.
slf4j
and logback (or log4jv1)? Should I then be forced to install a third logger to use your application? Or maybe corporate security decides you may only usejava.util.logging
in production, what then? – Ernie