Monday, December 8, 2014

Logs are like onions

Or, What underlying implementation is clojure.tools.logging using?

Today I want to change the logging configuration of a Clojure program. Where is that configuration located? Changing the obvious resources/log4j.properties doesn't seem to change the program's behavior.

The program uses clojure.tools.logging, but that's a wrapper around four different underlying implementations. Each of those implementations has its own ideas about configuration. How can I find out which one it uses?

Add a println to your program[1] to output this:
(.name clojure.tools.logging/*logger-factory*)         
In my case the output is:

org.slf4j
This is clojure logging's first choice of factories. If it can instantiate this, it'll use it. Now I can google slf4j and find that it... is also a facade on top of multiple logging implementations.


Digging into the slf4j source code reveals this trick:
(class (org.slf4j.LoggerFactory/getILoggerFactory)) 
which prints:
org.slf4j.impl.Log4jLoggerFactory
so hey! I am using log4j after all! Now why doesn't it pick up resources/log4j.properties?
Crawling through the log4j 1.2 (slf4j seems to use this version) source code suggests this[2]:
(org.apache.log4j.helpers.Loader/getResource "log4j.properties")
which gives me
#<URL file:/Users/jessitron/.../resources/log4j.properties>

So hey, I finally have a way to trace where logging configuration comes from! 

In the end, my guess of resources/log4j.properties was correct. I forgot to rebuild the uberjar that I was running. The uberjar found the properties file in itself:
jar:file:/Users/jessitron/.../target/program-0.1.0-SNAPSHOT-standalone.jar!/log4j.properties
Bet I'd have realized that a few hours earlier if I were pairing today. And then I wouldn't have made this lovely post.

----------
[1] or run it in the cider REPL in emacs, in your namespace
[2] actually it checks for log4j.xml first; if that's found it'll choose the xml file over the .properties.

No comments:

Post a Comment