In short, a JVM with Spring Boot 2.5.x project doesn’t shutdown on application start failure if log4j2’s Async
appender is used.
A demo project: sainaen/spring-boot-demo-log4j2 (see commits there for two preconditions: using log4j2 and breaking the app so it won’t start properly).
Example execution
❱ ./gradlew bootJar
BUILD SUCCESSFUL in 1s
4 actionable tasks: 4 executed
❱ java -jar build/libs/demo-0.0.1-SNAPSHOT.jar
2021-07-14T12:12:14,237 [Thread ID=1] ERROR o.s.b.d.LoggingFailureAnalysisReporter -
***************************
APPLICATION FAILED TO START
***************************
Description:
Failed to bind properties under 'spring.main.banner-mode' to org.springframework.boot.Banner$Mode:
Property: spring.main.banner-mode
Value: break
Origin: class path resource [application.yml] from demo-0.0.1-SNAPSHOT.jar - 3:21
Reason: failed to convert java.lang.String to org.springframework.boot.Banner$Mode (caused by java.lang.IllegalArgumentException: No enum constant org.springframework.boot.Banner.Mode.break)
Action:
Update your application's configuration. The following values are valid:
CONSOLE
LOG
OFF
At this point JVM keeps running, waiting for `AsyncAppenderEventDispatcher` thread indefinitely.
I’ve first found this behavior with the application that failed to start due to unreachable MySQL host configured in its DataSource, so it’s not specific to the broken config values.
The same setup works as expected in 2.4.8.
Comment From: wilkinsona
Thanks for the sample. I've reproduced the problem but I'm not entirely sure what's going on just yet. An equivalent app written in Java does not hang. I've tried with ./gradlew bootRun
, java -jar
, and running the main method in Eclipse. In all three cases, the AsyncAppenderEventDispatcher
thread isn't created so it does not prevent the JVM from exiting. I've yet to identify what it is about using Kotlin that causes Log4j2 to behave differently.
Comment From: sainaen
I’m not sure this is related to Kotlin. I’ve just switched to java in a separate branch and it behaves in the same way.
Comment From: wilkinsona
Sorry, I managed to typo the name of the config file when I copied it into the Java-based project. With that file name corrected, I see the hang with Java too.
This appears to be a Log4j2 bug. Using your log4j2.xml
configuration, this really simple application will also prevent the JVM for exiting:
package com.example.demo;
import org.slf4j.LoggerFactory;
public class Gh27325Application {
public static void main(String[] args) {
LoggerFactory.getLogger(Gh27325Application.class).info("Hello");
}
}
The problem appears to be that Log4j2 2.14's AsyncAppenderEventDispatcher
is not a daemon thread where as it's equivalent in Log4j2 2.13, AsyncThread
, is a daemon thread. This has been reported in LOG4J2-3102 where it says that it worked in 2.14.0 and fails with 2.14.1. The fix version on the issue is 2.15.0 which has not yet been released. As it appears to be a regression in 2.14.1, it may be worth asking the Log4j2 team to consider a 2.14.2 that fixes the problem.
Comment From: sainaen
Ohhh, I’ve checked an older Spring Boot version, but didn’t think to check log4j2. :facepalm:
Thank you so much for your help! Sorry for the noise.