Java Logging: Herding Text-Based Cats

Java has a rough history with logging. Over the years, several new standards and "de-facto" standards have been created, yet most older standards remain in use. We often encounter a combination of current and vintage logging implementations as we pull in dependencies. 

  • Java Util Logging (JUL) - Older library that is built into Java. Works, but lacks options. Never really caught on. 

  • Apache Commons Logging - Apache library for logging. To add confusion, this is usually referred to as JCL because it was historically called Jakarta Commons Logging. Is used by several AWS Java client jars. 

  • Log4j - Also from Apache. Popular option. The Chemistry Development Kit's default logging implementation is backed by log4j. 

  • SLF4J - Generally just the logging interface with actual logging being deferred to a separate implementation. 

  • Logback - Implementation of SLF4J that can also function as an implementation for the other logging types. 

Yes, all of these come into play once a Java app gets big and deals with enough external dependencies. Yes, they conflict with each other. Yes, this is terrible. 

What Happens in Logging Soup

With conflicting implementations come multiple logging formats and files, nearly unmanageable logging configurations, and seemingly unpredictable behavior. Logging statements disappear for lack of an implementation configured for the chosen API. Errors go unseen. File management gets difficult. Logging becomes useless. 

ideal case.png

Ideal Case

One. Clear. Path:

reality.png

Reality

Chaos and lost log statements.

goal.png

Goal

Get the other logging frameworks to push data through the same pipeline as the rest of our code. 

Pick One Path and Stick with It

We picked SLF4J as our logging API. It's widely used and there are many logging implementations that support it. 

Logback provides many configuration options and a lot of control over when log files roll (How many log files to keep, and how large each can get. A topic for later.). We chose Logback as our logging implementation.  

slf-logback-maven.png

SLF4J + Logback

Example of selecting SLF4J and basic Logback implementation via Maven:

When External Dependencies Won't Agree

When external dependencies have a different opinion about logging, it comes down to either exclusion or bridging. 

Bridging

Bridging means adding a dependency that allows one logging framework to feed into our preferred logging implementation, Logback. 

In the following case, we need a bridge to allow Commons Logging to work through SLF4J. Logback can handle it from there. 

bridge-maven.png

JCL Bridge

Add a bridge via Maven

The same idea applies to Java Util Logging but there are performance implications. Fortunately, we no longer encounter dependencies with JUL. 

Exclusions

Some Amazon Web Service clients bring in commons-logging. This must be excluded so all logging can go through Logback and not through two competing logging implementations. 

exclusion-maven.png

JCL Excluded

Excluding commons-logging with Maven

Same goes for log4j. We exclude it from external dependencies via Maven. 

Conclusion

Java logging takes some time to get right but it can be done. We have had success in standardizing on slf4j and Logback for internal code and applying bridges and exclusions for some dependencies.


Also published on Medium