Home > Java > Thoughts on Java logging and SLF4J

Thoughts on Java logging and SLF4J

In this post, I will ramble on logging in Java, how it was done in the old days and what can a library like SLF4J can bring.

Logging is a basic need when you create software. Logging use-cases are:

  • debugging the software during development,
  • help diagnose bugs during production
  • trace access for security purposes
  • create data for statistical use
  • etc.

Whatever the use, logs should be detailed, configurable and reliable.

History

Historically, Java logs where done with System.out.println(), System.err.println() or e.printStackTrace() statements. Debug logs where put in System.out and error logs in System.err. In production, both were redirected: the former on the null output, the latter to the desired error log file. So they were useful enough but they suffered from a big drawback: they were not very configurable. It was an all or nothing switch, either log or don’t. You could not focus detailed logs on a particular layer or package.

Log4J came to the rescue. It was everything one could ever want from a logging framework. It invented many concepts still used in today’s logging frameworks (it was the first framework I used so please bear with me if a concept was not invented by it):

  • the concept of Logger so that each logger can be configured independently
  • the concept of Appender so that each appender can log wherever it wants (file, database, message, etc.)
  • the concept of Level so that one can configure the logging (or not) of each message separately

After that, Sun felt the need for a logging feature inside the JDK but, instead of using log4j directly, it created an API inspired by it. However, it was not not so well finished as Log4J. If you want to use JDK 1.4 logging, chances are you’ll have to write your own Appenders – called Handlers – since the only handlers available are Console and File out-of-the-box.

With both frameworks available, you needed to configure both of them since whatever framework you used, there was surely at least one dependency of your project that used the other. Apache Commons Logging is an API bridge that connects itself to supported logging frameworks. Libraries should use commons-logging so that the real framework used is the choice of the project, and is not imposed by the dependencies. This is not always the case, so Commons Logging does not solve the double configuration problem. Morevoer, Commons Logging suffer from some class-loading problems, leading to NoClassDefFoundError errors.

Finally, the lead programer from Log4J quit the project for reasons I will not detail here. He created another logging framework, namely SLF4J, that should have been Log4J v2.

Some strange facts

The following facts are things that bother me with the previous frameworks. They are not drawbacks per se but are worth mentioning:

  • Log4J has Maven dependencies on JMS, Mail and JMX that are not optional, meaning they will appear on your classpath if you do not bother to exclude them
  • Likewise, Commons Logging has Maven dependencies on Avalon (another logging framework), Log4J, LogKit and Servlet API (!) that are not optional
  • A Swing log viewer is included in Log4J jar, even if you it is used in an headless environment, such as a batch or an application server
  • Log4J v1.3 main page redirects to v1.2 while Log4 v2.0 is experimental

Which framework to use ?

Log4J would be the framework of choice (and is for most) but it is no longer developed. Version 1.2 is the reference, version 1.3 is abandoned and version 2.0 is still in its early stage.

Commons Logging is a good choice for a library (as opposed to an application) but I suffered once classloaders issues, and once is enough to veto it (finally, i threw Commons Logging out and used Log4J directly).

JDK 1.4 Logging is a standard and does not raise concurrent versions problems. But it lacks so many features, it cannot be used without redeveloping some such as a database adapter and such. Too bad… but it does not answers the question: which framework to use?

Recently, architects of my company decided for SLF4J. Why such a choice?

SLF4J

SLF4J is not as widespread as Log4J because most architects (and developers alike) know Log4J well and either don’t know about SLF4J, or don’t care and stick to Log4J anyway. Moreover, for most projects Log4J fulfills all logging’s needs. Yet, interestingly enough, Hibernate uses SLF4J. It has some nice features that are not present in Log4J.

Simple syntax

Take the following Log4J example:

LOGGER.debug("Hello " + name);

Since String concatenation is frowned upon, many companies enforce the following syntax, so that the concatenation only does take place when in DEBUG level:

if (logger.isDebugEnabled()) {

  LOGGER.debug("Hello " + name);
}

It avoids String concatenation but it’s a bit heavy, isn’t it? In contrast, SLF4J offers the following simple syntax:

LOGGER.debug("Hello {}", name);

It’s like the first syntax but without the concatenation cost nor the heavy syntax burden.

SLF4J API and implementation

Moreover, SLF4 nicely decouples API from implementation so that you can use the API that works best with your development with the back-end that suits best your production team. For example, you could enforce the use of the SL4J API while letting the production still reuse the old log4j.properties they’ve known for ages. SLF4J’s logging implementation is known as LogKit.

SLF4J bridging

SLF4J has a bridging feature sor you can remove all log4j and commons-logging dependencies from your project’s dependencies and use only SLF4J.

SLF4J offers a JAR for each logging framework: they mimic its API but reroute the calls to the SLF4J API (which in turn uses the real framework). A word of warning: you could run into a cycle so beware to not have the bridging library along the implementation library in your classpath. For example, if you use the Log4J bridge, each Log4J API call will be rerouted to SLF4J. But if the SLF4J Log4J implementation is present, it will be routed back to Log4J then again, and again.

SLF4J API with Log4J implementation

Taking all these facts into account, my advice is to use SLF4J API and Log4J implementation. This way, you still configure logging the old Log4J way but you have access to SLF4J’s simpler API. In order to do so, you’ll have to:

Action Location Description
Add to classpath slf4j-api.jar* Main API without which you cannot use SLF4J
slf4j-log4j.jar* SLF4J Log4J implementation
jul-to-slf4j.jar* Enables rerouting JDK 1.4 logging calls to SLF4J
jcl-over-slf4j.jar* Reroutes commons-logging calls to SLF4J
Remove from classpath commons-logging.jar* Would conflict with commons-logging API in jcl-over-slf4j.jar
SLF4JBridgeHandler.install()** Main application Redirect JDK 1.4 logging calls to SLF4J
* Jar name will likely includes version
** 20% overhead advertised so do only if you need the single entry point and if there are a few calls

In you run inside an application server, you’ll probably have to change its libraries and / or its configuration to reach this situation.

To go further:

email
Send to Kindle
  1. Ashitkin Alexander
    November 22nd, 2009 at 23:04 | #1

    Hi Nicolas, thanks for your article. But after i’ve been read it i have a question – i should removedependencies from all projects relied on commons-logging?

  2. November 22nd, 2009 at 23:21 | #2

    Hi Alexander,

    You should remove commons-logging dependencies if 1. you want SLF4J to handle your log calls and 2. you provide jcl-over-slf4j.jar on the classpath. Hope I answered your question.

  3. November 23rd, 2009 at 00:37 | #3

    Note that Logger.isDebugEnabled() doesn’t just avoid expensive String concatenation, but all expensive operations. So even with Slf4j you may still require such code blocks for cases where the log output requires an expensive operation, eg.

    if (LOGGER.isDebugEnabled() {
    LOGGER.debug(“Expensive result: {}”, someObject.getSomeExpensiveResult());
    }

  4. November 23rd, 2009 at 19:10 | #4

    You should distinguish between logging wrappers (commons-logging, SLF4J) and logging implementations (log4j, JDK logging). There are reasons to choose wrappers over native implementations, and to choose between wrappers. Wrappers let you write logging code without worrying about concrete implementations: important if your code is meant to be used by third party apps that may have their own choice of logging implementation. Otherwise, they may be redundant. There are also newer implementations like logback that implement existing wrapper APIs. I generally configure both SLF4J and commons-logging to log through log4j so things don’t get scattered between different log files.

    I blogged about this topic here:

    http://chriswongdevblog.blogspot.com/2009/09/logging-should-i-wrap.html

  5. kk
    November 23rd, 2009 at 19:59 | #5

    “Remove from classpath: log4.jar Would cycle calls from SLF4J to Log4J and back again”

    Slf4j doesn’t do any logging itself. slf4j-log4j *uses log4j* to do the actual logging and thus log4j.jar is required if using slf4j-log4j. See slf4j manual -> “Binding with a logging framework at deployment time”.

  6. November 23rd, 2009 at 22:15 | #6

    @Chris Wong
    Your point on the difference between wrappers and implementations is valid. Yet from a developer point of view, you call the API (which are all very similar) and magic happens. That was not the reason of the post, anyway.

    Choosing to redirect commons-logging directly to log4j is a personal choice. I prefer to manage the classpath than 2 parallel properties files.

  7. November 23rd, 2009 at 22:15 | #7

    @kk
    Must have been lack of sleep or whatever: sorry about the mistake. This proves I should always make a prototype project synchronized with my post.

  8. Brian E
    June 18th, 2010 at 01:08 | #8

    Thanks for the post. However the code sections don’t render properly in firefox 3.6.3. I was able to look at the source but just a FYI.

  9. June 18th, 2010 at 18:46 | #9

    It should be corrected now. Thanks for the information, I changed the way I highlighted code but I was too lazy to look for regression in all articles.

  10. September 27th, 2010 at 20:06 | #10

    Hi Nicolas, thanks for sharing your thoughts.
    Just one question, to which version of jcl do you refer in your strange facts?
    The strange dependencies are marked as optional for commons logging version 1.1.1 which was released in Nov 2007. If your experience is with an older version this might be a little bit misleading.

  11. September 27th, 2010 at 20:22 | #11

    @Stefan
    Thanks for your comment. Based on plain old facts, like the date, you’re right. And yet you know as I do that most frameworks do not upgrade their dependencies when a new version is out. Besides (but that was not my initial point), for newer versions, making a dependency optional in Maven is not a thing to do. Never! So I guess it’s still strange…

  12. Tarik Makhija
    March 9th, 2012 at 12:02 | #12

    We are using Weblogic 9.2 and Log4j for logging the application logs
    Our Application is used by Multiple testers.
    All the Application Logs go to common Log file “APP.log”
    So each tester have to scroll down through the file to find his/her each piece of log.

    For Weblogic 9.2 Server We have file called “config.xml” , This File gets updated when anything is changed or updated from the Weblogic Console.

    In this “config.xml” File , We have a Server setting like below :
    -Dlog4j.configuration=file:/srvrs/dev/apacheLog4jCfg.xml

    This Setting is referring to File called “apacheLog4jCfg.xml” Which means that Weblogic Server is using apache Log4j to do the logging.

    In “apacheLog4jCfg.xml” File We have the setting like below :

    The Setting “” will log all the application logs to the File “APP.log”
    i.e All the testers will hit the application and all the logs will get stored in the File “APP.log”

    I have two questions below :
    Q1 : So Using log4j , Is it possible that for each Tester testing the application , a separate log file should be created, So that he/she can see their own set of logs ?

    Q2 : Is it possible to instruct the Weblogic Container or do Server side setting to use log4j at one point of time for logging to samelog file for all users OR if we want to use sl4j to log to different log file for different users ?

  13. Tarik Makhija
    March 9th, 2012 at 12:05 | #13

    In apacheLog4jCfg.xml , the setting is like below :

  14. Tarik Makhija
    March 9th, 2012 at 12:06 | #14

    appender name is APPLOGFILE class is org.apache.log4j.RollingFileAppender
    param name is File and value is APP.log

  15. August 17th, 2012 at 07:16 | #15

    how do i print the log info to the file using slf4j

  1. No trackbacks yet.