logfellow/logstash-logback-encoder

With Spring 3.2.2 I seem to need to add a runtime dependency on Jaxb?

frankjkelly opened this issue · 3 comments

Describe the bug
I have to add

  runtimeOnly('javax.xml.bind:jaxb-api:2.3.1') {
    because("Needed for logstash logback encoder for some reason")
  }

to avoid

java.lang.NoClassDefFoundError: javax/xml/bind/annotation/XmlElement
	at com.fasterxml.jackson.module.jaxb.JaxbAnnotationIntrospector.<init>(JaxbAnnotationIntrospector.java:137)
	at com.fasterxml.jackson.module.jaxb.JaxbAnnotationIntrospector.<init>(JaxbAnnotationIntrospector.java:124)
	at com.fasterxml.jackson.module.jaxb.JaxbAnnotationModule.setupModule(JaxbAnnotationModule.java:98)
	at com.fasterxml.jackson.databind.ObjectMapper.registerModule(ObjectMapper.java:879)
	at com.fasterxml.jackson.databind.ObjectMapper.registerModules(ObjectMapper.java:1081)
	at com.fasterxml.jackson.databind.ObjectMapper.findAndRegisterModules(ObjectMapper.java:1165)
	at net.logstash.logback.composite.AbstractCompositeJsonFormatter.createJsonFactory(AbstractCompositeJsonFormatter.java:247)
	at net.logstash.logback.composite.AbstractCompositeJsonFormatter.start(AbstractCompositeJsonFormatter.java:117)
	at net.logstash.logback.encoder.CompositeJsonEncoder.start(CompositeJsonEncoder.java:129)
	at ch.qos.logback.core.model.processor.ImplicitModelHandler.postHandleComplex(ImplicitModelHandler.java:208)
	at ch.qos.logback.core.model.processor.ImplicitModelHandler.postHandle(ImplicitModelHandler.java:186)
	at ch.qos.logback.core.model.processor.DefaultProcessor.secondPhaseTraverse(DefaultProcessor.java:257)
	at ch.qos.logback.core.model.processor.DefaultProcessor.secondPhaseTraverse(DefaultProcessor.java:253)
	at ch.qos.logback.core.model.processor.DefaultProcessor.secondPhaseTraverse(DefaultProcessor.java:253)
	at ch.qos.logback.core.model.processor.DefaultProcessor.traversalLoop(DefaultProcessor.java:90)
	at ch.qos.logback.core.model.processor.DefaultProcessor.process(DefaultProcessor.java:106)
	at ch.qos.logback.core.joran.GenericXMLConfigurator.processModel(GenericXMLConfigurator.java:208)
	at org.springframework.boot.logging.logback.SpringBootJoranConfigurator.processModel(SpringBootJoranConfigurator.java:122)
	at ch.qos.logback.core.joran.GenericXMLConfigurator.doConfigure(GenericXMLConfigurator.java:170)
	at ch.qos.logback.core.joran.GenericXMLConfigurator.doConfigure(GenericXMLConfigurator.java:122)
	at ch.qos.logback.core.joran.GenericXMLConfigurator.doConfigure(GenericXMLConfigurator.java:65)
	at org.springframework.boot.logging.logback.LogbackLoggingSystem.configureByResourceUrl(LogbackLoggingSystem.java:287)
	at org.springframework.boot.logging.logback.LogbackLoggingSystem.lambda$loadConfiguration$1(LogbackLoggingSystem.java:249)
	at org.springframework.boot.logging.logback.LogbackLoggingSystem.withLoggingSuppressed(LogbackLoggingSystem.java:467)
	at org.springframework.boot.logging.logback.LogbackLoggingSystem.loadConfiguration(LogbackLoggingSystem.java:244)
	at org.springframework.boot.logging.AbstractLoggingSystem.initializeWithConventions(AbstractLoggingSystem.java:81)
	at org.springframework.boot.logging.AbstractLoggingSystem.initialize(AbstractLoggingSystem.java:61)
	at org.springframework.boot.logging.logback.LogbackLoggingSystem.initialize(LogbackLoggingSystem.java:189)

To Reproduce
Steps to reproduce the behavior:

  1. Upgraded from 7.3 to 7.4 and switched to Spring Boot 3.2.2

Expected behavior
A clear and concise description of what you expected to happen.

  • I expected not to have to adjust my gradle dependencies

Additional context
Add any other context about the problem here.

  • logstash-logback-encoder version
  • logback version
  • jackson version
  • java version
 java --version
openjdk 17.0.9 2023-10-17
OpenJDK Runtime Environment Homebrew (build 17.0.9+0)
OpenJDK 64-Bit Server VM Homebrew (build 17.0.9+0, mixed mode, sharing)

From my lockfile

ch.qos.logback:logback-classic:1.4.14=compileClasspath,intTestCompileClasspath,intTestImplementation,intTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
ch.qos.logback:logback-core:1.4.14=compileClasspath,intTestCompileClasspath,intTestImplementation,intTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson.core:jackson-annotations:2.15.3=compileClasspath,intTestCompileClasspath,intTestImplementation,intTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson.core:jackson-core:2.15.3=compileClasspath,intTestCompileClasspath,intTestImplementation,intTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson.core:jackson-databind:2.15.3=compileClasspath,intTestCompileClasspath,intTestImplementation,intTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson.dataformat:jackson-dataformat-cbor:2.15.3=compileClasspath,intTestCompileClasspath,intTestImplementation,intTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.15.3=intTestCompileClasspath,intTestImplementation,intTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.15.3=intTestCompileClasspath,intTestImplementation,intTestRuntimeClasspath
com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.15.3=compileClasspath,intTestCompileClasspath,intTestImplementation,intTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.15.3=compileClasspath,intTestCompileClasspath,intTestImplementation,intTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson.jaxrs:jackson-jaxrs-base:2.15.3=intTestImplementation,intTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider:2.15.3=intTestImplementation,intTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.fasterxml.jackson.module:jackson-module-jaxb-annotations:2.15.3=intTestImplementation,intTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.fasterxml.jackson.module:jackson-module-parameter-names:2.15.3=compileClasspath,intTestCompileClasspath,intTestImplementation,intTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson:jackson-bom:2.15.3=compileClasspath,intTestCompileClasspath,intTestImplementation,intTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
net.logstash.logback:logstash-logback-encoder:7.4=compileClasspath,intTestCompileClasspath,intTestImplementation,intTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.openapitools:jackson-databind-nullable:0.2.6=compileClasspath,intTestCompileClasspath,intTestImplementation,intTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
software.amazon.awssdk:third-party-jackson-core:2.22.7=intTestCompileClasspath,intTestImplementation,intTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath

The reason is in the Pom.xml

          <dependency>
               <groupId>com.fasterxml.jackson</groupId>
               <artifactId>jackson-bom</artifactId>
               <version>${jackson.version}</version>
               <type>pom</type>
               <scope>import</scope>
           </dependency>

This includes jackson-module-jaxb-annotations-${jackson.version}.jar, which is then automatically discovered by jackson.

Thanks @jowi-ppi - is there any way around it?

That is not exactly correct. The jackson-bom is used in logstash-logback-encoder's dependencyManagement section. Not its dependencies section. Therefore logstash-logback-encoder does not depend on, or require, jackson-module-jaxb-annotations.

This can be confirmed by viewing the runtime dependency tree for logstash-logback-encoder (including optional dependencies).

❯ mvn dependency:tree -Dscope=runtime
[INFO] Scanning for projects...
[INFO]
[INFO] -----------< net.logstash.logback:logstash-logback-encoder >------------
[INFO] Building Logstash Logback Encoder 7.4
[INFO]   from pom.xml
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- dependency:3.6.0:tree (default-cli) @ logstash-logback-encoder ---
[INFO] net.logstash.logback:logstash-logback-encoder:jar:7.4
[INFO] +- ch.qos.logback:logback-classic:jar:1.3.7:compile
[INFO] |  \- org.slf4j:slf4j-api:jar:2.0.4:compile
[INFO] +- ch.qos.logback:logback-access:jar:1.3.7:compile
[INFO] +- com.fasterxml.jackson.core:jackson-databind:jar:2.15.2:compile
[INFO] |  +- com.fasterxml.jackson.core:jackson-annotations:jar:2.15.2:compile
[INFO] |  \- com.fasterxml.jackson.core:jackson-core:jar:2.15.2:compile
[INFO] +- com.fasterxml.jackson.dataformat:jackson-dataformat-cbor:jar:2.15.2:compile
[INFO] +- com.fasterxml.jackson.dataformat:jackson-dataformat-smile:jar:2.15.2:compile
[INFO] +- com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:jar:2.15.2:compile
[INFO] |  \- org.yaml:snakeyaml:jar:2.0:compile
[INFO] +- com.fasterxml.uuid:java-uuid-generator:jar:4.2.0:compile
[INFO] \- com.lmax:disruptor:jar:3.4.4:compile

By default, logstash-logback-encoder will tell jackson to dynamically discover all available jackson modules on the classpath by calling objectMapper.findAndRegisterModules(), as seen in your stacktrace. In your case, the jackson-module-jaxb-annotations is on your runtime classpath, and jackson is trying to register it, and failing due to one of its required dependencies being missing. Something else in your application (not logstash-logback-encoder) is putting jackson-module-jaxb-annotations on your classpath.

There are a couple solves for this:

  • If you do not need the jackson-module-jaxb-annotations module, then you can find whatever is putting that on your runtime classpath and exclude it.
  • If you do need jackson-module-jaxb-annotations, then you'll need to ensure its dependencies are on your runtime classpath.

Alternatively, you can tell logstash-logback-encoder to not configure jackson to find and register modules dynamically (as mentioned here), but then you'll need to register any modules that you do need explicitly.