Logging level with `clojure.tools.logging`


#1

Hi!

I am getting little confused about clojure.tools.logging framework. It all started as myself trying to implement --verbose switch in my little app. I’ve been already using clojure.tools.logging for logging so I started Googling.

It turned out that it is not so obvious, Java logging frameworks are rather configured by .properties files rather than code itself - I was struggling to find any example showing how to enable DEBUG output from the code itself.

Reading about it, things started to seem extremely confusing:

  • clojure.tools.logging is logging framework, supporting multiple backends like log4j and slf4j:

At runtime a specific implementation is selected from, in order, slf4j, Apache commons-logging, log4j, and finally java.util.logging

  • slf4j is again „meta” library, again using one of available backends:

The underlying logging backend is determined at runtime by adding the desired binding to the classpath and may be the standard Sun Java logging package java.util.logging, log4j, logback or tinylog.

So right now I do not even know what logging library I am using. clojure.tools.logging is indeed using slf4j, but that’s where I am losing my track and I know nothing further.

Could somebody explain like I am five what’s going on here and finally how to implement universal --verbose switch (not the CLI part of it, of course) which will be universal no matter what libraries end user have?

Thanks!


#2

Tools.logging is an interface. You need to decide on a logging engine too.

In effect it means Tools.logging can not log things on its own. It delegates to another library for that. So think of it as a wrapper around other logging libraries.

I personally recommend going with LogBack.

LogBack is SLF4J’s default logging engine. SLF4J is also an interface that wraps other logging engines.

It helps to think of history.

First there was java.utils.logging. It had a particular API and could log to files. Being both and interface and engine. Some people felt it didn’t have enough features. So Apache commons-log appeared. It had a different interfaxe and its own logging engine. People felt it was too slow, so log4j 1 came out. Again it wad a different interface and engine. Finally, someone thought, damn its annoying having all these different interfaces for logging and engines. So SLF4J came out to try and create a unified interface over all logging engines. It also created its own logging engine called LogBack. Finally, clojure.tools.logging came out as a common Clojure interface wrapper over all Java logging libraries, interfaces or engine.

But clojure.tools.logging has no logging engine. Its only an empty shell. So you need to add a dependency on a supported Java logging engine it knows how to use for it to actually log anything. Also, it does not wrap over the configuration for the engines, since that would be too hard to unify config over all logging engines as they vary too much.

Alright, so, how do you pick an underlying logging engine?

Basically, you just add them as a dependency to your project. If your project depends on more then one supported logging engine, clojure.tools.logging will use in priority the first one it finds using the order you quoted.

So, go look at your dependencies and see if you depend on a logging engine that clojure.tools.logging supports. Check all the ones there are, there could be more then one. Specifically, you have to look at transitive dependencies too, so you need to run

lein deps :tree

If you find some engines as part of transitive dependencies, exclude them. You don’t want a dependency bringing in a logging engine that suddenly takes priority on yours.

If you have more then one direct depency to some engines, pick one, and remove the others.

I’ll assume you had none. In this case, if you look at the quoted order, java.utils.logging is the last supported engine. And this one comes bundled with Java itself. So if you have no dependencies on any other, that’s the one clojure.tools.logging will use.

Now, I’d recommend LogBack for production uses. In which case, I’d recommend you start getting familiar with it right away. Its fast, doesn’t have any known deadlocks. Is stable. Has great documentation. Is relatively simple to configure. And has a good set of advanced features.

Anyways, at this point, you have to go read the documentation of the logging engine you chose. Each one will have a different way of setting the log level of the default logger.

Or just use Timbre which is a pure Clojure/Script logging interface which comes with its own engine.


#3

If you’re using Timbre (with https://github.com/fzakaria/slf4j-timbre), this may be easy:

Set the TIMBRE_LEVEL env variable to DEBUG before starting the app.

In case you are using Logback (with SLF4j) you can either hardcode the log level DEBUG, or use a variable in the Logback config:

https://logback.qos.ch/manual/configuration.html#sample0

Using variables (processed by Joran configurator – included in Logback 1.3.0) with Logback config involves setting Java system properties ahead of Logback initialization.