-
Notifications
You must be signed in to change notification settings - Fork 38.7k
Description
Current Behavior
The default logging configuration for Spring Boot is to send logs to the console. Info logs are printed at application context startup and via shutdown hook when the application context is shutting down. Due to the way that test runners in gradle, intellj, and other tools capture console for tests we end up with stray output.
> Task :components:database:test
2020-07-12 09:54:24.952 INFO 46723 --- [extShutdownHook] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2020-07-12 09:54:24.954 INFO 46723 --- [extShutdownHook] o.s.s.concurrent.ThreadPoolTaskExecutor : Shutting down ExecutorService 'applicationTaskExecutor'
2020-07-12 09:54:24.955 INFO 46723 --- [extShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
2020-07-12 09:54:25.402 INFO 46723 --- [extShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.
You can see a detailed discussion on this at Gradle Issue 12423 and a project to reproduce the issue. I am working on a multi module project and I end up with a lot of shutdown hook noise in my console every time I run ./gradlew build
. Ideally there is no shutdown hook output displayed to the screen.
Why this problem occurs
Test runners want to associate the execution of a test with the console output of that test. Lets say you have a method such as
@Test
void testSomething(){
// given
// when
System.out.println("Hello");
// then
}
The test runners want to generate a report that can display the console output that was generated when the testSomething
method was executing. When there are multiple threads present things become unpredictable if there is a test executing whatever threads on the JVM print to the console gets associated with that test. Consider the screenshot below.
The test case being executed does not not actually print any output , the output captured is from the spring initialization code running on what appears to be a test containers, this happens to be an integration test against a PostgreSQL database launched via test containers project.
According to a post from Luke Daly from the gradle team when there are multiple threads.
Gradle captures the output during tests, and associates it with the test case. Within a shutdown hook, there’s no test case to associate it with and nowhere else to send it other than to “the console”. This is the safest default as that output might be useful information, and there’s no other way to capture it.
So the observed behavior is not a bug in Gradle or Spring, but it is caused by the interaction between the default behaviors of spring and Gradle. BTW I see the same issue when running tests in intelliJ the output that intellij associates with each of the test case methods is somewhat random due to thread timing issues.
Workaround 1
One option is to turn off console logging in SpringBoot by setting
logging:
level:
root: error
this works no more shutdown hook info output. When a test class marked up with @SpringBootTest
runs it is quite useful to be able to inspect the console and see what version of boot is running, how long things took any useful info messages. Therefore this workaround is not desirable. The current of state of initalization and shutdown output being associated with random @Test
methods is also undesirable.
Workaround 2
I created a logback-spring.xml file and used it to direct logs from test executions to a log file, using profiles and the log file below.
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true">
<springProfile name="test">
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<property name="LOG_FILE" value="build/logs/spring-tests.log"/>
<include resource="org/springframework/boot/logging/logback/file-appender.xml"/>
<root level="INFO">
<appender-ref ref="FILE"/>
</root>
</springProfile>
<springProfile name="!test">
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<include resource="org/springframework/boot/logging/logback/console-appender.xml"/>
<root level="INFO">
<appender-ref ref="CONSOLE"/>
</root>
</springProfile>
</configuration>
The disadvantage of this workaround is that intelliJ Junit runner no longer displays any of the startup up/shutdown output I have to go to build/logs
to get it.
Desired Behavior
The workarounds are not ideal would be nice if there was a way to auto configure this type of setup or at the very least explain this issue in the docs.