Ensure logging filters are removed on cleanup

Update `Log4J2LoggingSystem` and `LogbackLoggingSystem` to ensure
that filters are removed when the `cleanUp` method is called. Prior
to this commit application failures would not remove the filter and
no log messages would appear.

The `LoggingApplicationListener` has also been updated since it
previously failed to handle `ApplicationFailureEvents`.

Finally `EventPublishingRunListener` and `DelegatingApplicationListener`
have been updated to deal with `null` parameters and to cope with
listener errors.

Fixes gh-7758
This commit is contained in:
Phillip Webb 2017-01-25 19:01:34 -08:00
parent 2cf93f89f5
commit ef69ae6a89
7 changed files with 74 additions and 33 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2016 the original author or authors. * Copyright 2012-2017 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -1233,7 +1233,6 @@ public class SpringApplication {
finally { finally {
close(context); close(context);
} }
} }
catch (Exception ex) { catch (Exception ex) {
ex.printStackTrace(); ex.printStackTrace();

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2014 the original author or authors. * Copyright 2012-2017 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -17,6 +17,7 @@
package org.springframework.boot.context.config; package org.springframework.boot.context.config;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
@ -70,8 +71,11 @@ public class DelegatingApplicationListener
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private List<ApplicationListener<ApplicationEvent>> getListeners( private List<ApplicationListener<ApplicationEvent>> getListeners(
ConfigurableEnvironment env) { ConfigurableEnvironment environment) {
String classNames = env.getProperty(PROPERTY_NAME); if (environment == null) {
return Collections.emptyList();
}
String classNames = environment.getProperty(PROPERTY_NAME);
List<ApplicationListener<ApplicationEvent>> listeners = new ArrayList<ApplicationListener<ApplicationEvent>>(); List<ApplicationListener<ApplicationEvent>> listeners = new ArrayList<ApplicationListener<ApplicationEvent>>();
if (StringUtils.hasLength(classNames)) { if (StringUtils.hasLength(classNames)) {
for (String className : StringUtils.commaDelimitedListToSet(classNames)) { for (String className : StringUtils.commaDelimitedListToSet(classNames)) {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2016 the original author or authors. * Copyright 2012-2017 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,6 +16,9 @@
package org.springframework.boot.context.event; package org.springframework.boot.context.event;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringApplicationRunListener; import org.springframework.boot.SpringApplicationRunListener;
import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationContextAware;
@ -25,6 +28,7 @@ import org.springframework.context.event.ApplicationEventMulticaster;
import org.springframework.context.event.SimpleApplicationEventMulticaster; import org.springframework.context.event.SimpleApplicationEventMulticaster;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.util.ErrorHandler;
/** /**
* {@link SpringApplicationRunListener} to publish {@link SpringApplicationEvent}s. * {@link SpringApplicationRunListener} to publish {@link SpringApplicationEvent}s.
@ -41,7 +45,7 @@ public class EventPublishingRunListener implements SpringApplicationRunListener,
private final String[] args; private final String[] args;
private final ApplicationEventMulticaster initialMulticaster; private final SimpleApplicationEventMulticaster initialMulticaster;
public EventPublishingRunListener(SpringApplication application, String[] args) { public EventPublishingRunListener(SpringApplication application, String[] args) {
this.application = application; this.application = application;
@ -88,9 +92,18 @@ public class EventPublishingRunListener implements SpringApplicationRunListener,
@Override @Override
public void finished(ConfigurableApplicationContext context, Throwable exception) { public void finished(ConfigurableApplicationContext context, Throwable exception) {
SpringApplicationEvent event = getFinishedEvent(context, exception);
if (context != null) {
// Listeners have been registered to the application context so we should // Listeners have been registered to the application context so we should
// use it at this point // use it at this point if we can
context.publishEvent(getFinishedEvent(context, exception)); context.publishEvent(event);
}
else {
if (event instanceof ApplicationFailedEvent) {
this.initialMulticaster.setErrorHandler(new LoggingErrorHandler());
}
this.initialMulticaster.multicastEvent(event);
}
} }
private SpringApplicationEvent getFinishedEvent( private SpringApplicationEvent getFinishedEvent(
@ -102,4 +115,15 @@ public class EventPublishingRunListener implements SpringApplicationRunListener,
return new ApplicationReadyEvent(this.application, this.args, context); return new ApplicationReadyEvent(this.application, this.args, context);
} }
private static class LoggingErrorHandler implements ErrorHandler {
private static Log logger = LogFactory.getLog(EventPublishingRunListener.class);
@Override
public void handleError(Throwable throwable) {
logger.warn("Error calling ApplicationEventListener", throwable);
}
}
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2016 the original author or authors. * Copyright 2012-2017 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -163,7 +163,7 @@ public class LoggingApplicationListener implements GenericApplicationListener {
private static Class<?>[] EVENT_TYPES = { ApplicationStartedEvent.class, private static Class<?>[] EVENT_TYPES = { ApplicationStartedEvent.class,
ApplicationEnvironmentPreparedEvent.class, ApplicationPreparedEvent.class, ApplicationEnvironmentPreparedEvent.class, ApplicationPreparedEvent.class,
ContextClosedEvent.class }; ContextClosedEvent.class, ApplicationFailedEvent.class };
private static Class<?>[] SOURCE_TYPES = { SpringApplication.class, private static Class<?>[] SOURCE_TYPES = { SpringApplication.class,
ApplicationContext.class }; ApplicationContext.class };

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2016 the original author or authors. * Copyright 2012-2017 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -218,6 +218,7 @@ public class Log4J2LoggingSystem extends Slf4JLoggingSystem {
super.cleanUp(); super.cleanUp();
LoggerContext loggerContext = getLoggerContext(); LoggerContext loggerContext = getLoggerContext();
markAsUninitialized(loggerContext); markAsUninitialized(loggerContext);
loggerContext.getConfiguration().removeFilter(FILTER);
} }
private LoggerConfig getLoggerConfig(String name) { private LoggerConfig getLoggerConfig(String name) {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2016 the original author or authors. * Copyright 2012-2017 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -193,9 +193,11 @@ public class LogbackLoggingSystem extends Slf4JLoggingSystem {
@Override @Override
public void cleanUp() { public void cleanUp() {
markAsUninitialized(getLoggerContext()); LoggerContext context = getLoggerContext();
markAsUninitialized(context);
super.cleanUp(); super.cleanUp();
getLoggerContext().getStatusManager().clear(); context.getStatusManager().clear();
context.getTurboFilterList().remove(FILTER);
} }
@Override @Override

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2016 the original author or authors. * Copyright 2012-2017 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -41,7 +41,10 @@ import org.springframework.boot.context.event.ApplicationFailedEvent;
import org.springframework.boot.context.event.ApplicationStartedEvent; import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.boot.logging.java.JavaLoggingSystem; import org.springframework.boot.logging.java.JavaLoggingSystem;
import org.springframework.boot.testutil.InternalOutputCapture; import org.springframework.boot.testutil.InternalOutputCapture;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextClosedEvent; import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.SimpleApplicationEventMulticaster;
import org.springframework.context.support.GenericApplicationContext; import org.springframework.context.support.GenericApplicationContext;
import org.springframework.test.context.support.TestPropertySourceUtils; import org.springframework.test.context.support.TestPropertySourceUtils;
import org.springframework.test.util.ReflectionTestUtils; import org.springframework.test.util.ReflectionTestUtils;
@ -83,8 +86,7 @@ public class LoggingApplicationListenerTests {
public void init() throws SecurityException, IOException { public void init() throws SecurityException, IOException {
LogManager.getLogManager().readConfiguration( LogManager.getLogManager().readConfiguration(
JavaLoggingSystem.class.getResourceAsStream("logging.properties")); JavaLoggingSystem.class.getResourceAsStream("logging.properties"));
this.initializer.onApplicationEvent( multicastEvent(new ApplicationStartedEvent(new SpringApplication(), NO_ARGS));
new ApplicationStartedEvent(new SpringApplication(), NO_ARGS));
new File("target/foo.log").delete(); new File("target/foo.log").delete();
new File(tmpDir() + "/spring.log").delete(); new File(tmpDir() + "/spring.log").delete();
} }
@ -352,8 +354,8 @@ public class LoggingApplicationListenerTests {
public void parseArgsDoesntReplace() throws Exception { public void parseArgsDoesntReplace() throws Exception {
this.initializer.setSpringBootLogging(LogLevel.ERROR); this.initializer.setSpringBootLogging(LogLevel.ERROR);
this.initializer.setParseArgs(false); this.initializer.setParseArgs(false);
this.initializer.onApplicationEvent(new ApplicationStartedEvent( multicastEvent(new ApplicationStartedEvent(this.springApplication,
this.springApplication, new String[] { "--debug" })); new String[] { "--debug" }));
this.initializer.initialize(this.context.getEnvironment(), this.initializer.initialize(this.context.getEnvironment(),
this.context.getClassLoader()); this.context.getClassLoader());
this.logger.debug("testatdebug"); this.logger.debug("testatdebug");
@ -363,7 +365,7 @@ public class LoggingApplicationListenerTests {
@Test @Test
public void bridgeHandlerLifecycle() throws Exception { public void bridgeHandlerLifecycle() throws Exception {
assertThat(bridgeHandlerInstalled()).isTrue(); assertThat(bridgeHandlerInstalled()).isTrue();
this.initializer.onApplicationEvent(new ContextClosedEvent(this.context)); multicastEvent(new ContextClosedEvent(this.context));
assertThat(bridgeHandlerInstalled()).isFalse(); assertThat(bridgeHandlerInstalled()).isFalse();
} }
@ -396,7 +398,7 @@ public class LoggingApplicationListenerTests {
TestLoggingApplicationListener listener = new TestLoggingApplicationListener(); TestLoggingApplicationListener listener = new TestLoggingApplicationListener();
System.setProperty(LoggingSystem.class.getName(), System.setProperty(LoggingSystem.class.getName(),
TestShutdownHandlerLoggingSystem.class.getName()); TestShutdownHandlerLoggingSystem.class.getName());
listener.onApplicationEvent( multicastEvent(listener,
new ApplicationStartedEvent(new SpringApplication(), NO_ARGS)); new ApplicationStartedEvent(new SpringApplication(), NO_ARGS));
listener.initialize(this.context.getEnvironment(), this.context.getClassLoader()); listener.initialize(this.context.getEnvironment(), this.context.getClassLoader());
assertThat(listener.shutdownHook).isNull(); assertThat(listener.shutdownHook).isNull();
@ -409,7 +411,7 @@ public class LoggingApplicationListenerTests {
TestShutdownHandlerLoggingSystem.class.getName()); TestShutdownHandlerLoggingSystem.class.getName());
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(this.context, TestPropertySourceUtils.addInlinedPropertiesToEnvironment(this.context,
"logging.register_shutdown_hook=true"); "logging.register_shutdown_hook=true");
listener.onApplicationEvent( multicastEvent(listener,
new ApplicationStartedEvent(new SpringApplication(), NO_ARGS)); new ApplicationStartedEvent(new SpringApplication(), NO_ARGS));
listener.initialize(this.context.getEnvironment(), this.context.getClassLoader()); listener.initialize(this.context.getEnvironment(), this.context.getClassLoader());
assertThat(listener.shutdownHook).isNotNull(); assertThat(listener.shutdownHook).isNotNull();
@ -422,12 +424,12 @@ public class LoggingApplicationListenerTests {
public void closingContextCleansUpLoggingSystem() { public void closingContextCleansUpLoggingSystem() {
System.setProperty(LoggingSystem.SYSTEM_PROPERTY, System.setProperty(LoggingSystem.SYSTEM_PROPERTY,
TestCleanupLoggingSystem.class.getName()); TestCleanupLoggingSystem.class.getName());
this.initializer.onApplicationEvent( multicastEvent(
new ApplicationStartedEvent(this.springApplication, new String[0])); new ApplicationStartedEvent(this.springApplication, new String[0]));
TestCleanupLoggingSystem loggingSystem = (TestCleanupLoggingSystem) ReflectionTestUtils TestCleanupLoggingSystem loggingSystem = (TestCleanupLoggingSystem) ReflectionTestUtils
.getField(this.initializer, "loggingSystem"); .getField(this.initializer, "loggingSystem");
assertThat(loggingSystem.cleanedUp).isFalse(); assertThat(loggingSystem.cleanedUp).isFalse();
this.initializer.onApplicationEvent(new ContextClosedEvent(this.context)); multicastEvent(new ContextClosedEvent(this.context));
assertThat(loggingSystem.cleanedUp).isTrue(); assertThat(loggingSystem.cleanedUp).isTrue();
} }
@ -435,16 +437,16 @@ public class LoggingApplicationListenerTests {
public void closingChildContextDoesNotCleanUpLoggingSystem() { public void closingChildContextDoesNotCleanUpLoggingSystem() {
System.setProperty(LoggingSystem.SYSTEM_PROPERTY, System.setProperty(LoggingSystem.SYSTEM_PROPERTY,
TestCleanupLoggingSystem.class.getName()); TestCleanupLoggingSystem.class.getName());
this.initializer.onApplicationEvent( multicastEvent(
new ApplicationStartedEvent(this.springApplication, new String[0])); new ApplicationStartedEvent(this.springApplication, new String[0]));
TestCleanupLoggingSystem loggingSystem = (TestCleanupLoggingSystem) ReflectionTestUtils TestCleanupLoggingSystem loggingSystem = (TestCleanupLoggingSystem) ReflectionTestUtils
.getField(this.initializer, "loggingSystem"); .getField(this.initializer, "loggingSystem");
assertThat(loggingSystem.cleanedUp).isFalse(); assertThat(loggingSystem.cleanedUp).isFalse();
GenericApplicationContext childContext = new GenericApplicationContext(); GenericApplicationContext childContext = new GenericApplicationContext();
childContext.setParent(this.context); childContext.setParent(this.context);
this.initializer.onApplicationEvent(new ContextClosedEvent(childContext)); multicastEvent(new ContextClosedEvent(childContext));
assertThat(loggingSystem.cleanedUp).isFalse(); assertThat(loggingSystem.cleanedUp).isFalse();
this.initializer.onApplicationEvent(new ContextClosedEvent(this.context)); multicastEvent(new ContextClosedEvent(this.context));
assertThat(loggingSystem.cleanedUp).isTrue(); assertThat(loggingSystem.cleanedUp).isTrue();
childContext.close(); childContext.close();
} }
@ -491,17 +493,26 @@ public class LoggingApplicationListenerTests {
public void applicationFailedEventCleansUpLoggingSystem() { public void applicationFailedEventCleansUpLoggingSystem() {
System.setProperty(LoggingSystem.SYSTEM_PROPERTY, System.setProperty(LoggingSystem.SYSTEM_PROPERTY,
TestCleanupLoggingSystem.class.getName()); TestCleanupLoggingSystem.class.getName());
this.initializer.onApplicationEvent( multicastEvent(
new ApplicationStartedEvent(this.springApplication, new String[0])); new ApplicationStartedEvent(this.springApplication, new String[0]));
TestCleanupLoggingSystem loggingSystem = (TestCleanupLoggingSystem) ReflectionTestUtils TestCleanupLoggingSystem loggingSystem = (TestCleanupLoggingSystem) ReflectionTestUtils
.getField(this.initializer, "loggingSystem"); .getField(this.initializer, "loggingSystem");
assertThat(loggingSystem.cleanedUp).isFalse(); assertThat(loggingSystem.cleanedUp).isFalse();
this.initializer multicastEvent(new ApplicationFailedEvent(this.springApplication, new String[0],
.onApplicationEvent(new ApplicationFailedEvent(this.springApplication, new GenericApplicationContext(), new Exception()));
new String[0], new GenericApplicationContext(), new Exception()));
assertThat(loggingSystem.cleanedUp).isTrue(); assertThat(loggingSystem.cleanedUp).isTrue();
} }
private void multicastEvent(ApplicationEvent event) {
multicastEvent(this.initializer, event);
}
private void multicastEvent(ApplicationListener<?> listener, ApplicationEvent event) {
SimpleApplicationEventMulticaster multicaster = new SimpleApplicationEventMulticaster();
multicaster.addApplicationListener(listener);
multicaster.multicastEvent(event);
}
private boolean bridgeHandlerInstalled() { private boolean bridgeHandlerInstalled() {
Logger rootLogger = LogManager.getLogManager().getLogger(""); Logger rootLogger = LogManager.getLogManager().getLogger("");
Handler[] handlers = rootLogger.getHandlers(); Handler[] handlers = rootLogger.getHandlers();