We're in the process of upgrading to spring boot 3 in our application at the moment and we're getting some issues at runtime with logger factories clashing.
We use logback for our logging implementation, but via slf4j.
We're pulling in the spring-boot-starter-logging
module, and not specifying our own versions of slf4j or logback in our build.gradle.
It's a really complicated project with lots of dependencies, but the logging dependencies are all here:
integrationTestRuntimeClasspath - Runtime classpath of source set 'integration test'.
+--- net.logstash.logback:logstash-logback-encoder:6.6
| \--- com.fasterxml.jackson.core:jackson-databind:2.12.0 -> 2.14.2
| +--- com.fasterxml.jackson.core:jackson-annotations:2.14.2
| | \--- com.fasterxml.jackson:jackson-bom:2.14.2
| | +--- com.fasterxml.jackson.core:jackson-annotations:2.14.2 (c)
| | +--- com.fasterxml.jackson.core:jackson-core:2.14.2 (c)
| | +--- com.fasterxml.jackson.core:jackson-databind:2.14.2 (c)
| | +--- com.fasterxml.jackson.datatype:jackson-datatype-guava:2.14.2 (c)
| | +--- com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.14.2 (c)
| | +--- com.fasterxml.jackson.module:jackson-module-parameter-names:2.14.2 (c)
| | +--- com.fasterxml.jackson.dataformat:jackson-dataformat-cbor:2.14.2 (c)
| | +--- com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.14.2 (c)
| | \--- com.fasterxml.jackson.datatype:jackson-datatype-joda:2.14.2 (c)
| +--- com.fasterxml.jackson.core:jackson-core:2.14.2
| | \--- com.fasterxml.jackson:jackson-bom:2.14.2 (*)
| \--- com.fasterxml.jackson:jackson-bom:2.14.2 (*)
+--- org.springframework.boot:spring-boot-starter-logging -> 3.0.5
| \--- ch.qos.logback:logback-classic:1.4.6
| +--- ch.qos.logback:logback-core:1.4.6
| \--- org.slf4j:slf4j-api:2.0.4 -> 2.0.7
+--- org.springframework:spring-context -> 6.0.7
| +--- org.springframework:spring-aop:6.0.7
| | +--- org.springframework:spring-beans:6.0.7
| | | \--- org.springframework:spring-core:6.0.7
| | | \--- org.springframework:spring-jcl:6.0.7
| | \--- org.springframework:spring-core:6.0.7 (*)
| +--- org.springframework:spring-beans:6.0.7 (*)
| +--- org.springframework:spring-core:6.0.7 (*)
| \--- org.springframework:spring-expression:6.0.7
| \--- org.springframework:spring-core:6.0.7 (*)
+--- org.springframework:spring-beans -> 6.0.7 (*)
+--- com.fasterxml.jackson.core:jackson-databind:{strictly 2.14.2} -> 2.14.2 (*)
+--- com.fasterxml.jackson.core:jackson-annotations:{strictly 2.14.2} -> 2.14.2 (*)
+--- org.apache.tomcat.embed:tomcat-embed-core:9.0.69
| \--- org.apache.tomcat:tomcat-annotations-api:9.0.69 -> 10.1.7
+--- commons-logging:commons-logging:1.1.3 -> 1.2
We apply the following exclusions and dependencies to all modules in the multimodule build:
configurations.all {
// Short version cache so we don't accidentally use old versions
resolutionStrategy.cacheDynamicVersionsFor 10, 'minutes'
exclude group: 'org.apache.logging.log4j', module: 'log4j-to-slf4j'
exclude group: 'org.apache.logging.log4j', module: 'log4j-api'
exclude group: 'org.slf4j', module: 'jul-to-slf4j'
exclude group: 'org.slf4j', module: 'slf4j-log4j'
exclude group: 'org.slf4j', module: 'slf4j-log4j12'
}
dependencies {
implementation 'net.logstash.logback:logstash-logback-encoder:6.6'
implementation 'org.springframework.boot:spring-boot-starter-logging'
Running my integration tests gives me:
Caused by: java.lang.IllegalArgumentException: LoggerFactory is not a Logback LoggerContext but Logback is on the classpath. Either remove Logback or the competing implementation (class org.slf4j.helpers.SubstituteLoggerFactory loaded from file: .../.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/2.0.7/41eb7184ea9d556f23e18b5cb99cad1f8581fc00/slf4j-api-2.0.7.jar). If you are using WebLogic you will need to add 'org.slf4j' to prefer-application-packages in WEB-INF/weblogic.xml: org.slf4j.helpers.SubstituteLoggerFactory
Strangely, this is only effecting some of the integration tests. Others are running as expected.
I'm at a bit of a loss here. I can't exclude logback or slf4j, as both of them are required. Any help would be appreciated.
EDIT I've done some debugging this morning and what seems to be happening is that whilst the org.slf4j.LoggerFactory
is being initialised, it returns the substitute logger.
We've got to the bottom of our issue here. Here's a summary of what was going on:
LoggerFactory
, we could see that SLF4J was finding Logback as the provider, and started initialising it.logback.xml
and apparently initialising the logstash
appender is a fairly slow process.The root cause of the problem seems to be that whilst slf4j is initialising, any requests to the LoggerFactory#getProvider
method return the substitute provider, at which point the LogbackLoggingSystem#getLoggerContext
in spring boot throws an exception, as the LoggerFactory
returned is not valid.
I'm going to raise a bug against slf4j for this behaviour. It feels to me that if it's initialising, it shouldn't just return a dummy implementation.