spring-projects / spring-boot

Spring Boot helps you to create Spring-powered, production-grade applications and services with absolute minimum fuss.
https://spring.io/projects/spring-boot
Apache License 2.0
75.08k stars 40.67k forks source link

Support using Log4j2 in a GraalVM native image #37152

Open msosa opened 1 year ago

msosa commented 1 year ago

Starting a native image built with bootBuildImage and org.springframework.boot:spring-boot-starter-log4j2 throws an error on startup

Exception in thread "main" java.lang.ExceptionInInitializerError
        at org.apache.logging.slf4j.Log4jMarkerFactory.<clinit>(Log4jMarkerFactory.java:36)
        at org.apache.logging.slf4j.SLF4JServiceProvider.initialize(SLF4JServiceProvider.java:53)
        at org.slf4j.LoggerFactory.bind(LoggerFactory.java:183)
        at org.slf4j.LoggerFactory.performInitialization(LoggerFactory.java:170)
        at org.slf4j.LoggerFactory.getProvider(LoggerFactory.java:455)
        at org.slf4j.LoggerFactory.getILoggerFactory(LoggerFactory.java:441)
        at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:390)
        at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:416)
        at com.example.log4j2native.Log4j2NativeApplicationKt.<clinit>(Log4j2NativeApplication.kt: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:242)
        at org.apache.logging.log4j.spi.AbstractLogger.<init>(AbstractLogger.java:141)
        at org.apache.logging.log4j.status.StatusLogger.<init>(StatusLogger.java:141)
        at org.apache.logging.log4j.status.StatusLogger.<clinit>(StatusLogger.java:91)
        ... 9 more
Caused by: java.lang.InstantiationException: org.apache.logging.log4j.message.DefaultFlowMessageFactory
        at java.base@17.0.7/java.lang.Class.newInstance(DynamicHub.java:639)
        at org.apache.logging.log4j.spi.AbstractLogger.createDefaultFlowMessageFactory(AbstractLogger.java:240)
        ... 12 more
Caused by: java.lang.NoSuchMethodException: org.apache.logging.log4j.message.DefaultFlowMessageFactory.<init>()
        at java.base@17.0.7/java.lang.Class.checkMethod(DynamicHub.java:1040)
        at java.base@17.0.7/java.lang.Class.getConstructor0(DynamicHub.java:1206)
        at java.base@17.0.7/java.lang.Class.newInstance(DynamicHub.java:626)
        ... 13 more

I did try adding some hints to help make it through these errors

hints.reflection().registerType(TypeReference.of("org.apache.logging.log4j.message.DefaultFlowMessageFactory"), MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS)
hints.reflection().registerType(TypeReference.of("org.apache.logging.log4j.message.ParameterizedMessageFactory"), MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS)

But I couldn't fix the error after these were added.

Here is a repo to help reproduce the error(without the hints): https://github.com/msosa/log4j2-native

wilkinsona commented 1 year ago

Log4j2 isn't supported in a native image. It requires extensive changes to Log4j2 for it to work. Please see LOG4J2-2649 for further details.

luisalves00 commented 1 year ago

@wilkinsona I have log4j2 on version [3.0.5,3.1.0[ and seems to work:

+--- org.springframework.boot:spring-boot-starter-log4j2 -> 3.0.9
|    +--- org.apache.logging.log4j:log4j-slf4j2-impl:2.19.0
|    |    +--- org.slf4j:slf4j-api:2.0.0 -> 2.0.7
|    |    \--- org.apache.logging.log4j:log4j-api:2.19.0
|    +--- org.apache.logging.log4j:log4j-core:2.19.0
|    |    \--- org.apache.logging.log4j:log4j-api:2.19.0
|    \--- org.apache.logging.log4j:log4j-jul:2.19.0
|         \--- org.apache.logging.log4j:log4j-api:2.19.0

I just have that error when I bump to spring boot to 3.1+. That pull log4j2 2.20.0.

davelet commented 3 months ago

@wilkinsona I have log4j2 on version [3.0.5,3.1.0[ and seems to work:

+--- org.springframework.boot:spring-boot-starter-log4j2 -> 3.0.9
|    +--- org.apache.logging.log4j:log4j-slf4j2-impl:2.19.0
|    |    +--- org.slf4j:slf4j-api:2.0.0 -> 2.0.7
|    |    \--- org.apache.logging.log4j:log4j-api:2.19.0
|    +--- org.apache.logging.log4j:log4j-core:2.19.0
|    |    \--- org.apache.logging.log4j:log4j-api:2.19.0
|    \--- org.apache.logging.log4j:log4j-jul:2.19.0
|         \--- org.apache.logging.log4j:log4j-api:2.19.0

I just have that error when I bump to spring boot to 3.1+. That pull log4j2 2.20.0.

I use springboot3.3.2 with log4j 2.23 but failed.

XenoAmess commented 2 weeks ago

Log4j2 isn't supported in a native image. It requires extensive changes to Log4j2 for it to work. Please see LOG4J2-2649 for further details.

@wilkinsona since it be resolved now, maybe we shall take some actions too...

https://github.com/apache/logging-log4j2/issues/2831

Application run failed
java.lang.IllegalStateException: java.lang.IllegalStateException: Could not initialize Log4J2 logging from classpath:org/springframework/boot/logging/log4j2/log4j2.xml
        at org.springframework.boot.context.logging.LoggingApplicationListener.initializeSystem(LoggingApplicationListener.java:347)
        at org.springframework.boot.context.logging.LoggingApplicationListener.initialize(LoggingApplicationListener.java:298)
        at org.springframework.boot.context.logging.LoggingApplicationListener.onApplicationEnvironmentPreparedEvent(LoggingApplicationListener.java:246)
        at org.springframework.boot.context.logging.LoggingApplicationListener.onApplicationEvent(LoggingApplicationListener.java:223)
        at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:185)
        at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:178)
        at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:156)
        at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:138)
        at org.springframework.boot.context.event.EventPublishingRunListener.multicastInitialEvent(EventPublishingRunListener.java:136)
        at org.springframework.boot.context.event.EventPublishingRunListener.environmentPrepared(EventPublishingRunListener.java:81)
        at org.springframework.boot.SpringApplicationRunListeners.lambda$environmentPrepared$2(SpringApplicationRunListeners.java:64)
        at java.base@21.0.5/java.lang.Iterable.forEach(Iterable.java:75)
        at org.springframework.boot.SpringApplicationRunListeners.doWithListeners(SpringApplicationRunListeners.java:118)
        at org.springframework.boot.SpringApplicationRunListeners.doWithListeners(SpringApplicationRunListeners.java:112)
        at org.springframework.boot.SpringApplicationRunListeners.environmentPrepared(SpringApplicationRunListeners.java:63)
        at org.springframework.boot.SpringApplication.prepareEnvironment(SpringApplication.java:370)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:330)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1363)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1352)
        at com.sangfor.gop.gop_auth.GopAuthApplication.main(GopAuthApplication.java:10)
        at java.base@21.0.5/java.lang.invoke.LambdaForm$DMH/sa346b79c.invokeStaticInit(LambdaForm$DMH)
Caused by: java.lang.IllegalStateException: Could not initialize Log4J2 logging from classpath:org/springframework/boot/logging/log4j2/log4j2.xml
        at org.springframework.boot.logging.log4j2.Log4J2LoggingSystem.loadConfiguration(Log4J2LoggingSystem.java:275)
        at org.springframework.boot.logging.log4j2.Log4J2LoggingSystem.load(Log4J2LoggingSystem.java:244)
        at org.springframework.boot.logging.log4j2.Log4J2LoggingSystem.loadDefaults(Log4J2LoggingSystem.java:230)
        at org.springframework.boot.logging.AbstractLoggingSystem.initializeWithConventions(AbstractLoggingSystem.java:84)
        at org.springframework.boot.logging.AbstractLoggingSystem.initialize(AbstractLoggingSystem.java:61)
        at org.springframework.boot.logging.log4j2.Log4J2LoggingSystem.initialize(Log4J2LoggingSystem.java:223)
        at org.springframework.boot.context.logging.LoggingApplicationListener.initializeSystem(LoggingApplicationListener.java:332)
        ... 20 more
Caused by: java.io.FileNotFoundException: class path resource [org/springframework/boot/logging/log4j2/log4j2.xml] cannot be resolved to URL because it does not exist
        at org.springframework.core.io.ClassPathResource.getURL(ClassPathResource.java:230)
        at org.springframework.boot.logging.log4j2.Log4J2LoggingSystem.getConfigurationSource(Log4J2LoggingSystem.java:289)
        at org.springframework.boot.logging.log4j2.Log4J2LoggingSystem.load(Log4J2LoggingSystem.java:281)
        at org.springframework.boot.logging.log4j2.Log4J2LoggingSystem.loadConfiguration(Log4J2LoggingSystem.java:266)
        ... 26 more
wilkinsona commented 2 weeks ago

Thanks for the nudge, @XenoAmess. It looks like the necessary changes are only available in 2.25 that has not yet been released. I'll re-open this so that we can investigate supporting Log4j2 once we've upgraded to 2.25 which is likely to be Spring Boot 3.5 at this point as we're nearing the end of the development cycle for 3.4.

In the meantime, you may be able to get things to work by adding some metadata to tell Graal to include org/springframework/boot/logging/log4j2/log4j2.xml in the native image.

XenoAmess commented 2 weeks ago

In the meantime, you may be able to get things to work by adding some metadata to tell Graal to include org/springframework/boot/logging/log4j2/log4j2.xml in the native image.

yes I'm trying it this way and see if there be other troubles in it. would report you when I finish this learning.