From d4fd2b7aa511dad77921822462ebd2ed9dd327e5 Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Fri, 16 Sep 2016 13:44:48 +0100 Subject: [PATCH] Extract logging system shutdown into a separate listener LoggingSystemShutdownListener runs immediately after BootstrapApplicationListener and contains the code which used to be in that to clean up the logging system (and make it go dark). That is still a little bit of a hack to work around some limitations in the static logging libraries (via the Spring Boot Logging System and LoggingApplicationListener). If the LoggingApplicationListener is then *not* applied in the ContextRefresher, then we don't need the LoggingSystemShutdownListener either. In fact we can narrow down the ContextRefresher to be only interested in the listeners that affect the Environment (of which we know of only 2). It means that any listeners that are added that affect the Environment will not be applied, but there are plenty of more "official" channels for modifying the Environment (EnvironmentPostProcessor and PropertySourceLocator), so users can easily migrate to a better implementation. --- .../BootstrapApplicationListener.java | 13 ---- .../LoggingSystemShutdownListener.java | 62 +++++++++++++++++++ .../context/refresh/ContextRefresher.java | 27 ++++---- .../main/resources/META-INF/spring.factories | 1 + 4 files changed, 76 insertions(+), 27 deletions(-) create mode 100644 spring-cloud-context/src/main/java/org/springframework/cloud/bootstrap/LoggingSystemShutdownListener.java diff --git a/spring-cloud-context/src/main/java/org/springframework/cloud/bootstrap/BootstrapApplicationListener.java b/spring-cloud-context/src/main/java/org/springframework/cloud/bootstrap/BootstrapApplicationListener.java index 0af9497b..8346c63e 100644 --- a/spring-cloud-context/src/main/java/org/springframework/cloud/bootstrap/BootstrapApplicationListener.java +++ b/spring-cloud-context/src/main/java/org/springframework/cloud/bootstrap/BootstrapApplicationListener.java @@ -29,7 +29,6 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.builder.ParentContextApplicationContextInitializer; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent; -import org.springframework.boot.logging.LoggingSystem; import org.springframework.cloud.bootstrap.encrypt.EnvironmentDecryptApplicationInitializer; import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ApplicationListener; @@ -85,18 +84,6 @@ public class BootstrapApplicationListener ConfigurableApplicationContext context = bootstrapServiceContext(environment, event.getSpringApplication()); apply(context, event.getSpringApplication(), environment); - shutdownLogging(); - } - - private void shutdownLogging() { - // Clean up the logging system. Logging will go dark until the - // ConfigFileApplicationListener fires, but this is the price we pay for that - // listener being able to adjust the log levels according to what it finds in its - // own configuration. - LoggingSystem loggingSystem = LoggingSystem - .get(ClassUtils.getDefaultClassLoader()); - loggingSystem.cleanUp(); - loggingSystem.beforeInitialize(); } private ConfigurableApplicationContext bootstrapServiceContext( diff --git a/spring-cloud-context/src/main/java/org/springframework/cloud/bootstrap/LoggingSystemShutdownListener.java b/spring-cloud-context/src/main/java/org/springframework/cloud/bootstrap/LoggingSystemShutdownListener.java new file mode 100644 index 00000000..684e0098 --- /dev/null +++ b/spring-cloud-context/src/main/java/org/springframework/cloud/bootstrap/LoggingSystemShutdownListener.java @@ -0,0 +1,62 @@ +/* + * Copyright 2013-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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.bootstrap; + +import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent; +import org.springframework.boot.logging.LoggingSystem; +import org.springframework.context.ApplicationListener; +import org.springframework.core.Ordered; +import org.springframework.util.ClassUtils; + +/** + * Cleans up the logging system immediately after the bootstrap context is created on + * startup. Logging will go dark until the ConfigFileApplicationListener fires, but this + * is the price we pay for that listener being able to adjust the log levels according to + * what it finds in its own configuration. + * + * @author Dave Syer + */ +public class LoggingSystemShutdownListener + implements ApplicationListener, Ordered { + + public static final int DEFAULT_ORDER = BootstrapApplicationListener.DEFAULT_ORDER + + 1; + + private int order = DEFAULT_ORDER; + + @Override + public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) { + shutdownLogging(); + } + + private void shutdownLogging() { + LoggingSystem loggingSystem = LoggingSystem + .get(ClassUtils.getDefaultClassLoader()); + loggingSystem.cleanUp(); + loggingSystem.beforeInitialize(); + } + + public void setOrder(int order) { + this.order = order; + } + + @Override + public int getOrder() { + return this.order; + } + +} diff --git a/spring-cloud-context/src/main/java/org/springframework/cloud/context/refresh/ContextRefresher.java b/spring-cloud-context/src/main/java/org/springframework/cloud/context/refresh/ContextRefresher.java index 7581de00..2b7b59a3 100644 --- a/spring-cloud-context/src/main/java/org/springframework/cloud/context/refresh/ContextRefresher.java +++ b/spring-cloud-context/src/main/java/org/springframework/cloud/context/refresh/ContextRefresher.java @@ -10,9 +10,10 @@ import java.util.Set; import org.springframework.boot.Banner.Mode; import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.context.config.ConfigFileApplicationListener; +import org.springframework.cloud.bootstrap.BootstrapApplicationListener; import org.springframework.cloud.context.environment.EnvironmentChangeEvent; import org.springframework.cloud.context.scope.refresh.RefreshScope; -import org.springframework.context.ApplicationContext; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.CompositePropertySource; @@ -63,8 +64,14 @@ public class ContextRefresher { try { StandardEnvironment environment = copyEnvironment( this.context.getEnvironment()); - capture = new SpringApplicationBuilder(Empty.class).bannerMode(Mode.OFF) - .web(false).environment(environment).run(); + SpringApplicationBuilder builder = new SpringApplicationBuilder(Empty.class) + .bannerMode(Mode.OFF).web(false).environment(environment); + // Just the listeners that affect the environment (e.g. excluding logging + // listener because it has side effects) + builder.application() + .setListeners(Arrays.asList(new BootstrapApplicationListener(), + new ConfigFileApplicationListener())); + capture = builder.run(); if (environment.getPropertySources().contains(REFRESH_ARGS_PROPERTY_SOURCE)) { environment.getPropertySources().remove(REFRESH_ARGS_PROPERTY_SOURCE); } @@ -98,17 +105,9 @@ public class ContextRefresher { } finally { ConfigurableApplicationContext closeable = capture; - while (closeable != null) { - closeable.close(); - ApplicationContext parent = closeable.getParent(); - if (parent instanceof ConfigurableApplicationContext) { - closeable = (ConfigurableApplicationContext) parent; - } - else { - closeable = null; - } - } + closeable.close(); } + } // Don't use ConfigurableEnvironment.merge() in case there are clashes with property @@ -128,7 +127,7 @@ public class ContextRefresher { map.put("spring.jmx.enabled", false); map.put("spring.main.sources", ""); capturedPropertySources - .addFirst(new MapPropertySource(REFRESH_ARGS_PROPERTY_SOURCE, map)); + .addFirst(new MapPropertySource(REFRESH_ARGS_PROPERTY_SOURCE, map)); return environment; } diff --git a/spring-cloud-context/src/main/resources/META-INF/spring.factories b/spring-cloud-context/src/main/resources/META-INF/spring.factories index e770dc28..ddf6a5b0 100644 --- a/spring-cloud-context/src/main/resources/META-INF/spring.factories +++ b/spring-cloud-context/src/main/resources/META-INF/spring.factories @@ -8,6 +8,7 @@ org.springframework.cloud.autoconfigure.LifecycleMvcEndpointAutoConfiguration # Application Listeners org.springframework.context.ApplicationListener=\ org.springframework.cloud.bootstrap.BootstrapApplicationListener,\ +org.springframework.cloud.bootstrap.LoggingSystemShutdownListener,\ org.springframework.cloud.context.restart.RestartListener # Bootstrap components