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 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; + } + }