diff --git a/spring-boot/src/main/java/org/springframework/boot/logging/LoggingApplicationListener.java b/spring-boot/src/main/java/org/springframework/boot/logging/LoggingApplicationListener.java
index 4080bdbebd..a3824c9728 100644
--- a/spring-boot/src/main/java/org/springframework/boot/logging/LoggingApplicationListener.java
+++ b/spring-boot/src/main/java/org/springframework/boot/logging/LoggingApplicationListener.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2014 the original author or authors.
+ * Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,8 +27,10 @@ import org.springframework.boot.SpringApplication;
import org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
import org.springframework.boot.context.event.ApplicationStartedEvent;
+import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
+import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.SmartApplicationListener;
import org.springframework.core.Ordered;
import org.springframework.core.env.ConfigurableEnvironment;
@@ -56,6 +58,7 @@ import org.springframework.util.StringUtils;
*
* @author Dave Syer
* @author Phillip Webb
+ * @author Andy Wilkinson
* @see LoggingSystem#get(ClassLoader)
*/
public class LoggingApplicationListener implements SmartApplicationListener {
@@ -95,6 +98,12 @@ public class LoggingApplicationListener implements SmartApplicationListener {
LOG_LEVEL_LOGGERS.add(LogLevel.DEBUG, "org.hibernate.SQL");
}
+ private static Class>[] EVENT_TYPES = { ApplicationStartedEvent.class,
+ ApplicationEnvironmentPreparedEvent.class, ContextClosedEvent.class };
+
+ private static Class>[] SOURCE_TYPES = { SpringApplication.class,
+ ApplicationContext.class };
+
private final Log logger = LogFactory.getLog(getClass());
private LoggingSystem loggingSystem;
@@ -107,13 +116,21 @@ public class LoggingApplicationListener implements SmartApplicationListener {
@Override
public boolean supportsEventType(Class extends ApplicationEvent> eventType) {
- return ApplicationStartedEvent.class.isAssignableFrom(eventType)
- || ApplicationEnvironmentPreparedEvent.class.isAssignableFrom(eventType);
+ return isAssignableFrom(eventType, EVENT_TYPES);
}
@Override
public boolean supportsSourceType(Class> sourceType) {
- return SpringApplication.class.isAssignableFrom(sourceType);
+ return isAssignableFrom(sourceType, SOURCE_TYPES);
+ }
+
+ private boolean isAssignableFrom(Class> type, Class>[] supportedTypes) {
+ for (Class> supportedType : supportedTypes) {
+ if (supportedType.isAssignableFrom(type)) {
+ return true;
+ }
+ }
+ return false;
}
@Override
@@ -124,6 +141,9 @@ public class LoggingApplicationListener implements SmartApplicationListener {
else if (event instanceof ApplicationEnvironmentPreparedEvent) {
onApplicationPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
}
+ else if (event instanceof ContextClosedEvent) {
+ onContextClosedEvent();
+ }
}
private void onApplicationStartedEvent(ApplicationStartedEvent event) {
@@ -140,6 +160,12 @@ public class LoggingApplicationListener implements SmartApplicationListener {
initialize(event.getEnvironment(), event.getSpringApplication().getClassLoader());
}
+ private void onContextClosedEvent() {
+ if (this.loggingSystem != null) {
+ this.loggingSystem.cleanUp();
+ }
+ }
+
/**
* Initialize the logging system according to preferences expressed through the
* {@link Environment} and the classpath.
diff --git a/spring-boot/src/main/java/org/springframework/boot/logging/LoggingSystem.java b/spring-boot/src/main/java/org/springframework/boot/logging/LoggingSystem.java
index 50e46551bc..f4f64f145a 100644
--- a/spring-boot/src/main/java/org/springframework/boot/logging/LoggingSystem.java
+++ b/spring-boot/src/main/java/org/springframework/boot/logging/LoggingSystem.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2014 the original author or authors.
+ * Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -28,6 +28,7 @@ import org.springframework.util.StringUtils;
*
* @author Phillip Webb
* @author Dave Syer
+ * @author Andy Wilkinson
*/
public abstract class LoggingSystem {
@@ -66,6 +67,14 @@ public abstract class LoggingSystem {
*/
public abstract void initialize(String configLocation, LogFile logFile);
+ /**
+ * Clean up the logging system. The default implementation does nothing. Subclasses
+ * should override this method to perform any logging system-specific cleanup.
+ */
+ public void cleanUp() {
+
+ }
+
/**
* Sets the logging level for a given logger.
* @param loggerName the name of the logger to set
diff --git a/spring-boot/src/main/java/org/springframework/boot/logging/Slf4JLoggingSystem.java b/spring-boot/src/main/java/org/springframework/boot/logging/Slf4JLoggingSystem.java
index d3f4545371..ca9796d3db 100644
--- a/spring-boot/src/main/java/org/springframework/boot/logging/Slf4JLoggingSystem.java
+++ b/spring-boot/src/main/java/org/springframework/boot/logging/Slf4JLoggingSystem.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2014 the original author or authors.
+ * Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -36,15 +36,18 @@ public abstract class Slf4JLoggingSystem extends AbstractLoggingSystem {
@Override
public void beforeInitialize() {
super.beforeInitialize();
+ configureJdkLoggingBridgeHandler();
+ }
+
+ @Override
+ public void cleanUp() {
+ removeJdkLoggingBridgeHandler();
+ }
+
+ private void configureJdkLoggingBridgeHandler() {
try {
- if (ClassUtils.isPresent(BRIDGE_HANDLER, getClassLoader())) {
- try {
- SLF4JBridgeHandler.removeHandlersForRootLogger();
- }
- catch (NoSuchMethodError ex) {
- // Method missing in older versions of SLF4J like in JBoss AS 7.1
- SLF4JBridgeHandler.uninstall();
- }
+ if (bridgeHandlerIsAvailable()) {
+ removeJdkLoggingBridgeHandler();
SLF4JBridgeHandler.install();
}
}
@@ -53,4 +56,25 @@ public abstract class Slf4JLoggingSystem extends AbstractLoggingSystem {
}
}
+ private boolean bridgeHandlerIsAvailable() {
+ return ClassUtils.isPresent(BRIDGE_HANDLER, getClassLoader());
+ }
+
+ private void removeJdkLoggingBridgeHandler() {
+ try {
+ if (bridgeHandlerIsAvailable()) {
+ try {
+ SLF4JBridgeHandler.removeHandlersForRootLogger();
+ }
+ catch (NoSuchMethodError ex) {
+ // Method missing in older versions of SLF4J like in JBoss AS 7.1
+ SLF4JBridgeHandler.uninstall();
+ }
+ }
+ }
+ catch (Throwable ex) {
+ // Ignore and continue
+ }
+ }
+
}
diff --git a/spring-boot/src/main/java/org/springframework/boot/logging/log4j/Log4JLoggingSystem.java b/spring-boot/src/main/java/org/springframework/boot/logging/log4j/Log4JLoggingSystem.java
index 970b3b7979..2820fe96dd 100644
--- a/spring-boot/src/main/java/org/springframework/boot/logging/log4j/Log4JLoggingSystem.java
+++ b/spring-boot/src/main/java/org/springframework/boot/logging/log4j/Log4JLoggingSystem.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2013 the original author or authors.
+ * Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -36,6 +36,7 @@ import org.springframework.util.StringUtils;
*
* @author Phillip Webb
* @author Dave Syer
+ * @author Andy Wilkinson
*/
public class Log4JLoggingSystem extends Slf4JLoggingSystem {
diff --git a/spring-boot/src/main/java/org/springframework/boot/logging/logback/LogbackLoggingSystem.java b/spring-boot/src/main/java/org/springframework/boot/logging/logback/LogbackLoggingSystem.java
index cdf087da15..4756be2486 100644
--- a/spring-boot/src/main/java/org/springframework/boot/logging/logback/LogbackLoggingSystem.java
+++ b/spring-boot/src/main/java/org/springframework/boot/logging/logback/LogbackLoggingSystem.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2014 the original author or authors.
+ * Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -42,7 +42,7 @@ import ch.qos.logback.classic.util.ContextInitializer;
import ch.qos.logback.core.spi.FilterReply;
/**
- * {@link LoggingSystem} for for logback.
+ * {@link LoggingSystem} for logback.
*
* @author Phillip Webb
* @author Dave Syer
diff --git a/spring-boot/src/test/java/org/springframework/boot/logging/LoggingApplicationListenerTests.java b/spring-boot/src/test/java/org/springframework/boot/logging/LoggingApplicationListenerTests.java
index 077cd1dab1..d2d1548b13 100644
--- a/spring-boot/src/test/java/org/springframework/boot/logging/LoggingApplicationListenerTests.java
+++ b/spring-boot/src/test/java/org/springframework/boot/logging/LoggingApplicationListenerTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2014 the original author or authors.
+ * Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,7 +18,9 @@ package org.springframework.boot.logging;
import java.io.File;
import java.io.IOException;
+import java.util.logging.Handler;
import java.util.logging.LogManager;
+import java.util.logging.Logger;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -28,11 +30,13 @@ import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
+import org.slf4j.bridge.SLF4JBridgeHandler;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.boot.logging.java.JavaLoggingSystem;
import org.springframework.boot.test.EnvironmentTestUtils;
import org.springframework.boot.test.OutputCapture;
+import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.support.GenericApplicationContext;
import static org.hamcrest.Matchers.containsString;
@@ -46,6 +50,7 @@ import static org.junit.Assert.assertTrue;
*
* @author Dave Syer
* @author Phillip Webb
+ * @author Andy Wilkinson
*/
public class LoggingApplicationListenerTests {
@@ -262,4 +267,22 @@ public class LoggingApplicationListenerTests {
this.logger.debug("testatdebug");
assertThat(this.outputCapture.toString(), not(containsString("testatdebug")));
}
+
+ @Test
+ public void bridgeHandlerLifecycle() throws Exception {
+ assertTrue(bridgeHandlerInstalled());
+ this.initializer.onApplicationEvent(new ContextClosedEvent(this.context));
+ assertFalse(bridgeHandlerInstalled());
+ }
+
+ private boolean bridgeHandlerInstalled() {
+ Logger rootLogger = LogManager.getLogManager().getLogger("");
+ Handler[] handlers = rootLogger.getHandlers();
+ for (Handler handler : handlers) {
+ if (handler instanceof SLF4JBridgeHandler) {
+ return true;
+ }
+ }
+ return false;
+ }
}
diff --git a/spring-boot/src/test/java/org/springframework/boot/logging/log4j/Log4JLoggingSystemTests.java b/spring-boot/src/test/java/org/springframework/boot/logging/log4j/Log4JLoggingSystemTests.java
index d1bf0f4ccf..af39dca37d 100644
--- a/spring-boot/src/test/java/org/springframework/boot/logging/log4j/Log4JLoggingSystemTests.java
+++ b/spring-boot/src/test/java/org/springframework/boot/logging/log4j/Log4JLoggingSystemTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2014 the original author or authors.
+ * Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,12 +17,16 @@
package org.springframework.boot.logging.log4j;
import java.io.File;
+import java.util.logging.Handler;
+import java.util.logging.LogManager;
import org.apache.commons.logging.impl.Log4JLogger;
+import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
+import org.slf4j.bridge.SLF4JBridgeHandler;
import org.springframework.boot.logging.AbstractLoggingSystemTests;
import org.springframework.boot.logging.LogLevel;
import org.springframework.boot.test.OutputCapture;
@@ -37,6 +41,7 @@ import static org.junit.Assert.assertTrue;
* Tests for {@link Log4JLoggingSystem}.
*
* @author Phillip Webb
+ * @author Andy Wilkinson
*/
public class Log4JLoggingSystemTests extends AbstractLoggingSystemTests {
@@ -53,6 +58,12 @@ public class Log4JLoggingSystemTests extends AbstractLoggingSystemTests {
this.logger = new Log4JLogger(getClass().getName());
}
+ @Override
+ @After
+ public void clear() {
+ this.loggingSystem.cleanUp();
+ }
+
@Test
public void noFile() throws Exception {
this.loggingSystem.beforeInitialize();
@@ -118,4 +129,24 @@ public class Log4JLoggingSystemTests extends AbstractLoggingSystemTests {
assertTrue("Wrong output:\n" + output, output.contains("Hello world"));
}
+ @Test
+ public void bridgeHandlerLifecycle() {
+ assertFalse(bridgeHandlerInstalled());
+ this.loggingSystem.beforeInitialize();
+ assertTrue(bridgeHandlerInstalled());
+ this.loggingSystem.cleanUp();
+ assertFalse(bridgeHandlerInstalled());
+ }
+
+ private boolean bridgeHandlerInstalled() {
+ java.util.logging.Logger rootLogger = LogManager.getLogManager().getLogger("");
+ Handler[] handlers = rootLogger.getHandlers();
+ for (Handler handler : handlers) {
+ if (handler instanceof SLF4JBridgeHandler) {
+ return true;
+ }
+ }
+ return false;
+ }
+
}
diff --git a/spring-boot/src/test/java/org/springframework/boot/logging/logback/LogbackLoggingSystemTests.java b/spring-boot/src/test/java/org/springframework/boot/logging/logback/LogbackLoggingSystemTests.java
index ea1cba3d2a..d48299e1e8 100644
--- a/spring-boot/src/test/java/org/springframework/boot/logging/logback/LogbackLoggingSystemTests.java
+++ b/spring-boot/src/test/java/org/springframework/boot/logging/logback/LogbackLoggingSystemTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2014 the original author or authors.
+ * Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,13 +17,17 @@
package org.springframework.boot.logging.logback;
import java.io.File;
+import java.util.logging.Handler;
+import java.util.logging.LogManager;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.impl.SLF4JLogFactory;
+import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.slf4j.ILoggerFactory;
+import org.slf4j.bridge.SLF4JBridgeHandler;
import org.slf4j.impl.StaticLoggerBinder;
import org.springframework.boot.logging.AbstractLoggingSystemTests;
import org.springframework.boot.logging.LogLevel;
@@ -62,6 +66,12 @@ public class LogbackLoggingSystemTests extends AbstractLoggingSystemTests {
this.logger = new SLF4JLogFactory().getInstance(getClass().getName());
}
+ @Override
+ @After
+ public void clear() {
+ this.loggingSystem.cleanUp();
+ }
+
@Test
public void noFile() throws Exception {
this.loggingSystem.beforeInitialize();
@@ -142,4 +152,24 @@ public class LogbackLoggingSystemTests extends AbstractLoggingSystemTests {
assertEquals("slf4j", System.getProperty("org.jboss.logging.provider"));
}
+ @Test
+ public void bridgeHandlerLifecycle() {
+ assertFalse(bridgeHandlerInstalled());
+ this.loggingSystem.beforeInitialize();
+ assertTrue(bridgeHandlerInstalled());
+ this.loggingSystem.cleanUp();
+ assertFalse(bridgeHandlerInstalled());
+ }
+
+ private boolean bridgeHandlerInstalled() {
+ java.util.logging.Logger rootLogger = LogManager.getLogManager().getLogger("");
+ Handler[] handlers = rootLogger.getHandlers();
+ for (Handler handler : handlers) {
+ if (handler instanceof SLF4JBridgeHandler) {
+ return true;
+ }
+ }
+ return false;
+ }
+
}