Skip to content

Commit 45ad155

Browse files
committed
Perform LoggingSystem cleanup after web server shutdown
Update `LoggingApplicationListener` so that logging system cleanup is performed by a `SmartLifecycle` phased after web server shutdown. Prior to this commit, cleanup occurred on the `ContextClosedEvent` which was published before Lifecycle beans were stopped. This meant that any exceptions output during web server shutdown were not logged. Fixes gh-9457
1 parent d69fcf8 commit 45ad155

File tree

2 files changed

+160
-54
lines changed

2 files changed

+160
-54
lines changed

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/logging/LoggingApplicationListener.java

Lines changed: 52 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@
4545
import org.springframework.context.ApplicationContext;
4646
import org.springframework.context.ApplicationEvent;
4747
import org.springframework.context.ApplicationListener;
48+
import org.springframework.context.ConfigurableApplicationContext;
49+
import org.springframework.context.SmartLifecycle;
4850
import org.springframework.context.event.ContextClosedEvent;
4951
import org.springframework.context.event.GenericApplicationListener;
5052
import org.springframework.core.Ordered;
@@ -136,6 +138,11 @@ public class LoggingApplicationListener implements GenericApplicationListener {
136138
*/
137139
public static final String LOGGER_GROUPS_BEAN_NAME = "springBootLoggerGroups";
138140

141+
/**
142+
* The name of the {@link Lifecycle} bean used to handle cleanup.
143+
*/
144+
private static final String LOGGING_LIFECYCLE_BEAN_NAME = "springBootLoggingLifecycle";
145+
139146
private static final Map<String, List<String>> DEFAULT_GROUP_LOGGERS;
140147
static {
141148
MultiValueMap<String, String> loggers = new LinkedMultiValueMap<>();
@@ -218,9 +225,8 @@ else if (event instanceof ApplicationEnvironmentPreparedEvent) {
218225
else if (event instanceof ApplicationPreparedEvent) {
219226
onApplicationPreparedEvent((ApplicationPreparedEvent) event);
220227
}
221-
else if (event instanceof ContextClosedEvent
222-
&& ((ContextClosedEvent) event).getApplicationContext().getParent() == null) {
223-
onContextClosedEvent();
228+
else if (event instanceof ContextClosedEvent) {
229+
onContextClosedEvent((ContextClosedEvent) event);
224230
}
225231
else if (event instanceof ApplicationFailedEvent) {
226232
onApplicationFailedEvent();
@@ -241,7 +247,8 @@ private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPrepare
241247
}
242248

243249
private void onApplicationPreparedEvent(ApplicationPreparedEvent event) {
244-
ConfigurableListableBeanFactory beanFactory = event.getApplicationContext().getBeanFactory();
250+
ConfigurableApplicationContext applicationContext = event.getApplicationContext();
251+
ConfigurableListableBeanFactory beanFactory = applicationContext.getBeanFactory();
245252
if (!beanFactory.containsBean(LOGGING_SYSTEM_BEAN_NAME)) {
246253
beanFactory.registerSingleton(LOGGING_SYSTEM_BEAN_NAME, this.loggingSystem);
247254
}
@@ -251,20 +258,29 @@ private void onApplicationPreparedEvent(ApplicationPreparedEvent event) {
251258
if (this.loggerGroups != null && !beanFactory.containsBean(LOGGER_GROUPS_BEAN_NAME)) {
252259
beanFactory.registerSingleton(LOGGER_GROUPS_BEAN_NAME, this.loggerGroups);
253260
}
261+
if (!beanFactory.containsBean(LOGGING_LIFECYCLE_BEAN_NAME) && applicationContext.getParent() == null) {
262+
beanFactory.registerSingleton(LOGGING_LIFECYCLE_BEAN_NAME, new Lifecycle());
263+
}
254264
}
255265

256-
private void onContextClosedEvent() {
257-
if (this.loggingSystem != null) {
258-
this.loggingSystem.cleanUp();
266+
private void onContextClosedEvent(ContextClosedEvent event) {
267+
ApplicationContext applicationContext = event.getApplicationContext();
268+
if (applicationContext.getParent() != null || applicationContext.containsBean(LOGGING_LIFECYCLE_BEAN_NAME)) {
269+
return;
259270
}
271+
cleanupLoggingSystem();
260272
}
261273

262-
private void onApplicationFailedEvent() {
274+
void cleanupLoggingSystem() {
263275
if (this.loggingSystem != null) {
264276
this.loggingSystem.cleanUp();
265277
}
266278
}
267279

280+
private void onApplicationFailedEvent() {
281+
cleanupLoggingSystem();
282+
}
283+
268284
/**
269285
* Initialize the logging system according to preferences expressed through the
270286
* {@link Environment} and the classpath.
@@ -438,4 +454,32 @@ public void setParseArgs(boolean parseArgs) {
438454
this.parseArgs = parseArgs;
439455
}
440456

457+
private class Lifecycle implements SmartLifecycle {
458+
459+
private volatile boolean running;
460+
461+
@Override
462+
public void start() {
463+
this.running = true;
464+
}
465+
466+
@Override
467+
public void stop() {
468+
this.running = false;
469+
cleanupLoggingSystem();
470+
}
471+
472+
@Override
473+
public boolean isRunning() {
474+
return this.running;
475+
}
476+
477+
@Override
478+
public int getPhase() {
479+
// Shutdown late and always after WebServerStartStopLifecycle
480+
return Integer.MIN_VALUE + 1;
481+
}
482+
483+
}
484+
441485
}

0 commit comments

Comments
 (0)