10 Comments

Taking Control of Logging in Clojure

Clojure inherits some interesting tradeoffs as a result of being built atop the Java Virtual Machine. One upside is the availability of many full-featured and mature Java libraries. But one downside is the need to survey the historical as well as technical landscape of your available choices.

Unsurprisingly, when it came time to add proper logging to my project, I had to make sense of the several available Java logging libraries. Here’s what I found.

Use clojure.tools.logging

You should absolutely use Clojure’s tools.logging library. Like most Clojure libraries, it provides the right balance of simplicity with just the complexity you really need. Unfortunately, one of those needed complexities is a dependency on an underlying Java logging library. It uses, in order of preference:

  • slf4j
  • commons-logging
  • log4j
  • java.util.logging

So now you must make a choice, or (through inaction) choose Java’s built in util.logging. (Thankfully, that’s probably a pretty reasonable choice as long as you’re not logging a ton of information and don’t need finer control over what gets logged.)

log4j?

If you’re like me, your attention might first gravitate toward log4j, simply because it’s one you recognize as widely used. Nevertheless, it’s likely not your best choice for a new project. It’s not maintained, and it absolutely won’t function without you first spending your time giving it some configuration that it should have just used by default anyway. Worse, it depends on some packages that appear not to be in maven, so you have to explicitly exclude those in your project.clj dependencies.

slf4j?

Given that tools.logging selects slf4j as its first preference, I made this my next area of investigation. I discovered that slf4j is not a logging library; it’s actually an API meant to wrap a variety of other logging libraries. At this point, I was left feeling mildly disgusted at the idea of using a logging library that wraps another logging library that wraps other logging libraries. Worse, most of the Clojure examples I found in my searching were actually using slf4j to wrap log4j.

The solution: logback

With some more digging, I found out that the folks that made slf4 also made log4j, and that they have also made a new logging library meant to replace log4j. This library is logback. Naturally, logback natively supports the slf4j API. It also appears to provide all the flexibility I could want, while still providing entirely sane defaults. No need to spend my time up front configuring it, before I have any actual problems to solve.

Conclusion

Use clojure.tools.logging with logback. Just add these two dependencies to your project.clj:

  :dependencies [...
                 [org.clojure/tools.logging "0.3.1"]
                 ; No need to specify slf4j-api, it’s required by logback
                 [ch.qos.logback/logback-classic "1.1.3"]
                 ...]

It’s not thrilling or enjoyable to spend your time hunting down the best way to add a glorified println to your project instead of actually getting real work done. I hope this will help others who find themselves in this situation in the future.