3

I am building a dirt-simple application so that I can understand slf4j, bridging libraries, logging implementations, etc. My application relies on the slf4j which uses logback for the logging implementation. In addition, it pulls in a library which includes the commons-logging library.

Application dependencies:

  dependencies {
    compile project(':library-with-jcl')

    compile group: 'org.slf4j', name: 'jcl-over-slf4j', version: '1.7.25'
    compile group: 'org.slf4j', name: 'slf4j-api', version: '1.7.25'
    compile group: 'ch.qos.logback', name: 'logback-classic', version: '1.2.3'
}

Library dependencies:

dependencies {
  compile group: 'commons-logging', name: 'commons-logging', version: '1.2'
}

When I do not include jcl-over-slf4j, then the library will output its logging via JCL, as expected. When I do include jcl-over-slf4j, then any logging via the JCL logger is picked up by slf4j and redirected to logback.

I keep reading articles all over the internet that say I must exclude the commons-logging dependency if my application relies on slf4j and a library that depends on JCL. It does not seem to be necessary, however, and I do not understand why. In what situations must I exclude the JCL dependency? Is my example here too simple to expose potential problems?

In addition, I am not seeing suggestions that I must exclude, for example, the log4j dependencies from libraries that depend on it if I am using the log4j bridge. Why not? Is JCL a special case?

JAG
  • 33
  • 1
  • 3

1 Answers1

6

JCL and SLF are both logging facades, i.e. they are just an API abstracting away the logging implementation.

JUL (java.util.logging) and Logback are logging implementations. By default, JCL will call JUL, and SLF will call Logback.

Why would you want two active logging implementations, that needs to be separately configured, and must log to different log files?

You don't, and your application has chosen to use Logback, and has chosen that JCL should call SLF (jcl-over-slf4j), so that it doesn't matter whether the code calls JCL or SLF, logging will be done by Logback.

Therefore, you need to remove (exclude) the duplicate JCL facade added by Library dependencies, that facade is now implemented by jcl-over-slf4j.

Andreas
  • 154,647
  • 11
  • 152
  • 247
  • 2
    I think I am still missing something, so let me ask a follow up question: I don't see any functional difference in my application whether I exclude the duplicate JCL facade or not. Should I remove it simply because it is not needed? Is it a matter of house-keeping, if you will? Or are there functional consequences of not excluding it? In addition, when I include `jcl-overslf4j` I wouldn't need to configure the two active logging implementations -- only the one that slf4j is delegating to. So what am I gaining by excluding JCL? – JAG Dec 11 '18 at 04:21
  • @JAG `commons-logging` and `jcl-over-slf4j` are two different jar files containing classes of the same name, and they behave differently. So, which one will you application be using when you call e.g. `warning()`? *Unknown!!!* --- There is a **conflict** between them, and you should resolve that to prevent unexplained behavior. Since your application *wants* `jcl-over-slf4j`, you need to exclude any transitive dependency on `commons-logging`. --- `jcl-over-slf4j` is specifically *designed* to be a drop-in replacement of `commons-logging`. – Andreas Dec 11 '18 at 04:26
  • Ah, okay, this finally makes sense. Is this not the same case for log4j? I assumed that the `log4j-over-slf4j` library had the same class names as the `log4j` jar, but that it just delegated to slf4j instead of logging. Would I need to exclude `log4j` from any libraries that depend on it if I use `log4j-over-slf4j`? Does that same conflict not exist as with JCL? – JAG Dec 11 '18 at 04:32
  • @JAG That is correct. You must exclude transitive dependency on `log4j` if you include `log4j-over-slf4j`. Be aware of this potential issue: [Using log4j2 with slf4j](https://stackoverflow.com/a/32366618/5221149). – Andreas Dec 11 '18 at 04:36
  • If your project uses a third-party dependency, and that dependency brings in `commons-logging` as a transitive dependency, you can manually exclude `commons-logging` and use `jcl-over-slf4j` in its place. But in some projects `commons-logging` is brought in from numerous dependencies, at various levels, and is difficult to exclude. The [Alternative 3 Empty Artifacts](https://www.slf4j.org/faq.html#excludingJCL) approach is very effective in such cases, giving you an empty `commons-logging` dependency so `jcl-over-slf4j` can do its thing without a conflict. – MikeOnline Nov 18 '22 at 07:19