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.
One. Clear. Path.
Chaos and lost log statements.
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.
When External Dependencies Won’t Agree
When external dependencies have a different opinion about logging, it comes down to either exclusion or 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.
The same idea applies to Java Util Logging but there are performance implications. Fortunately, we no longer encounter dependencies with JUL.
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.
Same goes for log4j. We exclude it from external dependencies via Maven.
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.
Originally published at Collective Scientific