When you log stuff from a Java program if you really don’t know what you’re doing (or don’t care) you’ll use System.out.println(“something to log”). This not too bad for a tiny project. It works and it is very simple to implement.
But the fairly obvious downside it that if you want to turn the logging off you have to go edit the code. Finding all those log statements is a pain.
There are several logging libraries that have been around for years, notably Log4j, which solve this. They usually have these features:
- A configuration file external to the program can turn logging on or off
- You select the logging you want by level, so you can get minimal logging or more detailed logging by changing the external file.
- You select which classes you want to log at whatever detail as well.
- You can change the format to include the logging location if you want so it is easy to find just where the logged message came from.
I started off not liking the way Log4j has to work. You have to define a static in every class to enable logging for that class. It seemed like more work than I wanted to do (yes, just one line, I am really lazy). I built my own logger which worked differently, but I’ve since come around to the Log4j approach if only because all the open source libraries use that as well. Plus I am pretty sure mine was slow.
But there is a subtlety going on with this. You don’t actually want to use Log4j directly because then you commit to using it for all your logging. If some library you want to use has decided to use another logger then you have to have two config files etc. This is bad. What you really want is an interface that delegates to a logger you can chose later.
This is the thinking behind Apache Java Commons Logging (JCL). It is an interface and one of the implementations is Log4j. There are others. Lots of the libraries you want to use call JCL. So one obvious thing to do is put JCL calls into your program and then configure it to use Log4j.
Except that there are two problems with JCL. The first is that the API could be better and the second is that there are class loader problems with JCL. It is inclined to not find the implementation classes (link).
So enter SLF4J. This is a newer interface which is slicker and simpler and doesn’t have the classloader problems. It includes a dummy JCL interface which maps to the SLF4J interface so your libraries will still work. And there are log4j and other implementations. The particularly nice thing about the interface is that you can get away with using fewer isDebugEnabled() method calls because it evaluates the output later than JCL, ie after it had decided whether to log or not. That means your complex log message doesn’t incur any overheads if logging is turned off.
It doesn’t stop there. Like JCL, SLF4J supports several logging implementations apart from log4j. There is Java utils logging (JUL) which is bundled with Java. In a perfect world this would be the one we all use, but in practice almost no one does. This seems to be because it often takes more than a configuration file to set up. You have to write code, and no one wants to. We tried using JUL with Glassfish, using SLF4J. It does work, but we could not find a way to direct different output to different files so we went back to log4j.
But we did hit odd problems with log4j, I forget just what they were now, and they were mostly on JBoss rather than Glassfish. I think the embedded (JBoss) log4j collided with our configuration somewhow.
Then we started using logback. This is a descendent of log4j, so it is an implementation and SLF4J plays happily with it. Logback allows us to have separate configuration files for each application on the same server, plus separate log files.
We’re very comfortable with this combination. There are some tricks to getting logback running optimally, but these are covered well in the online docs. We also realised that not configuring it generates a lot of log messages and slows the application down. This is a fair call on their part though. The default is to log everything, what else could it be? So if you go this way do make sure you supply a logging configuration file.