How do you personally do logging?

I’m new to the logging game, but it’s past time for me to jump in. What solutions do you use and why? So far the two dominant approaches seem to be timbre and clojure/tools.logging, but the latter seems to be just a wrapper that leaves you to make another decision in Java-land.

What logging solution do you use, how, and why?


Timbre, because it is very quick to set up, easy to configure from within my code without having to write XML files and place them in magic locations and there are integrations that redirect many other Java logging libraries through Timbre, which makes for a nice unified output.


So, Clojure/tools.logging is built in, but requires some arcane outside knowledge to customize. timbre is a new package, but works all-Clojure, without magic? Definitely sounds like a win for Timbre, unless you’re already a user of Java logging

I’ve heard, but can’t personally comment, that Timbre suffers from some issues at scale which may make it less then ideal for big production systems. I think that was from some convo on slack about it.

I’d say the combination of tools.logging and logback is pretty simple and works amazingly well at all scales, without known issues.

Just add: ch.qos.logback/logback-core, ch.qos.logback/logback-classic and org.slf4j/slf4j-api to your dependencies. As well as tools.logging.

Then you’re good to go. Just log things, and you should see it logged to the console. When you’re ready to move the logging to a file, refer to:

Simplest way is to create a logback.xml file in your resource folder, or a logback.groovy if you prefer groovy syntax over xml for configuration, here’s the doc for groovy configs

I personally prefer the xml config, even though it’s a bit annoying to type all these open and closed tags.

That’s it. You should also be able to find quite a lot of info on logback, example configs, how tos, etc. In all cases, you can ignore all the Java.

Good Luck


This reminded me of a post on the Juxt blog some time ago:

From my perspective there are two important points in that post: That logging data is better than logging strings, and that a logger is really not an advanced concept. Logging data structures instead of strings has definitely helped me think about the way I do logging, and combined with something like Riemann it provides a lot of insight. For simple console logging it is always possible to turn your data structure into a good log string. For the second part, I had great success logging using a protocol implementation much like the one described in the post.

What I am getting at is that rolling your own logging system is not reinventing the wheel, it is implementing a protocol. Of course, not every solution works for everyone, and some projects might benefit from an advanced logging system, but my guess is that a lot of projects are very well off with a small and custom system for logging.

Analyse your problem and project, and choose whichever solution looks like it will make you more productive and give you fewer headaches.

1 Like

I don’t think this is a fair assesement. Though I agree with the use of machine parsable log formats.

The biggest challenges with logging are performance and compatibility. For the latter, it’s a case of when you use a framework or library, and you want logs documenting their activity. You can’t insert logs into their code base. So they need to already have log statements in place to debug them. But then, you need a generic log interface which you are still in control of configuring, so you can specify where their logs should go, what level to log at, if to log at all, etc.

This was the first drive towards a common log interface in Java. To solve the compatibility problem.

The former, that of performance, makes the compatibility one harder. Because certain interfaces aren’t powerful enough to support all the features needed for performance. This for example is why log4j2 has a new interface, and isn’t using slf4j. There were also issues at first where the implementation and interface were coupled, and when the implementation turned out to be too slow, it couldn’t be swapped out. So whole new decoupled interface to implementation were created.

At which point, things fragmented. So different libraries and frameworks used different interfaces and even implementation and that’s when all the log adapters came out to unify the fragmentation.

Now, going back to the article suggesting the use of a simple protocol. That is proposing a common interface. Already, it’s an interface that can’t support all the performance requirements, because protocol function calls will always evaluate the arguments. What if debug level is disabled, and you don’t want the overhead of the map creation and string concatenation of all the debug level log statements? And now someone creates a second interface, and we’re back at Java’s issue of fragmentation again.

Also, tools.logger already has such a protocol, though with a few more features. But its wrapped in a macro so that log statements only evaluate when necessary.

And finally, implementing performant and concurrent logging to a file with rotation isn’t trivial.

Bottom line, logging is easy for easy use cases, and hard for hard use cases.

There’s a reason why Google just came out with one more logging API for Java


This is a very good point, and in my opinion the best reason to use a logging framework.

I will have to look at flogger. What is your opinion on it?

I second @didibus recommendation. That’s exactly what I do.

I’ve had dependency issues with Timbre years ago which I’m sure are all solved by now, but that forced me to understand the underlying Java infrastructure, and I haven’t looked back (didn’t need to).

I like to use @pyr’s unilog. It mostly just gets out of your way while handling some of the Java decisions that tools.logging didn’t make for you.


(Disclaimer: Shameless plug - I wrote Cambium.)

I prefer because at some point you need to machine-process the data from the logs, and it comes with battle-tested SLF4j/Logback integration.


Thanks for the kind words @martinklepsch!

@kumarshantanu I think unilog and cambium are complementary in this case. Unilog is here to serve two purposes:

  • Allowing you to configure sl4j/log4j/logback with data instead of XML files, making configuration management easy and allowing you to keep logging configuration close to the rest of your app’s configuration.
  • Providing idiomatic access to MDC functionality with support for capturing context across threads

Beyond that it has no opinions on what happens once you start logging which is where cambium can help.


At World Singles Networks we use Timbre. We tried a bunch of things before we ended up there and I will offer the caveat that we don’t like Timbre but we like the other things we tried even less :slight_smile:

What’s good about Timbre?

  • It’s easy to get ALL logging to flow through it – when you’re using a lot of Java libraries that all have different ideas about how to do logging, that’s very helpful.
  • It’s easy to programmatically adjust every aspect of logging while the program is running – so you can REPL into a live app, dial up the logging in some areas to debug an issue, and then dial it back down again.
  • logged-future – a macro to use in place of future so that any exceptions thrown in your futures are logged in a standardized manner.

What’s bad about Timbre?

  • The dependencies – the taoensso libraries are a bit of a kitchen sink and some of them are widely used and so you end up using exclusions and pinning several transitive deps.
  • It sidesteps all of the “standard” Java logging stuff (a pro and a con really).

+INF to what @didibus said: most ops folks I’ve worked with have plenty of experience wrangling the (messy) world of Java logging, and so using a library that’s configured by and emits standard Java logging artifacts is a good way to keep those folks happy. They are the “customers” of logging, after all.

FWIW I usually use org.clojure/tools.logging with SLF4J, logback, and SLF4J’s various logging bridges. You also need this static somewhere in order to force Java libraries that use java.util.logging to emit via SLF4J:

; Because java.util.logging is a hot mess

At Magnet we use:

+1. All the same reasons.