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.
This commit is contained in:
Dave Syer
2016-09-16 13:44:48 +01:00
parent 68c7fcf7d8
commit d4fd2b7aa5
4 changed files with 76 additions and 27 deletions

View File

@@ -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(

View File

@@ -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<ApplicationEnvironmentPreparedEvent>, 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;
}
}

View File

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

View File

@@ -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