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.

Conversation
  • Dax Fohl says:

    I’m using https://github.com/ptaoussanis/timbre, and it seems to address all these problems without the dependencies, allowing a more clojuresque API.

  • v1oo says:

    have you look at log4j2 ? http://logging.apache.org/log4j/2.x/
    seems to be similar to logback

  • Brian Marick says:

    I also use timbre, for about two years. It’s worked well. (Disclaimer: I’m not coming from the Java ecosystem, so I wasn’t familiar with all the options before choosing.)

  • Chris Farber Chris Farber says:

    Dax and Brian:

    Thanks for letting me know about timbre. I wish I’d found it before I wrote this article so I could have included it.

    V100:

    Log4j2 is probably a fine choice too. My approach was to look for the easiest setup that didn’t sacrifice configurability or require me to write any XML.

  • crocket says:

    Don’t use timbre. timbre is maintained by a person who think ANSI colors should be enabled by default in log files. The maintainer blocked me from creating new issues in his timbre github repository for wanting to disable ANSI colors in log files.

  • crocket says:

    How do you programmatically configure logback in clojure?
    I used logback before, and there was no easy way to configure logback other than an XML configuration file.

  • Petr says:

    Even if you do not use slf4j directly you still have to have it in your project. No matter how you hate having layers in your application your libraries using loggers. And you want output from them. I bet prospect of having several independent loggers is even worse than having layers.
    By adding adapters of their-preferred-logger to slf4j this problem is solved cleanly: you have several adapters to slf4j and single slf4j implementation of your choice.
    If you saw examples of slf4j over log4j you could just use them and make your choice later. That is the problem slf4j solves.

  • Cory says:

    Logging seems like a huge rabbit hole in this ecosystem. Thank you for giving us a clear example of how to handle it trivially.

  • Comments are closed.