why does tools.logging prefer commons logging?

292 views
Skip to first unread message

Stuart Halloway

unread,
Aug 29, 2011, 9:49:04 PM8/29/11
to cloju...@googlegroups.com
SLF4J offers adapter layers that mimic the other logging frameworks and thunk to the SLF4J API (http://www.slf4j.org/legacy.html).

Unfortunately, It is not obvious how to use tools.logging in such a scenario. tools.logging will pick up the bridge version of commons logging, think it has the real commons logging, and configure itself to use the bridge. This is undesirable from a performance perspective.

Is there are reason that commons comes first? Would it be better to provide a path for consumers to be explicit about which lib they want?

Stu

ataggart

unread,
Aug 30, 2011, 11:49:47 AM8/30/11
to cloju...@googlegroups.com
Commons-logging can itself be configured, and for that reason it needed to take precedence over the concrete logging library it might be wrapping.  Checking now though, the current docs don't mention it anymore.

SLF4J was included later by request from someone using logback. Since SLF4J does not have its own configuration it really should go after the known, concrete logging libraries to avoid the unnecessary wrapping.

It was not foreseen that someone would use tools.logging, and SLF4J, and commons-logging, all at the same time.

I suspect that the commons-logging configuration was rarely used (it's not mentioned in the current docs), so in order to avoid minimize wrapping I am inclined to change the order to the following:
log4j
SLF4J
commons-logging
java.util.logging

Does the above look reasonable?

To answer your second question, you can alter/bind the value of *logger-factory* to something else that satisfies the LoggerFactory protocol, e.g., the instance returned from (log4j/load-factory), and that will be the system that gets invoked.

Note that the generated docs do not reflect the latest code.

Laurent PETIT

unread,
Aug 30, 2011, 11:58:47 AM8/30/11
to cloju...@googlegroups.com
2011/8/30 ataggart <alexcloj...@gmail.com>

Commons-logging can itself be configured, and for that reason it needed to take precedence over the concrete logging library it might be wrapping.  Checking now though, the current docs don't mention it anymore.

SLF4J was included later by request from someone using logback. Since SLF4J does not have its own configuration it really should go after the known, concrete logging libraries to avoid the unnecessary wrapping.

It was not foreseen that someone would use tools.logging, and SLF4J, and commons-logging, all at the same time.

I suspect that the commons-logging configuration was rarely used (it's not mentioned in the current docs), so in order to avoid minimize wrapping I am inclined to change the order to the following:
log4j
SLF4J
commons-logging
java.util.logging

I'd say ... no.

Really, only slf4j is doing things right, by separating in different jars the client API and the implementation. This allows librairies to only use the client API, facilitating code composition. And it's only the final application which decides which implementation to use (logback, log4j, etc.).

But since I understand tools.logging must remain compatible with all the logging-related legacy in the java world, I'd say :
1. slf4j
2. commons-logging
3. java.util.logging
4. log4j

(or switch 3 <-> 4)


 

Does the above look reasonable?

To answer your second question, you can alter/bind the value of *logger-factory* to something else that satisfies the LoggerFactory protocol, e.g., the instance returned from (log4j/load-factory), and that will be the system that gets invoked.

Note that the generated docs do not reflect the latest code.

--
You received this message because you are subscribed to the Google Groups "Clojure Dev" group.
To view this discussion on the web visit https://groups.google.com/d/msg/clojure-dev/-/QQ9u4nd8vwcJ.

To post to this group, send email to cloju...@googlegroups.com.
To unsubscribe from this group, send email to clojure-dev...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/clojure-dev?hl=en.

ataggart

unread,
Aug 30, 2011, 2:51:01 PM8/30/11
to cloju...@googlegroups.com


On Tuesday, 30 August 2011 08:58:47 UTC-7, lpetit wrote:
Really, only slf4j is doing things right, by separating in different jars the client API and the implementation.

How is that relevant for clojure code that uses tools.logging?  Why should -- at runtime -- tools.logging wrap SLF4J which in turn wraps log4j?

ataggart

unread,
Aug 30, 2011, 2:53:47 PM8/30/11
to cloju...@googlegroups.com


On Tuesday, 30 August 2011 08:58:47 UTC-7, lpetit wrote:
1. slf4j
2. commons-logging
3. java.util.logging
4. log4j

(or switch 3 <-> 4)

JUL must go last since it's always available.  If some system has commons-logging in the classpath but wants tools.logging to use JUL directly, then they'll need to change *logger-factory*

Alex Miller

unread,
Aug 30, 2011, 3:30:06 PM8/30/11
to cloju...@googlegroups.com
Using slf4j defers the decision to the deployer. If you are in the
context of an app using slf4j consistently, the deployer can choose
once which log library to use across their app by choosing which slf4j
impl to put on their classpath. Using log4j forces the deployer to
deal with a log4j impl regardless of what other libs are using.

In mixed Java lib/app + Clojure envs, using slf4j is the least worst
choice (which is truly the best we can hope for these days). In a
pure Clojure env, pick what you like.

> --
> You received this message because you are subscribed to the Google Groups
> "Clojure Dev" group.
> To view this discussion on the web visit

> https://groups.google.com/d/msg/clojure-dev/-/vna9Dz9DQEUJ.

Stuart Halloway

unread,
Aug 30, 2011, 3:48:13 PM8/30/11
to cloju...@googlegroups.com
> It was not foreseen that someone would use tools.logging, and SLF4J, and commons-logging, all at the same time.

Like most people in a large Java project, I don't have any choice. My set of dependencies forces every single one of the Java logging choices on me, simultaneously.

SLF4J helps by pretending to be all of them.

tools.logging is fooled by SLF4J into thinking I am using commons.logging, and then binds to the commons logging bridge.

Stu

ataggart

unread,
Aug 31, 2011, 5:57:08 PM8/31/11
to cloju...@googlegroups.com
Barring further objection, I will alter the default search order to the following:

1. slf4j
2. commons-logging
3. log4j
4. java.util.logging (must go last)

Sean Corfield

unread,
Oct 11, 2011, 9:18:15 PM10/11/11
to cloju...@googlegroups.com

I finally got around to trying tools.logging 0.2.3 with this change
and ran into a problem. We're using a container that has its own
variant of SLF4J and tools.logging selects that and reports it's using
LOG4J (which is what we really want) but the variant SLF4J throws NPE
trying to get the stack trace from the non-existent exception we're
passing in (nil). Clearly it's a bug in the SLF4J variant (which we
have no control over right now) but I wanted to report it as a data
point.

Luckily we have our own wrapper around tools.logging so I added this:

(alter-var-root (var log/*logger-factory*) (constantly (impl/log4j-factory)))
;; log = clojure.tools.logging, impl = clojure.tools.logging.impl

That seems to work but I wondered how "kosher" it is to do that and
whether there is a better / safer way?
--
Sean A Corfield -- (904) 302-SEAN
An Architect's View -- http://corfield.org/
World Singles, LLC. -- http://worldsingles.com/
Railo Technologies, Inc. -- http://www.getrailo.com/

"Perfection is the enemy of the good."
-- Gustave Flaubert, French realist novelist (1821-1880)

Alexander Taggart

unread,
Oct 11, 2011, 9:37:51 PM10/11/11
to cloju...@googlegroups.com


On Tuesday, 11 October 2011 18:18:15 UTC-7, Sean Corfield wrote:

LOG4J (which is what we really want) but the variant SLF4J throws NPE
trying to get the stack trace from the non-existent exception we're
passing in (nil). 

The api docs aren't clear on the behaviour of calling Logger.foo(msg, ex) when ex is null, but everything I've seen just ignores it, so to save typing, that's what gets called irrespective of the value of the exception.  It's easy enough to avoid, so I'll patch in an explicit branching.

 

That seems to work but I wondered how "kosher" it is to do that and

whether there is a better / safer way?

That's how to change the root var value.  Alternately you can use binding. 

Alexander Taggart

unread,
Oct 11, 2011, 9:47:30 PM10/11/11
to cloju...@googlegroups.com

Sean Corfield

unread,
Oct 11, 2011, 10:13:18 PM10/11/11
to cloju...@googlegroups.com
On Tue, Oct 11, 2011 at 6:47 PM, Alexander Taggart <ma...@ataggart.ca> wrote:
> Done.
> https://github.com/clojure/tools.logging/commit/7694fd2067e3366b63b50dc698c32da17488ea10

Thanx. I'll keep an eye out for 0.2.4 and see if that allows me to
remove the alter-root-var call (I didn't want to add binding to each
of my wrapper functions).

Alexander Taggart

unread,
Oct 12, 2011, 12:02:35 AM10/12/11
to cloju...@googlegroups.com
It'd be great if you could test latest.  I think it's in a position to move towards 1.0.0, but I'd like to get some feedback from those using it, especially the edge cases I hadn't considered when making the API.

Sean Corfield

unread,
Oct 12, 2011, 3:32:22 AM10/12/11
to cloju...@googlegroups.com

That fixes the NPE I was seeing. Thanx.

It still selects the container's variant SLF4J which doesn't pick up
my LOG4J configuration (and custom appender) so I still need the
alter-var-root to force the real LOG4J configuration. That feels
pretty ugly, especially since I have to reach into the impl ns for the
log4j factory. It would be nicer if there was a simple way to tell
tools.logging "Hey, just use this logger instead of trying to deduce
one for me!" so I could just tell it :log4j or "log4j" or something...

Alexander Taggart

unread,
Oct 13, 2011, 2:38:07 AM10/13/11
to cloju...@googlegroups.com
Since the default lookup occurs as soon as the namespace is loaded, the only way to abort that process would be to add some c.t.l-specific configuration, which is a road I'm not prepared to go down.

If full control over the classpath is not an option, and the default behaviour does not do what you want, altering/binding the *logging-factory* var is the way to be explicit about which implementation should be used.

Sean Corfield

unread,
Oct 13, 2011, 4:04:59 AM10/13/11
to cloju...@googlegroups.com
On Wed, Oct 12, 2011 at 11:38 PM, Alexander Taggart <ma...@ataggart.ca> wrote:
> If full control over the classpath is not an option, and the default
> behaviour does not do what you want, altering/binding the *logging-factory*
> var is the way to be explicit about which implementation should be used.

OK, then alter-var-root is the right (but ugly) solution to my problem, yes?

If so, that should be clearly documented for this library.

Alexander Taggart

unread,
Oct 13, 2011, 5:30:11 AM10/13/11
to cloju...@googlegroups.com
From *logger-factory* docs:

An instance satisfying the impl/LoggerFactory protocol. Used internally to
obtain an impl/Logger. Defaults to the value returned from impl/find-factory.


From impl/LoggerFactory protocol docs:

The protocol through which the core api will obtain an instance satisfying Logger
as well as providing information about the particular implementation being used.
Implementations should be bound to *logger-factory* in order to be picked up by
this library.

I'm open to suggestions.

Alexander Taggart

unread,
Oct 13, 2011, 5:34:39 AM10/13/11
to cloju...@googlegroups.com
Also, as for alter-var-root being an ugly way to set a var root, that's something you'll have to bring up with Rich.  I guess you could just re-def it.

Sean Corfield

unread,
Oct 15, 2011, 3:09:32 AM10/15/11
to cloju...@googlegroups.com
On Thu, Oct 13, 2011 at 2:34 AM, Alexander Taggart <ma...@ataggart.ca> wrote:
> Also, as for alter-var-root being an ugly way to set a var root

I meant more that it's a ugly way to tell tools.logging you want to
override the default algorithm for finding a logger, esp. since it
means knowledge of clojure.tools.logging.impl which ought to be an
implementation detail...

How about a function, say (set-logger! logger-name), that takes a
keyword (:auto - default, :log4j, :slf4j, :commons, :jul) and then
have the default factory implementation do its current behavior if
:auto is selected but otherwise have it directly select the
appropriate logger factory.

That would allow for the simple case to be nice and clean - folks who
need to override the default lookup could just call (set-logger!
:log4j) - for example - before they log anything; folks who need to
provide their own logger factory based on the (implementation
detail's) protocol and can alter/re-def *logger-factory*.

Sean Corfield

unread,
Oct 15, 2011, 7:48:09 PM10/15/11
to cloju...@googlegroups.com
BTW, the approach we've used at World Singles for stuff like this is
to use delay on configuration items so I have the chance to override
them safely before using the functions from the namespace. I realized
after re-reading this thread that *logger-factory* is evaluated at
load time (per your "default lookup occurs as soon as the namespace is
loaded" comment) so you couldn't defer that lookup without using delay
- which is why we do that in our code at World Singles.

Regards,
Sean

Reply all
Reply to author
Forward
0 new messages