I have a CLI tool using Spring Boot 3.0.0.RC2 with Spring Shell. When I compile it natively and run it, it suddenly now prints this at the start:
ERROR StatusLogger Unable to load services for service class org.apache.logging.log4j.spi.Provider
java.lang.NoSuchMethodException: no such method: java.util.ServiceLoader.load(Class,ClassLoader)ServiceLoader/invokeStatic
at java.base@19.0.1/java.lang.invoke.MemberName.makeAccessException(MemberName.java:976)
at java.base@19.0.1/java.lang.invoke.MemberName$Factory.resolveOrFail(MemberName.java:1117)
at java.base@19.0.1/java.lang.invoke.MethodHandles$Lookup.resolveOrFail(MethodHandles.java:3664)
at java.base@19.0.1/java.lang.invoke.MethodHandles$Lookup.findStatic(MethodHandles.java:2598)
at org.apache.logging.log4j.util.ServiceLoaderUtil.loadClassloaderServices(ServiceLoaderUtil.java:91)
at org.apache.logging.log4j.util.ServiceLoaderUtil.loadServices(ServiceLoaderUtil.java:74)
at org.apache.logging.log4j.util.ServiceLoaderUtil.loadServices(ServiceLoaderUtil.java:68)
at org.apache.logging.log4j.util.ProviderUtil.<init>(ProviderUtil.java:67)
at org.apache.logging.log4j.util.ProviderUtil.lazyInit(ProviderUtil.java:145)
at org.apache.logging.log4j.util.ProviderUtil.hasProviders(ProviderUtil.java:129)
at org.apache.logging.log4j.LogManager.<clinit>(LogManager.java:90)
at org.jboss.logging.LoggerProviders.tryLog4j2(LoggerProviders.java:119)
at org.jboss.logging.LoggerProviders.findProvider(LoggerProviders.java:84)
at org.jboss.logging.LoggerProviders.find(LoggerProviders.java:32)
at org.jboss.logging.LoggerProviders.<clinit>(LoggerProviders.java:29)
at org.jboss.logging.Logger.getLogger(Logger.java:2465)
at org.jboss.logging.Logger.doGetMessageLogger(Logger.java:2573)
at org.jboss.logging.Logger.getMessageLogger(Logger.java:2530)
at org.jboss.logging.Logger.getMessageLogger(Logger.java:2516)
at org.hibernate.validator.internal.util.logging.LoggerFactory.make(LoggerFactory.java:22)
at org.hibernate.validator.internal.util.Version.<clinit>(Version.java:20)
at org.hibernate.validator.internal.engine.AbstractConfigurationImpl.<clinit>(AbstractConfigurationImpl.java:85)
at java.base@19.0.1/java.lang.Class.ensureInitialized(DynamicHub.java:528)
at org.hibernate.validator.HibernateValidator.createGenericConfiguration(HibernateValidator.java:33)
at jakarta.validation.Validation$GenericBootstrapImpl.configure(Validation.java:296)
at org.springframework.boot.validation.MessageInterpolatorFactory.getMessageInterpolator(MessageInterpolatorFactory.java:79)
at org.springframework.boot.validation.MessageInterpolatorFactory.getObject(MessageInterpolatorFactory.java:70)
at org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration.defaultValidator(ValidationAutoConfiguration.java:64)
at org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration__BeanDefinitions.lambda$getDefaultValidatorInstanceSupplier$0(ValidationAutoConfiguration__BeanDefinitions.java:33)
at org.springframework.util.function.ThrowingBiFunction.apply(ThrowingBiFunction.java:68)
at org.springframework.util.function.ThrowingBiFunction.apply(ThrowingBiFunction.java:54)
at org.springframework.beans.factory.aot.BeanInstanceSupplier.lambda$get$2(BeanInstanceSupplier.java:208)
at org.springframework.util.function.ThrowingSupplier.get(ThrowingSupplier.java:59)
at org.springframework.util.function.ThrowingSupplier.get(ThrowingSupplier.java:47)
at org.springframework.beans.factory.aot.BeanInstanceSupplier.invokeBeanSupplier(BeanInstanceSupplier.java:220)
at org.springframework.beans.factory.aot.BeanInstanceSupplier.get(BeanInstanceSupplier.java:208)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.obtainInstanceFromSupplier(AbstractAutowireCapableBeanFactory.java:1225)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.obtainFromSupplier(AbstractAutowireCapableBeanFactory.java:1210)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1157)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:561)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:521)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:961)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:916)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:584)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:730)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:432)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:308)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1302)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1291)
at io.github.wimdeblauwe.ttcli.TamingThymeleafCliApplication.main(TamingThymeleafCliApplication.java:10)
Caused by: java.lang.NoSuchMethodError: java.util.ServiceLoader.load(java.lang.Class, java.lang.ClassLoader)
at org.graalvm.nativeimage.builder/com.oracle.svm.core.methodhandles.Util_java_lang_invoke_MethodHandleNatives.resolve(Target_java_lang_invoke_MethodHandleNatives.java:345)
at java.base@19.0.1/java.lang.invoke.MethodHandleNatives.resolve(MethodHandleNatives.java:223)
at java.base@19.0.1/java.lang.invoke.MemberName$Factory.resolve(MemberName.java:1085)
at java.base@19.0.1/java.lang.invoke.MemberName$Factory.resolveOrFail(MemberName.java:1114)
... 52 more
ERROR StatusLogger Log4j2 could not find a logging implementation. Please add log4j-core to the classpath. Using SimpleLogger to log to the console...
To be clear: this is not printed when running on the JVM.
I am not using Log4j2, it is not in my dependency tree:
[INFO] --- maven-dependency-plugin:3.3.0:tree (default-cli) @ ttcli ---
[INFO] io.github.wimdeblauwe:ttcli:jar:0.0.3-SNAPSHOT
[INFO] +- org.springframework.boot:spring-boot-starter-web:jar:3.0.0-RC2:compile
[INFO] | +- org.springframework.boot:spring-boot-starter:jar:3.0.0-RC2:compile
[INFO] | | +- org.springframework.boot:spring-boot:jar:3.0.0-RC2:compile
[INFO] | | +- org.springframework.boot:spring-boot-autoconfigure:jar:3.0.0-RC2:compile
[INFO] | | +- org.springframework.boot:spring-boot-starter-logging:jar:3.0.0-RC2:compile
[INFO] | | | +- ch.qos.logback:logback-classic:jar:1.4.4:compile
[INFO] | | | | \- ch.qos.logback:logback-core:jar:1.4.4:compile
[INFO] | | | +- org.apache.logging.log4j:log4j-to-slf4j:jar:2.19.0:compile
[INFO] | | | | \- org.apache.logging.log4j:log4j-api:jar:2.19.0:compile
[INFO] | | | \- org.slf4j:jul-to-slf4j:jar:2.0.3:compile
[INFO] | | +- jakarta.annotation:jakarta.annotation-api:jar:2.1.1:compile
[INFO] | | \- org.yaml:snakeyaml:jar:1.33:compile
[INFO] | +- org.springframework.boot:spring-boot-starter-json:jar:3.0.0-RC2:compile
[INFO] | | +- com.fasterxml.jackson.datatype:jackson-datatype-jdk8:jar:2.14.0-rc3:compile
[INFO] | | +- com.fasterxml.jackson.datatype:jackson-datatype-jsr310:jar:2.14.0-rc3:compile
[INFO] | | \- com.fasterxml.jackson.module:jackson-module-parameter-names:jar:2.14.0-rc3:compile
[INFO] | +- org.springframework.boot:spring-boot-starter-tomcat:jar:3.0.0-RC2:compile
[INFO] | | +- org.apache.tomcat.embed:tomcat-embed-core:jar:10.1.1:compile
[INFO] | | +- org.apache.tomcat.embed:tomcat-embed-el:jar:10.1.1:compile
[INFO] | | \- org.apache.tomcat.embed:tomcat-embed-websocket:jar:10.1.1:compile
[INFO] | +- org.springframework:spring-web:jar:6.0.0-RC4:compile
[INFO] | | +- org.springframework:spring-beans:jar:6.0.0-RC4:compile
[INFO] | | \- io.micrometer:micrometer-observation:jar:1.10.0:compile
[INFO] | | \- io.micrometer:micrometer-commons:jar:1.10.0:compile
[INFO] | \- org.springframework:spring-webmvc:jar:6.0.0-RC4:compile
[INFO] | +- org.springframework:spring-aop:jar:6.0.0-RC4:compile
[INFO] | +- org.springframework:spring-context:jar:6.0.0-RC4:compile
[INFO] | \- org.springframework:spring-expression:jar:6.0.0-RC4:compile
[INFO] +- org.springframework.boot:spring-boot-starter-webflux:jar:3.0.0-RC2:compile
[INFO] | +- org.springframework.boot:spring-boot-starter-reactor-netty:jar:3.0.0-RC2:compile
[INFO] | | \- io.projectreactor.netty:reactor-netty-http:jar:1.1.0:compile
[INFO] | | +- io.netty:netty-codec-http:jar:4.1.84.Final:compile
[INFO] | | | +- io.netty:netty-common:jar:4.1.84.Final:compile
[INFO] | | | +- io.netty:netty-buffer:jar:4.1.84.Final:compile
[INFO] | | | +- io.netty:netty-transport:jar:4.1.84.Final:compile
[INFO] | | | +- io.netty:netty-codec:jar:4.1.84.Final:compile
[INFO] | | | \- io.netty:netty-handler:jar:4.1.84.Final:compile
[INFO] | | +- io.netty:netty-codec-http2:jar:4.1.84.Final:compile
[INFO] | | +- io.netty:netty-resolver-dns:jar:4.1.84.Final:compile
[INFO] | | | +- io.netty:netty-resolver:jar:4.1.84.Final:compile
[INFO] | | | \- io.netty:netty-codec-dns:jar:4.1.84.Final:compile
[INFO] | | +- io.netty:netty-resolver-dns-native-macos:jar:osx-x86_64:4.1.84.Final:compile
[INFO] | | | \- io.netty:netty-resolver-dns-classes-macos:jar:4.1.84.Final:compile
[INFO] | | +- io.netty:netty-transport-native-epoll:jar:linux-x86_64:4.1.84.Final:compile
[INFO] | | | +- io.netty:netty-transport-native-unix-common:jar:4.1.84.Final:compile
[INFO] | | | \- io.netty:netty-transport-classes-epoll:jar:4.1.84.Final:compile
[INFO] | | \- io.projectreactor.netty:reactor-netty-core:jar:1.1.0:compile
[INFO] | | \- io.netty:netty-handler-proxy:jar:4.1.84.Final:compile
[INFO] | | \- io.netty:netty-codec-socks:jar:4.1.84.Final:compile
[INFO] | \- org.springframework:spring-webflux:jar:6.0.0-RC4:compile
[INFO] | \- io.projectreactor:reactor-core:jar:3.5.0:compile
[INFO] | \- org.reactivestreams:reactive-streams:jar:1.0.4:compile
[INFO] +- org.springframework.shell:spring-shell-starter:jar:3.0.0-M2:compile
[INFO] | +- org.springframework.shell:spring-shell-autoconfigure:jar:3.0.0-M2:compile
[INFO] | +- org.springframework.shell:spring-shell-core:jar:3.0.0-M2:compile
[INFO] | | +- org.springframework.boot:spring-boot-starter-validation:jar:3.0.0-RC2:compile
[INFO] | | | \- org.hibernate.validator:hibernate-validator:jar:8.0.0.Final:compile
[INFO] | | | +- jakarta.validation:jakarta.validation-api:jar:3.0.2:compile
[INFO] | | | +- org.jboss.logging:jboss-logging:jar:3.5.0.Final:compile
[INFO] | | | \- com.fasterxml:classmate:jar:1.5.1:compile
[INFO] | | +- org.springframework:spring-messaging:jar:6.0.0-RC4:compile
[INFO] | | +- org.jline:jline:jar:3.21.0:compile
[INFO] | | +- org.antlr:ST4:jar:4.3.1:compile
[INFO] | | | \- org.antlr:antlr-runtime:jar:3.5.2:compile
[INFO] | | \- commons-io:commons-io:jar:2.11.0:compile
[INFO] | +- org.springframework.shell:spring-shell-standard:jar:3.0.0-M2:compile
[INFO] | +- org.springframework.shell:spring-shell-standard-commands:jar:3.0.0-M2:compile
[INFO] | \- org.springframework.shell:spring-shell-table:jar:3.0.0-M2:compile
[INFO] +- org.jsoup:jsoup:jar:1.15.3:compile
[INFO] +- us.codecraft:xsoup:jar:0.3.6:compile
[INFO] | \- org.assertj:assertj-core:jar:3.23.1:compile
[INFO] | \- net.bytebuddy:byte-buddy:jar:1.12.18:compile
[INFO] +- com.fasterxml.jackson.core:jackson-databind:jar:2.14.0-rc3:compile
[INFO] | +- com.fasterxml.jackson.core:jackson-annotations:jar:2.14.0-rc3:compile
[INFO] | \- com.fasterxml.jackson.core:jackson-core:jar:2.14.0-rc3:compile
[INFO] \- org.springframework.boot:spring-boot-starter-test:jar:3.0.0-RC2:test
[INFO] +- org.springframework.boot:spring-boot-test:jar:3.0.0-RC2:test
[INFO] +- org.springframework.boot:spring-boot-test-autoconfigure:jar:3.0.0-RC2:test
[INFO] +- com.jayway.jsonpath:json-path:jar:2.7.0:test
[INFO] | +- net.minidev:json-smart:jar:2.4.8:test
[INFO] | | \- net.minidev:accessors-smart:jar:2.4.8:test
[INFO] | | \- org.ow2.asm:asm:jar:9.1:test
[INFO] | \- org.slf4j:slf4j-api:jar:2.0.3:compile
[INFO] +- jakarta.xml.bind:jakarta.xml.bind-api:jar:4.0.0:test
[INFO] | \- jakarta.activation:jakarta.activation-api:jar:2.1.0:test
[INFO] +- org.hamcrest:hamcrest:jar:2.2:test
[INFO] +- org.junit.jupiter:junit-jupiter:jar:5.9.1:test
[INFO] | +- org.junit.jupiter:junit-jupiter-api:jar:5.9.1:test
[INFO] | | +- org.opentest4j:opentest4j:jar:1.2.0:test
[INFO] | | +- org.junit.platform:junit-platform-commons:jar:1.9.1:test
[INFO] | | \- org.apiguardian:apiguardian-api:jar:1.1.2:test
[INFO] | +- org.junit.jupiter:junit-jupiter-params:jar:5.9.1:test
[INFO] | \- org.junit.jupiter:junit-jupiter-engine:jar:5.9.1:test
[INFO] | \- org.junit.platform:junit-platform-engine:jar:1.9.1:test
[INFO] +- org.mockito:mockito-core:jar:4.8.1:test
[INFO] | +- net.bytebuddy:byte-buddy-agent:jar:1.12.18:test
[INFO] | \- org.objenesis:objenesis:jar:3.2:test
[INFO] +- org.mockito:mockito-junit-jupiter:jar:4.8.1:test
[INFO] +- org.skyscreamer:jsonassert:jar:1.5.1:test
[INFO] | \- com.vaadin.external.google:android-json:jar:0.0.20131108.vaadin1:test
[INFO] +- org.springframework:spring-core:jar:6.0.0-RC4:compile
[INFO] | \- org.springframework:spring-jcl:jar:6.0.0-RC4:compile
[INFO] +- org.springframework:spring-test:jar:6.0.0-RC4:test
[INFO] \- org.xmlunit:xmlunit-core:jar:2.9.0:test
I compiled on macOS using GraalVM:
sdk use java 22.3.r19-grl
mvn -Pnative native:compile
Let me know if you need more info. The project can be viewed at https://github.com/wimdeblauwe/ttcli
Comment From: wimdeblauwe
It seems I can work around the issue by adding this exclusion in my pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-to-slf4j</artifactId>
</exclusion>
</exclusions>
</dependency>
Comment From: dr-eme
I have a similar issue in Gradle, but the error is shown only when triggering the run from ./gradlew nativeRun, and then stopping the app.
My app uses SLF4J API for logging, and if I simply launch the application from build/nativeCompile/..., no error is shown.
So maybe the issue is with the way the plugins register/trigger the run commands and/or the logger they use.
The proposed workaround (excluding log4j-to-slf4j) seems to solve the issue.
Comment From: wilkinsona
Thanks for reporting this, @wimdeblauwe. It's an unfortunate combination of the way that JBoss Logging behaves and Log4j2, even just its API jar, not playing nicely with GraalVM. We include org.apache.logging.log4j:log4j-to-slf4j in spring-boot-starter-logging so that logging that's using the Log4j2 API can be routed in to SLF4J. If neither your application nor any of its dependencies uses the Log4j2 API for logging, excluding org.apache.logging.log4j:log4j-to-slf4j is a good way to address the problem. Alternatively, setting the org.jboss.logging.provider system property to jdk will fix the problem. Setting it to slf4j should also fix it (and would be a better value) but, for reasons I don't yet understand, it does not help. I'm investigating.
Comment From: wilkinsona
the error is shown only when triggering the run from ./gradlew nativeRun. If I simply launch the application from build/nativeCompile/..., no error is shown
We've seen somewhat similar behavior. In theory, any application that uses JBoss logging should be affected. However, that is not the case. It appears that something (initialization order?) causes JBoss Logging to behave differently so the problem doesn't always occur.
Comment From: wilkinsona
Setting it to slf4j should also fix it (and would be a better value) but, for reasons I don't yet understand, it does not help. I'm investigating.
When configured to try using SLF4J, JBoss Logging relies upon reflection to analyse the methods of org.slf4j.spi.LocationAwareLogger. This fails in a native image:
java.lang.NoSuchMethodError: Cannot find LocationAwareLogger.log() method
at org.jboss.logging.Slf4jLocationAwareLogger.<clinit>(Slf4jLocationAwareLogger.java:47)
at org.jboss.logging.Slf4jLoggerProvider.getLogger(Slf4jLoggerProvider.java:38)
at org.jboss.logging.LoggerProviders.logProvider(LoggerProviders.java:151)
at org.jboss.logging.LoggerProviders.trySlf4j(LoggerProviders.java:113)
at org.jboss.logging.LoggerProviders.findProvider(LoggerProviders.java:53)
at org.jboss.logging.LoggerProviders.find(LoggerProviders.java:32)
at org.jboss.logging.LoggerProviders.<clinit>(LoggerProviders.java:29)
at org.jboss.logging.Logger.getLogger(Logger.java:2465)
at org.jboss.logging.Logger.doGetMessageLogger(Logger.java:2573)
at org.jboss.logging.Logger.getMessageLogger(Logger.java:2530)
at org.jboss.logging.Logger.getMessageLogger(Logger.java:2516)
at org.hibernate.validator.internal.util.logging.LoggerFactory.make(LoggerFactory.java:22)
at org.hibernate.validator.internal.util.Version.<clinit>(Version.java:20)
at org.hibernate.validator.internal.engine.AbstractConfigurationImpl.<clinit>(AbstractConfigurationImpl.java:85)
at java.base@17.0.5/java.lang.Class.ensureInitialized(DynamicHub.java:528)
at org.hibernate.validator.HibernateValidator.createGenericConfiguration(HibernateValidator.java:33)
at jakarta.validation.Validation$GenericBootstrapImpl.configure(Validation.java:296)
at org.springframework.boot.validation.MessageInterpolatorFactory.getMessageInterpolator(MessageInterpolatorFactory.java:79)
at org.springframework.boot.validation.MessageInterpolatorFactory.getObject(MessageInterpolatorFactory.java:70)
at org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration.defaultValidator(ValidationAutoConfiguration.java:64)
at org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration__BeanDefinitions.lambda$getDefaultValidatorInstanceSupplier$0(ValidationAutoConfiguration__BeanDefinitions.java:33)
at org.springframework.util.function.ThrowingBiFunction.apply(ThrowingBiFunction.java:68)
at org.springframework.util.function.ThrowingBiFunction.apply(ThrowingBiFunction.java:54)
at org.springframework.beans.factory.aot.BeanInstanceSupplier.lambda$get$2(BeanInstanceSupplier.java:208)
at org.springframework.util.function.ThrowingSupplier.get(ThrowingSupplier.java:59)
at org.springframework.util.function.ThrowingSupplier.get(ThrowingSupplier.java:47)
at org.springframework.beans.factory.aot.BeanInstanceSupplier.invokeBeanSupplier(BeanInstanceSupplier.java:220)
at org.springframework.beans.factory.aot.BeanInstanceSupplier.get(BeanInstanceSupplier.java:208)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.obtainInstanceFromSupplier(AbstractAutowireCapableBeanFactory.java:1225)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.obtainFromSupplier(AbstractAutowireCapableBeanFactory.java:1210)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1157)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:561)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:521)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:961)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:916)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:584)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:730)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:432)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:308)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1302)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1291)
at io.github.wimdeblauwe.ttcli.TamingThymeleafCliApplication.main(TamingThymeleafCliApplication.java:10)
Comment From: wilkinsona
When log4j-to-slf4j is on the classpath, JBoss Logging uses org.jboss.logging.Log4j2LoggerProvider on the JVM. This means that the log routing is JBoss Logging -> Log4j2 API -> SLF4J -> Logback. In a native image, Log4j2 fails and org.jboss.logging.JDKLoggerProvider is used instead. This means that the log routing is JBoss Logging -> JDK -> SLF4J -> Logback. We should avoid this difference in routing if we can. It's also less efficient than it could be, both on the JVM and in a native image. It would be preferable to omit the second step and use JBoss Logging -> SLF4J -> Logback instead. For this to work in a native image, reachability metadata for JBoss Logging introspecting org.slf4j.spi.LocationAwareLogger is required.
Comment From: wilkinsona
In our Hibernate Validator smoke test, we don't see the unwanted error-level logging. This is because the attempt to use Log4j2 fails differently:
java.lang.ExceptionInInitializerError
at org.apache.logging.log4j.LogManager.<clinit>(LogManager.java:61)
at org.jboss.logging.LoggerProviders.tryLog4j2(LoggerProviders.java:126)
at org.jboss.logging.LoggerProviders.findProvider(LoggerProviders.java:88)
at org.jboss.logging.LoggerProviders.find(LoggerProviders.java:32)
at org.jboss.logging.LoggerProviders.<clinit>(LoggerProviders.java:29)
at org.jboss.logging.Logger.getLogger(Logger.java:2465)
at org.jboss.logging.Logger.doGetMessageLogger(Logger.java:2573)
at org.jboss.logging.Logger.getMessageLogger(Logger.java:2530)
at org.jboss.logging.Logger.getMessageLogger(Logger.java:2516)
at org.hibernate.validator.internal.util.logging.LoggerFactory.make(LoggerFactory.java:22)
at org.hibernate.validator.internal.util.Version.<clinit>(Version.java:20)
at org.hibernate.validator.internal.engine.AbstractConfigurationImpl.<clinit>(AbstractConfigurationImpl.java:85)
at java.base@17.0.5/java.lang.Class.ensureInitialized(DynamicHub.java:528)
at org.hibernate.validator.HibernateValidator.createGenericConfiguration(HibernateValidator.java:33)
at jakarta.validation.Validation$GenericBootstrapImpl.configure(Validation.java:296)
at org.springframework.boot.validation.MessageInterpolatorFactory.getMessageInterpolator(MessageInterpolatorFactory.java:79)
at org.springframework.boot.validation.MessageInterpolatorFactory.getObject(MessageInterpolatorFactory.java:70)
at org.springframework.boot.context.properties.ConfigurationPropertiesJsr303Validator$Delegate.<init>(ConfigurationPropertiesJsr303Validator.java:68)
at org.springframework.boot.context.properties.ConfigurationPropertiesJsr303Validator.<init>(ConfigurationPropertiesJsr303Validator.java:41)
at org.springframework.boot.context.properties.ConfigurationPropertiesBinder.getJsr303Validator(ConfigurationPropertiesBinder.java:154)
at org.springframework.boot.context.properties.ConfigurationPropertiesBinder.getValidators(ConfigurationPropertiesBinder.java:144)
at org.springframework.boot.context.properties.ConfigurationPropertiesBinder.getBindHandler(ConfigurationPropertiesBinder.java:112)
at org.springframework.boot.context.properties.ConfigurationPropertiesBinder.bind(ConfigurationPropertiesBinder.java:93)
at org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor.bind(ConfigurationPropertiesBindingPostProcessor.java:89)
at org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor.postProcessBeforeInitialization(ConfigurationPropertiesBindingPostProcessor.java:78)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:420)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1745)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:599)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:521)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200)
at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1405)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1325)
at org.springframework.beans.factory.aot.BeanInstanceSupplier.resolveArgument(BeanInstanceSupplier.java:334)
at org.springframework.beans.factory.aot.BeanInstanceSupplier.resolveArguments(BeanInstanceSupplier.java:271)
at org.springframework.beans.factory.aot.BeanInstanceSupplier.get(BeanInstanceSupplier.java:206)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.obtainInstanceFromSupplier(AbstractAutowireCapableBeanFactory.java:1225)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.obtainFromSupplier(AbstractAutowireCapableBeanFactory.java:1210)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1157)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:561)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:521)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:961)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:916)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:584)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:730)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:432)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:308)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1302)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1291)
at com.example.validation.ValidationApplication.main(ValidationApplication.java:10)
Caused by: java.lang.IllegalStateException: java.lang.InstantiationException: org.apache.logging.log4j.message.DefaultFlowMessageFactory
at org.apache.logging.log4j.spi.AbstractLogger.createDefaultFlowMessageFactory(AbstractLogger.java:246)
at org.apache.logging.log4j.spi.AbstractLogger.<init>(AbstractLogger.java:144)
at org.apache.logging.log4j.status.StatusLogger.<init>(StatusLogger.java:133)
at org.apache.logging.log4j.status.StatusLogger.<clinit>(StatusLogger.java:85)
... 58 more
Caused by: java.lang.InstantiationException: org.apache.logging.log4j.message.DefaultFlowMessageFactory
at java.base@17.0.5/java.lang.Class.newInstance(DynamicHub.java:639)
at org.apache.logging.log4j.spi.AbstractLogger.createDefaultFlowMessageFactory(AbstractLogger.java:244)
... 61 more
Caused by: java.lang.NoSuchMethodException: org.apache.logging.log4j.message.DefaultFlowMessageFactory.<init>()
at java.base@17.0.5/java.lang.Class.getConstructor0(DynamicHub.java:3585)
at java.base@17.0.5/java.lang.Class.newInstance(DynamicHub.java:626)
... 62 more
It's failed earlier and, crucially, before the point at which the Log4j2 API jar performs the unwanted logging. I don't yet understand why, apparently, it would sometimes be possible to call org.apache.logging.log4j.message.DefaultFlowMessageFactory.class.newInstance() in a native image and sometimes it would not.
Comment From: wilkinsona
org.apache.logging.log4j.message.DefaultFlowMessageFactory is accessible reflectively when Netty's on the classpath. This is due to its reachability metadata. Given that Log4j2 doesn't work in a native image and that Netty prefers SLF4J over Log4j2, I wonder if this metadata is doing more harm than good.
Comment From: wilkinsona
I've opened https://github.com/oracle/graalvm-reachability-metadata/pull/108 to fix the reachability metadata for Netty. With this change in place, Log4j2 no longer performs the error-level logging of the exception. Until this change is available, please continue to exclude the org.apache.logging.log4j:log4j-to-slf4j dependency or set the org.jboss.logging.provider system property to jdk.
I've also opened https://github.com/spring-projects/spring-boot/issues/33155 to address the different providers on the JVM and in a native image that was discovered while investigating this issue.