Commit 43361e0c authored by Andy Wilkinson's avatar Andy Wilkinson

Prevent child applications from reinitializing the logging system

Closes gh-6688
parent 25e72fab
...@@ -28,6 +28,7 @@ import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; ...@@ -28,6 +28,7 @@ import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.bind.RelaxedPropertyResolver; import org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent; import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
import org.springframework.boot.context.event.ApplicationFailedEvent;
import org.springframework.boot.context.event.ApplicationPreparedEvent; import org.springframework.boot.context.event.ApplicationPreparedEvent;
import org.springframework.boot.context.event.ApplicationStartedEvent; import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
...@@ -214,6 +215,9 @@ public class LoggingApplicationListener implements GenericApplicationListener { ...@@ -214,6 +215,9 @@ public class LoggingApplicationListener implements GenericApplicationListener {
.getApplicationContext().getParent() == null) { .getApplicationContext().getParent() == null) {
onContextClosedEvent(); onContextClosedEvent();
} }
else if (event instanceof ApplicationFailedEvent) {
onApplicationFailedEvent();
}
} }
private void onApplicationStartedEvent(ApplicationStartedEvent event) { private void onApplicationStartedEvent(ApplicationStartedEvent event) {
...@@ -245,6 +249,12 @@ public class LoggingApplicationListener implements GenericApplicationListener { ...@@ -245,6 +249,12 @@ public class LoggingApplicationListener implements GenericApplicationListener {
} }
} }
private void onApplicationFailedEvent() {
if (this.loggingSystem != null) {
this.loggingSystem.cleanUp();
}
}
/** /**
* Initialize the logging system according to preferences expressed through the * Initialize the logging system according to preferences expressed through the
* {@link Environment} and the classpath. * {@link Environment} and the classpath.
......
...@@ -129,15 +129,24 @@ public class Log4J2LoggingSystem extends Slf4JLoggingSystem { ...@@ -129,15 +129,24 @@ public class Log4J2LoggingSystem extends Slf4JLoggingSystem {
@Override @Override
public void beforeInitialize() { public void beforeInitialize() {
LoggerContext loggerContext = getLoggerContext();
if (isAlreadyInitialized(loggerContext)) {
return;
}
super.beforeInitialize(); super.beforeInitialize();
getLoggerContext().getConfiguration().addFilter(FILTER); loggerContext.getConfiguration().addFilter(FILTER);
} }
@Override @Override
public void initialize(LoggingInitializationContext initializationContext, public void initialize(LoggingInitializationContext initializationContext,
String configLocation, LogFile logFile) { String configLocation, LogFile logFile) {
getLoggerContext().getConfiguration().removeFilter(FILTER); LoggerContext loggerContext = getLoggerContext();
if (isAlreadyInitialized(loggerContext)) {
return;
}
loggerContext.getConfiguration().removeFilter(FILTER);
super.initialize(initializationContext, configLocation, logFile); super.initialize(initializationContext, configLocation, logFile);
markAsInitialized(loggerContext);
} }
@Override @Override
...@@ -204,6 +213,13 @@ public class Log4J2LoggingSystem extends Slf4JLoggingSystem { ...@@ -204,6 +213,13 @@ public class Log4J2LoggingSystem extends Slf4JLoggingSystem {
return new ShutdownHandler(); return new ShutdownHandler();
} }
@Override
public void cleanUp() {
super.cleanUp();
LoggerContext loggerContext = getLoggerContext();
markAsUninitialized(loggerContext);
}
private LoggerConfig getLoggerConfig(String name) { private LoggerConfig getLoggerConfig(String name) {
name = (StringUtils.hasText(name) ? name : LogManager.ROOT_LOGGER_NAME); name = (StringUtils.hasText(name) ? name : LogManager.ROOT_LOGGER_NAME);
return getLoggerContext().getConfiguration().getLoggers().get(name); return getLoggerContext().getConfiguration().getLoggers().get(name);
...@@ -213,6 +229,18 @@ public class Log4J2LoggingSystem extends Slf4JLoggingSystem { ...@@ -213,6 +229,18 @@ public class Log4J2LoggingSystem extends Slf4JLoggingSystem {
return (LoggerContext) LogManager.getContext(false); return (LoggerContext) LogManager.getContext(false);
} }
private boolean isAlreadyInitialized(LoggerContext loggerContext) {
return LoggingSystem.class.getName().equals(loggerContext.getExternalContext());
}
private void markAsInitialized(LoggerContext loggerContext) {
loggerContext.setExternalContext(LoggingSystem.class.getName());
}
private void markAsUninitialized(LoggerContext loggerContext) {
loggerContext.setExternalContext(null);
}
private final class ShutdownHandler implements Runnable { private final class ShutdownHandler implements Runnable {
@Override @Override
...@@ -221,4 +249,5 @@ public class Log4J2LoggingSystem extends Slf4JLoggingSystem { ...@@ -221,4 +249,5 @@ public class Log4J2LoggingSystem extends Slf4JLoggingSystem {
} }
} }
} }
...@@ -94,16 +94,25 @@ public class LogbackLoggingSystem extends Slf4JLoggingSystem { ...@@ -94,16 +94,25 @@ public class LogbackLoggingSystem extends Slf4JLoggingSystem {
@Override @Override
public void beforeInitialize() { public void beforeInitialize() {
LoggerContext loggerContext = getLoggerContext();
if (isAlreadyInitialized(loggerContext)) {
return;
}
super.beforeInitialize(); super.beforeInitialize();
getLogger(null).getLoggerContext().getTurboFilterList().add(FILTER); loggerContext.getTurboFilterList().add(FILTER);
configureJBossLoggingToUseSlf4j(); configureJBossLoggingToUseSlf4j();
} }
@Override @Override
public void initialize(LoggingInitializationContext initializationContext, public void initialize(LoggingInitializationContext initializationContext,
String configLocation, LogFile logFile) { String configLocation, LogFile logFile) {
getLogger(null).getLoggerContext().getTurboFilterList().remove(FILTER); LoggerContext loggerContext = getLoggerContext();
if (isAlreadyInitialized(loggerContext)) {
return;
}
loggerContext.getTurboFilterList().remove(FILTER);
super.initialize(initializationContext, configLocation, logFile); super.initialize(initializationContext, configLocation, logFile);
markAsInitialized(loggerContext);
if (StringUtils.hasText(System.getProperty(CONFIGURATION_FILE_PROPERTY))) { if (StringUtils.hasText(System.getProperty(CONFIGURATION_FILE_PROPERTY))) {
getLogger(LogbackLoggingSystem.class.getName()).warn( getLogger(LogbackLoggingSystem.class.getName()).warn(
"Ignoring '" + CONFIGURATION_FILE_PROPERTY + "' system property. " "Ignoring '" + CONFIGURATION_FILE_PROPERTY + "' system property. "
...@@ -184,6 +193,7 @@ public class LogbackLoggingSystem extends Slf4JLoggingSystem { ...@@ -184,6 +193,7 @@ public class LogbackLoggingSystem extends Slf4JLoggingSystem {
@Override @Override
public void cleanUp() { public void cleanUp() {
markAsUninitialized(getLoggerContext());
super.cleanUp(); super.cleanUp();
getLoggerContext().getStatusManager().clear(); getLoggerContext().getStatusManager().clear();
} }
...@@ -243,6 +253,18 @@ public class LogbackLoggingSystem extends Slf4JLoggingSystem { ...@@ -243,6 +253,18 @@ public class LogbackLoggingSystem extends Slf4JLoggingSystem {
return "unknown location"; return "unknown location";
} }
private boolean isAlreadyInitialized(LoggerContext loggerContext) {
return loggerContext.getObject(LoggingSystem.class.getName()) != null;
}
private void markAsInitialized(LoggerContext loggerContext) {
loggerContext.putObject(LoggingSystem.class.getName(), new Object());
}
private void markAsUninitialized(LoggerContext loggerContext) {
loggerContext.removeObject(LoggingSystem.class.getName());
}
private final class ShutdownHandler implements Runnable { private final class ShutdownHandler implements Runnable {
@Override @Override
......
...@@ -16,9 +16,15 @@ ...@@ -16,9 +16,15 @@
package org.springframework.boot.logging; package org.springframework.boot.logging;
import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.boot.testutil.InternalOutputCapture;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
...@@ -31,6 +37,9 @@ import static org.assertj.core.api.Assertions.assertThat; ...@@ -31,6 +37,9 @@ import static org.assertj.core.api.Assertions.assertThat;
*/ */
public class LoggingApplicationListenerIntegrationTests { public class LoggingApplicationListenerIntegrationTests {
@Rule
public InternalOutputCapture outputCapture = new InternalOutputCapture();
@Test @Test
public void loggingSystemRegisteredInTheContext() { public void loggingSystemRegisteredInTheContext() {
ConfigurableApplicationContext context = new SpringApplicationBuilder( ConfigurableApplicationContext context = new SpringApplicationBuilder(
...@@ -44,6 +53,21 @@ public class LoggingApplicationListenerIntegrationTests { ...@@ -44,6 +53,21 @@ public class LoggingApplicationListenerIntegrationTests {
} }
} }
@Test
public void loggingPerformedDuringChildApplicationStartIsNotLost() {
new SpringApplicationBuilder(Config.class).web(false).child(Config.class)
.web(false).listeners(new ApplicationListener<ApplicationStartedEvent>() {
private final Logger logger = LoggerFactory.getLogger(getClass());
@Override
public void onApplicationEvent(ApplicationStartedEvent event) {
this.logger.info("Child application started");
}
}).run();
assertThat(this.outputCapture.toString()).contains("Child application started");
}
@Component @Component
static class SampleService { static class SampleService {
...@@ -55,4 +79,8 @@ public class LoggingApplicationListenerIntegrationTests { ...@@ -55,4 +79,8 @@ public class LoggingApplicationListenerIntegrationTests {
} }
static class Config {
}
} }
...@@ -37,6 +37,7 @@ import org.slf4j.bridge.SLF4JBridgeHandler; ...@@ -37,6 +37,7 @@ import org.slf4j.bridge.SLF4JBridgeHandler;
import org.springframework.boot.ApplicationPid; import org.springframework.boot.ApplicationPid;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
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;
...@@ -464,6 +465,21 @@ public class LoggingApplicationListenerTests { ...@@ -464,6 +465,21 @@ public class LoggingApplicationListenerTests {
.isEqualTo("target/" + new ApplicationPid().toString() + ".log"); .isEqualTo("target/" + new ApplicationPid().toString() + ".log");
} }
@Test
public void applicationFailedEventCleansUpLoggingSystem() {
System.setProperty(LoggingSystem.SYSTEM_PROPERTY,
TestCleanupLoggingSystem.class.getName());
this.initializer.onApplicationEvent(
new ApplicationStartedEvent(this.springApplication, new String[0]));
TestCleanupLoggingSystem loggingSystem = (TestCleanupLoggingSystem) ReflectionTestUtils
.getField(this.initializer, "loggingSystem");
assertThat(loggingSystem.cleanedUp).isFalse();
this.initializer
.onApplicationEvent(new ApplicationFailedEvent(this.springApplication,
new String[0], new GenericApplicationContext(), new Exception()));
assertThat(loggingSystem.cleanedUp).isTrue();
}
private boolean bridgeHandlerInstalled() { private boolean bridgeHandlerInstalled() {
Logger rootLogger = LogManager.getLogManager().getLogger(""); Logger rootLogger = LogManager.getLogManager().getLogger("");
Handler[] handlers = rootLogger.getHandlers(); Handler[] handlers = rootLogger.getHandlers();
......
...@@ -16,6 +16,8 @@ ...@@ -16,6 +16,8 @@
package org.springframework.boot.logging.log4j2; package org.springframework.boot.logging.log4j2;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File; import java.io.File;
import java.io.FileReader; import java.io.FileReader;
import java.util.ArrayList; import java.util.ArrayList;
...@@ -25,6 +27,7 @@ import java.util.List; ...@@ -25,6 +27,7 @@ import java.util.List;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.config.Configuration; import org.apache.logging.log4j.core.config.Configuration;
import org.hamcrest.Matcher; import org.hamcrest.Matcher;
import org.hamcrest.Matchers; import org.hamcrest.Matchers;
...@@ -43,6 +46,10 @@ import org.springframework.util.StringUtils; ...@@ -43,6 +46,10 @@ import org.springframework.util.StringUtils;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.not;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
/** /**
* Tests for {@link Log4J2LoggingSystem}. * Tests for {@link Log4J2LoggingSystem}.
...@@ -62,6 +69,7 @@ public class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests { ...@@ -62,6 +69,7 @@ public class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests {
@Before @Before
public void setup() { public void setup() {
this.loggingSystem.cleanUp();
this.logger = LogManager.getLogger(getClass()); this.logger = LogManager.getLogger(getClass());
} }
...@@ -223,6 +231,24 @@ public class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests { ...@@ -223,6 +231,24 @@ public class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests {
} }
} }
@Test
public void initializationIsOnlyPerformedOnceUntilCleanedUp() throws Exception {
LoggerContext loggerContext = (LoggerContext) LogManager.getContext(false);
PropertyChangeListener listener = mock(PropertyChangeListener.class);
loggerContext.addPropertyChangeListener(listener);
this.loggingSystem.beforeInitialize();
this.loggingSystem.initialize(null, null, null);
this.loggingSystem.beforeInitialize();
this.loggingSystem.initialize(null, null, null);
this.loggingSystem.beforeInitialize();
this.loggingSystem.initialize(null, null, null);
verify(listener, times(2)).propertyChange(any(PropertyChangeEvent.class));
this.loggingSystem.cleanUp();
this.loggingSystem.beforeInitialize();
this.loggingSystem.initialize(null, null, null);
verify(listener, times(4)).propertyChange(any(PropertyChangeEvent.class));
}
private static class TestLog4J2LoggingSystem extends Log4J2LoggingSystem { private static class TestLog4J2LoggingSystem extends Log4J2LoggingSystem {
private List<String> availableClasses = new ArrayList<String>(); private List<String> availableClasses = new ArrayList<String>();
......
...@@ -23,6 +23,7 @@ import java.util.logging.LogManager; ...@@ -23,6 +23,7 @@ import java.util.logging.LogManager;
import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext; import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.spi.LoggerContextListener;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.impl.SLF4JLogFactory; import org.apache.commons.logging.impl.SLF4JLogFactory;
import org.hamcrest.Matcher; import org.hamcrest.Matcher;
...@@ -48,6 +49,9 @@ import org.springframework.util.StringUtils; ...@@ -48,6 +49,9 @@ import org.springframework.util.StringUtils;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.not;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
/** /**
* Tests for {@link LogbackLoggingSystem}. * Tests for {@link LogbackLoggingSystem}.
...@@ -72,6 +76,7 @@ public class LogbackLoggingSystemTests extends AbstractLoggingSystemTests { ...@@ -72,6 +76,7 @@ public class LogbackLoggingSystemTests extends AbstractLoggingSystemTests {
@Before @Before
public void setup() { public void setup() {
this.loggingSystem.cleanUp();
this.logger = new SLF4JLogFactory().getInstance(getClass().getName()); this.logger = new SLF4JLogFactory().getInstance(getClass().getName());
this.environment = new MockEnvironment(); this.environment = new MockEnvironment();
this.initializationContext = new LoggingInitializationContext(this.environment); this.initializationContext = new LoggingInitializationContext(this.environment);
...@@ -304,17 +309,34 @@ public class LogbackLoggingSystemTests extends AbstractLoggingSystemTests { ...@@ -304,17 +309,34 @@ public class LogbackLoggingSystemTests extends AbstractLoggingSystemTests {
} }
@Test @Test
public void reinitializeShouldSetSystemProperty() throws Exception { public void initializeShouldSetSystemProperty() throws Exception {
// gh-5491 // gh-5491
this.loggingSystem.beforeInitialize(); this.loggingSystem.beforeInitialize();
this.logger.info("Hidden"); this.logger.info("Hidden");
this.loggingSystem.initialize(this.initializationContext, null, null);
LogFile logFile = getLogFile(tmpDir() + "/example.log", null, false); LogFile logFile = getLogFile(tmpDir() + "/example.log", null, false);
this.loggingSystem.initialize(this.initializationContext, this.loggingSystem.initialize(this.initializationContext,
"classpath:logback-nondefault.xml", logFile); "classpath:logback-nondefault.xml", logFile);
assertThat(System.getProperty("LOG_FILE")).endsWith("example.log"); assertThat(System.getProperty("LOG_FILE")).endsWith("example.log");
} }
@Test
public void initializationIsOnlyPerformedOnceUntilCleanedUp() throws Exception {
LoggerContext loggerContext = (LoggerContext) StaticLoggerBinder.getSingleton()
.getLoggerFactory();
LoggerContextListener listener = mock(LoggerContextListener.class);
loggerContext.addListener(listener);
this.loggingSystem.beforeInitialize();
this.loggingSystem.initialize(this.initializationContext, null, null);
this.loggingSystem.beforeInitialize();
this.loggingSystem.initialize(this.initializationContext, null, null);
verify(listener, times(1)).onReset(loggerContext);
this.loggingSystem.cleanUp();
loggerContext.addListener(listener);
this.loggingSystem.beforeInitialize();
this.loggingSystem.initialize(this.initializationContext, null, null);
verify(listener, times(2)).onReset(loggerContext);
}
private String getLineWithText(File file, String outputSearch) throws Exception { private String getLineWithText(File file, String outputSearch) throws Exception {
return getLineWithText(FileCopyUtils.copyToString(new FileReader(file)), return getLineWithText(FileCopyUtils.copyToString(new FileReader(file)),
outputSearch); outputSearch);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment