目录
前言
昨天组内有人反映说系统的日志级别在Apollo中配置了没有生效,在默认的properties文件中配置了会生效,这就有点意思了。在前面的文章中我们分析过,Apollo属性的优先级是高于properties文件,如果二者同时存在,应该以Apollo中的属性为准,那么最有可能的是在该属性作用的位置,Apollo属性还没有加载进来,默认的properties文件已经加载。
一、日志的自动配置过程
springboot实现了日志的自动装配,在factory文件中引入了LoggingApplicationListener和ClasspathLoggingApplicationListener两个类,主要的配置逻辑在LoggingApplicationListener中。
public class LoggingApplicationListener implements GenericApplicationListener {
private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
if (this.loggingSystem == null) {
this.loggingSystem = LoggingSystem.get(event.getSpringApplication().getClassLoader());
}
initialize(event.getEnvironment(), event.getSpringApplication().getClassLoader());
}
}
protected void initialize(ConfigurableEnvironment environment, ClassLoader classLoader) {
initializeSystem(environment, this.loggingSystem, this.logFile);
//初始化最终的日志级别
initializeFinalLoggingLevels(environment, this.loggingSystem);
registerShutdownHookIfNecessary(environment, this.loggingSystem);
}
private void initializeFinalLoggingLevels(ConfigurableEnvironment environment, LoggingSystem system) {
bindLoggerGroups(environment);
if (this.springBootLogging != null) {
initializeLogLevel(system, this.springBootLogging);
}
//设置级别
setLogLevels(system, environment);
}
根据日志的级别配置位置,如果Apollo正常配置,此时应该已经加载进来。
我们项目中只配置了Apollo.bootstrap.enabled,没有配置Apollo.bootstrap.eagerLoad.enabled,所以不能在postProcessEnvironment方法中初始化,最终初始化的调用过程如下
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
applyInitializers(context);
public void initialize(ConfigurableApplicationContext context) {
ConfigurableEnvironment environment = context.getEnvironment();
//环境中是否配置apollo.bootstrap.enabled=true,由于
String enabled = environment.getProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_ENABLED, "false");
if (!Boolean.valueOf(enabled)) {
logger.debug("Apollo bootstrap config is not enabled for context {}, see property: ${{}}", context, PropertySourcesConstants.APOLLO_BOOTSTRAP_ENABLED);
return;
}
logger.debug("Apollo bootstrap config is enabled for context {}", context);
initialize(environment);
}
由于配置上的问题,导致项目中会先初始化日志级别,再加载Apollo配置,导致Apollo配置的日志级别失效。所以,如果想让Apollo配置的日志级别在程序开始运行时生效,只需要配置Apollo.bootstrap.eagerLoad.enabled和Apollo.bootstrap.enabled,在日志初始化之前加载Apollo配置,但还没有实现动态修改日志级别。
二、动态修改日志级别
Apollo的配置修改之后不会影响日志级别,因为程序中没有属性对相关配置监听,配置修改之后,程序不会做出应对。所以程序的设计思路很明确,自定义一个Apollo监听器,当日志级别相关的配置修改时,手动修改程序中的日志级别,无需重启。
1.日志修改类
创建一个类能够调用loggingSystem的方法,修改日志级别。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.logging.LogLevel;
import org.springframework.boot.logging.LoggerConfiguration;
import org.springframework.boot.logging.LoggingSystem;
import org.springframework.stereotype.Component;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
@Component
public class LoggerLevelSettingService {
@Autowired
private LoggingSystem loggingSystem;
private static final Logger LOGGER = LoggerFactory.getLogger(LoggerLevelSettingService.class);
public void setRootLoggerLevel(String level,String loggerName) {
LoggerConfiguration loggerConfiguration = loggingSystem.getLoggerConfiguration(loggerName);
if (loggerConfiguration == null) {
if (LOGGER.isErrorEnabled()) {
LOGGER.error("no loggerConfiguration with loggerName " + level);
}
return;
}
if (!supportLevels().contains(level)) {
if (LOGGER.isErrorEnabled()) {
LOGGER.error("current Level is not support : " + level);
}
return;
}
if (!loggerConfiguration.getEffectiveLevel().equals(LogLevel.valueOf(level))) {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("setRootLoggerLevel success,old level is '" + loggerConfiguration.getEffectiveLevel()
+ "' , new level is '" + level + "'");
}
loggingSystem.setLogLevel(loggerName, LogLevel.valueOf(level));
}
}
private List<String> supportLevels() {
return loggingSystem.getSupportedLogLevels().stream().map(Enum::name).collect(Collectors.toList());
}
}
2.Apollo监听器
创建Apollo监听器,当相关配置发生改变时,调用修改类的方法,修改日志级别。
@Service
public class ValidateService {
private static final Logger logger = LoggerFactory.getLogger(ValidateService.class);
@Autowired
LoggerLevelSettingService loggerLevelSettingService;
@Value("${logging.level.com.ctrip.framework.apollo.demo.controller}")
private String controllerLevel;
@ApolloConfigChangeListener
private void someChangeHandler(ConfigChangeEvent changeEvent) {
if (changeEvent.isChanged("logging.level.com.ctrip.framework.apollo.demo.controller")) {
loggerLevelSettingService.setRootLoggerLevel(changeEvent.getChange("logging.level.com.ctrip.framework.apollo.demo.controller").getNewValue().toUpperCase(),
"com.ctrip.framework.apollo.demo.controller");
}
}
}
总结
本文针对Apollo配置日志级别时出现的问题进行分析,最后提出动态修改日志级别的解决方法,代码进行了部分删减,仅提供思路,尚需完善。