null

NullPointerException, probably…

Unified (as much as possible) Logging Using SLF4J

with 7 comments

Integrating, integrating, integrating. That’s what we do in Java enterprise development. Persisting objects with Hibernate wrapped by JPA using C3Po (or JTA?) (or MongoDB over Morphia?), processed with JBMP, created by JAXB (jackson-json?) from JAX-RS scheduled by Quartz … (a few dozen frameworks later) … all this glued with Spring (or Guice?) deployed on Jetty (or Tomcat, JBoss, Resin?) into cluster by Terracotta (or Hadoop, GigaSpaces, JBoss cache, Infinispam?). Ah, and all this built using Maven Gradle with Artifactory on Jenkins. I sure forgot ½ of the frameworks we constantly use.

Generally we don’t mind much about the internals of the frameworks we use (as long as they are good) – the whole encapsulation stuff is the last undoubted good thing. But except for the API (part of which is the configuration) frameworks have another user-facing end – the logging. When we build a system we want it to behave as one system – single configuration from one end, and single log from another (break it to different files, if you wish, but it should still be a unified logging system).

The reality is that there is no standard de-facto for logging. The standard de-jure – JUL, is not very popular because of its lack of functionality (compared to alternatives) and its suboptimal performance. And then there is Log4J, which almost became standard, but did not. And there is logback, which is a Log4J trashover, and there are facades (JCL and SLF4J), which try to unite all this zoo, and some others, which you have probably never heard of, like syslog4j*, logging framework by the Object Guy, jLo, MonoLog, Lumberjack, Houston, JTraceDump, qflog, LN2, TracingClassLoader, SMTPHandler, Log4Ant, Simple Log, Log Bridge, Craftsman Spy, Pencil, JDLabAgent, Trace Log, JDBC Logger, LimpidLog and Microlog.

Let it be, you’d say – why not have many logging tools, which are good and diverse! Well, the problem, as I’ve already mentioned, is that they leak out of the frameworks. Their diverse configuration leaks from one end, while their diverse output from another. Spring uses Log4J over JCL. So does Hibernate. Jetty uses Logback over SLF4J. Some (like Terracotta modules) use plain Log4J, Jersey uses JUL.  This means we end up with 5 separate configurations (Log4J, SLF4J, Logback, JCL and JUL) and 3 different types of log files (Log4j, Logback and JUL). What a system!

To make the long story short – How can we achieve the desired consolidation? Clearly, we need a facade. There are two most commonly used – SLF4J and JCL. JCL is known for its classloader hell, SLF4J is newer, better performing, smarter, simplier to use and generally provides better quality for the same buck (well, no buck – both are open source, of course), so we’ll stick to it. SLF4J is an adapter – thin layer of API to and from different logging implementations. Yap, both ways. It means with SLF4J we can use JUL API on top and log using Log4J in the bottom!

First we need to pick an actual logger. Log4j was considered the best choice up until recently (2006) when Ceki Gülcü decided he needed a fresh start and rewrote from scratch a new Java logging framework, just better than log4j, called Logback. We can give it a try as our underlying logging implementation (we can switch in a moment, as we are using  good facade, remember?).

So, here’s what we have to do:

    1. Establish our own good logging:
        1. Add Logback to our classpath
        2. Add SLF4J API to our classpath

      Done here. Now our own brand new code will use top-notch logging.

    2. Now for the tricky part. Let’s make the example stack I listed above taking configuration from one source (our config files) and writing to one target (files, listed in our configuration)
      1. All the tools using SLF4J will just work. That includes dozen of Apache projects, inc. Camel and Mina, some SpringSource projects and many others.
      2. Now let’s start rolling with all the rest. This is how you do it (click to enlarge):
        Bridging architecture
        1. Jakarta Commons Logging:
          1. Remove commons-logging.jar from your classpath. Usually, it is transitive dependency from the framework, so you need to instruct your build tool on how to do it. What a lucky coincidence, I just wrote short and instructive blog post about how to do it!
          2. Add jcl-over-slf4j.jar instead. It contains alternative commons-logging API implementation, so the code will run just fine.
        2. Log4J:
          1. Same goes here! Remove log4j.jar from your classpath (Again, it would usually be a transitive dependency from the framework, look here).
          2. Add log4j-over-slf4j.jar instead. It contains alternative log4j API implementation, so the code will run just fine.
        3. JUL:
          1. Well, you can’t remove JUL from classpath (it’s a part of the JRE, dude). For the same reason SLF4J can’t reimplement JUL’s API.
          2. Add jul-to-slf4j.jar. It will translate java.util.logging.LogRecord objects into their SLF4J equivalent.
          3. Install SLF4JBridgeHandler and LevelChangePropagator.
          4. Expect 20% decrease in performance (so use it wisely).

All done. Now both our code and all the 3rd paries configured from single source and write to single target. Hooray!

* syslog4j claims it is cross-platform. Well,  I’ll just quote: “Is Syslog4j cross-platform? Yes! Syslog4j UDP/IP and TCP/IP clients should work in any typical Java JRE environment.”

Written by JBaruch

22/06/2011 at 08:40

Posted in Frameworks

Tagged with , , , , ,

7 Responses

Subscribe to comments with RSS.

  1. It’s important that you code against the SLF4J-api. Treat the API as an API, and then at runtime, select and implementation: Logback.

    Because slf4j-api is nothing but a set of interfaces, it’s perfect for ‘library projects’. Let your users decide what they want! SLF4J lets your users pick what logging framework they want to use!

    Jonathan Fisher

    22/06/2011 at 16:50

    • Well, the focus of my article is a little bit different, but your point is correct and very important. Thank you for stressing it!

      jbaruch

      23/06/2011 at 06:24

      • I’ll also point out that if you add Logback to the classpath, you don’t have to bother adding SLFJ, since Logback natively implements the SLFJ API (and thus includes it as a dependency).

        Dave

        23/06/2011 at 17:04

      • That’s true, but it looses some declarative flexibility, I’d say. The idea is keeping SLF4J and replacing the underlying implementation. Once I have SLF4J+Logback, replacing Logback with log4j (for example) will work, but in your solution replacing Logback won’t work.

        jbaruch

        23/06/2011 at 18:14

  2. If you add log4j-over-slf4j.jar and remove log4j.jar, as mentionned in 2.2, I think that the “logging framework of your choice” can’t be log4j, as described in the bridging.png schema :
    You must add slf4j-log4j12.jar, resulting in an infinite loop and StackOverflowError.

    Am I missing something ?

    Bernard

    17/08/2011 at 11:09

    • That’s perfectly correct. If you want to use log4j as the actual logging framework and some of your tools use log4j as logging API, just leave the log4j jar in place. Your code, which uses SLF4j as API will just work.

      jbaruch

      17/08/2011 at 11:36

  3. Great article. It made gathering up my spaghetti mess of component loggers into a neat little package a breeze. Thank you!

    ckm

    12/02/2013 at 22:09


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: