背景
有时候在服务运行中,需要查看debug 级别的日志,但是不方便重新打包,或者到部署的服务器去改日志配置文件,那么就需要通过接口,或者配置中心的方式动态修改。
实现
核心代码主要是借助 org.springframework.boot.logging.LoggingSystem
以下是一个根据日志名前缀匹配,动态修改日志级别接口,可以把该接口暴露为http 接口,一般的日志名,都是用以下方式设置:
LoggerFactory.getLogger(LogLevelConfig.class)
这样,就可以用输入包路径,或者具体类名,来动态修改对应的日志级别了
@Service
public class LogLevelConfig {
private static final Logger logger = LoggerFactory.getLogger(LogLevelConfig.class);
@Autowired
private LoggingSystem loggingSystem;
public void refreshLoggingLevels(String logNamePrefix,String strLevel ) {
//info,error,debug
LogLevel level = LogLevel.valueOf(strLevel.toUpperCase());
for(LoggerConfiguration loggerCfg:loggingSystem.getLoggerConfigurations())
{
if(loggerCfg.getName().startsWith(logNamePrefix))
{
//这里调用一次就更新一次,不过没有好的接口,好在这个操作不多
loggingSystem.setLogLevel(loggerCfg.getName(), level);
}
}
}
}
经过测试,修改是立即生效的。因为spring boot的 log 组件配合 slf4j 使用, 所以可以适配不同日志框架,logback和log4j都可以使用该方式动态修改日志级别。
如果配合配置中心使用,会更符合生产中实际情况,比如接入 apollo配置中心
public class LogLevelConfig {
private static final Logger logger = LoggerFactory.getLogger(LogLevelConfig.class);
private static final String LOGGER_TAG = "logging.level.";
@Autowired
private LoggingSystem loggingSystem;
@ApolloConfig
private Config config;
//监听自己服务和一个公共基础 name space
@ApolloConfigChangeListener({ConfigConsts.NAMESPACE_APPLICATION, SvcConstant.baseMsvcNameSpace})
private void configChangeListter(ConfigChangeEvent changeEvent) {
refreshLoggingLevels(changeEvent.changedKeys());
}
@PostConstruct
public void intLevel()
{
refreshLoggingLevels(null);
}
private void refreshLoggingLevels(Set<String> keyNames ) {
if(keyNames ==null)
{
keyNames = config.getPropertyNames();
}
for (String key : keyNames) {
if (StringUtils.containsIgnoreCase(key, LOGGER_TAG)) {
String strLevel = config.getProperty(key, "info");
LogLevel level = LogLevel.valueOf(strLevel.toUpperCase());
key = key.replace(LOGGER_TAG, "");
//前缀匹配
if(key.endsWith("*"))
{
key = key.replace("*","");
for(LoggerConfiguration loggerCfg:loggingSystem.getLoggerConfigurations())
{
if(loggerCfg.getName().startsWith(key))
{
//这里调用一次就更新一次,不过没有好的接口,好在这个操作不多
loggingSystem.setLogLevel(loggerCfg.getName(), level);
}
}
}
else//精确匹配
{
loggingSystem.setLogLevel(key, level);
}
logger.info("{}:{}", key, strLevel);
}
}
}
}
在有微服务平台的开发场景下,配合配置中心,可以发挥最大效果,配置中心可以是Apollo或者nacos