背景
有時候在服務運行中,需要查看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