目前常用的实现动态配置日志级别的应该非SpringBoot的spring-boot-starter-actuator莫属了。不过通过spring-boot-starter-actuator配置的日志级别,服务一旦重启就会恢复原状。且只能通过访问指定的接口来修改单个实例的日志级别(SpringBootAdmin也是一样,只能修改单个实例的)。如果是想修改某个服务所有实例的日志级别,只能修改配置文件,然后重启服务,可以说局限性稍微大点儿。
由于重启服务太费劲儿,所以想到了利用Apollo配置中心来动态修改日志级别。
实现的大体思路是通过Apollo的监听机制,结合Spring的事件监听,来刷新日志级别。
具体代码如下:
LogLevelRefreshEvent:自定义Spring事件。
- import lombok.ToString;
- import org.springframework.context.ApplicationEvent;
- /**
- * @author lifengdi
- * @date 2021/12/24 11:29
- */
- @ToString
- public class LogLevelRefreshEvent extends ApplicationEvent {
- /**
- * key
- */
- private final String key;
- /**
- * 旧值
- */
- private final Object oldValue;
- /**
- * 新值
- */
- private final Object newValue;
- public LogLevelRefreshEvent(String key, Object oldValue, Object newValue) {
- super(key);
- this.key = key;
- this.oldValue = oldValue;
- this.newValue = newValue;
- }
- public String getKey() {
- return key;
- }
- public Object getOldValue() {
- return oldValue;
- }
- public Object getNewValue() {
- return newValue;
- }
- }
LogConfig:日志级别配置文件,用于获取Apollo配置以及转换日志级别。
- import com.ctrip.framework.apollo.spring.annotation.ApolloJsonValue;
- import lombok.Getter;
- import org.springframework.boot.logging.LogLevel;
- import org.springframework.context.annotation.Configuration;
- import java.util.HashMap;
- import java.util.Map;
- /**
- * @author lifengdi
- * @date 2021/12/24 14:29
- */
- @Configuration
- @Getter
- public class LogConfig {
- /**
- * 日志级别配置key
- */
- public static final String LOG_LEVEL_CONFIG_KEY = "logLevel.list";
- /**
- * 日志级别配置,格式:
- * <code>
- * {
- * "com.cowell.conveyor": "warn",
- * "com.cowell.tools": "warn"
- * }
- * </code>
- */
- @ApolloJsonValue("${logLevel.list:{}}")
- private Map<String, String> logLevelConfig;
- /**
- * 日志级别映射
- */
- private final Map<String, LogLevel> levelMap = new HashMap<String, LogLevel>() {
- {
- put("trace", LogLevel.TRACE);
- put("debug", LogLevel.DEBUG);
- put("info", LogLevel.INFO);
- put("warn", LogLevel.WARN);
- put("error", LogLevel.ERROR);
- put("fatal", LogLevel.FATAL);
- put("off", LogLevel.OFF);
- }
- };
- public Map<String, LogLevel> getLevelMap() {
- return levelMap;
- }
- /**
- * 根据名称获取日志级别,默认返回debug级别。
- * @param logLevel 级别名称
- * @return 日志级别 {@link LogLevel}
- */
- public LogLevel getFromLevelMap(String logLevel) {
- return levelMap.getOrDefault(logLevel, LogLevel.DEBUG);
- }
- }
LogLevelUtils:工具类
- import com.lifengdi.config.LogConfig;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import org.springframework.boot.logging.LoggerConfiguration;
- import org.springframework.boot.logging.LoggingSystem;
- import java.util.List;
- import java.util.Map;
- /**
- * @author lifengdi
- * @date 2021/12/27 17:44
- */
- public class LogLevelUtils {
- private static final Logger logger = LoggerFactory.getLogger(LogLevelUtils.class);
- /**
- * 动态刷新日志级别
- * @param loggingSystem LoggingSystem
- * @param logConfig LogConfig
- */
- public static void refreshLogLevel(LoggingSystem loggingSystem, LogConfig logConfig) {
- Map<String, String> logLevelConfig = logConfig.getLogLevelConfig();
- List<LoggerConfiguration> loggerConfigurations = loggingSystem.getLoggerConfigurations();
- if (!logLevelConfig.isEmpty()) {
- logLevelConfig.forEach((loggerName, value) -> {
- String logLevel = value.toLowerCase();
- for (LoggerConfiguration loggerConfiguration : loggerConfigurations) {
- if (loggerConfiguration.getName().startsWith(loggerName)) {
- loggingSystem.setLogLevel(loggerName, logConfig.getFromLevelMap(logLevel));
- logger.debug("LogLevelUtils|RefreshLogLevel|SUCCESS|loggerName:{},logLevel:{}", loggerName, logLevel);
- }
- }
- });
- }
- }
- }
日志级别刷新Listener:
- import com.lifengdi.config.LogConfig;
- import com.lifengdi.util.LogLevelUtils;
- import lombok.extern.slf4j.Slf4j;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.boot.logging.LoggingSystem;
- import org.springframework.context.ApplicationListener;
- import org.springframework.stereotype.Component;
- /**
- * 日志级别刷新Listener
- *
- * @author lifengdi
- * @date 2021/12/24 11:34
- */
- @Component
- @Slf4j
- public class LogLevelRefreshListener implements ApplicationListener<LogLevelRefreshEvent> {
- @Autowired
- private LoggingSystem loggingSystem;
- @Autowired
- private LogConfig logConfig;
- @Override
- public void onApplicationEvent(LogLevelRefreshEvent logLevelRefreshEvent) {
- if (LogConfig.LOG_LEVEL_CONFIG_KEY.equals(logLevelRefreshEvent.getKey())) {
- log.info("LogLevelRefreshListener|onApplicationEvent|refreshLogLevel|configRefreshEvent:{}", logLevelRefreshEvent);
- LogLevelUtils.refreshLogLevel(loggingSystem, logConfig);
- log.info("LogLevelRefreshListener|onApplicationEvent|refreshLogLevel|SUCCESS|configRefreshEvent:{}", logLevelRefreshEvent);
- }
- }
- }
服务启动时日志级别刷新Listener:
- import com.lifengdi.config.LogConfig;
- import com.lifengdi.util.LogLevelUtils;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.boot.context.event.ApplicationReadyEvent;
- import org.springframework.boot.logging.LoggingSystem;
- import org.springframework.context.ApplicationListener;
- import org.springframework.stereotype.Component;
- /**
- * 服务启动时日志级别刷新Listener
- *
- * @author lifengdi
- * @date 2021/12/24 13:59
- */
- @Component
- public class LogLevelApplicationReadyEventListener implements ApplicationListener<ApplicationReadyEvent> {
- @Autowired
- private LoggingSystem loggingSystem;
- @Autowired
- private LogConfig logConfig;
- @Override
- public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
- LogLevelUtils.refreshLogLevel(loggingSystem, logConfig);
- }
- }
- 增加事件发布:
- @Component
- public class ApolloAutoRefreshBean {
- private final Logger logger = LoggerFactory.getLogger(ApolloAutoRefreshBean.class);
- @Autowired
- private ApplicationContext applicationContext;
- @ApolloConfigChangeListener
- public void onChange(ConfigChangeEvent changeEvent) {
- logger.info("ApolloAutoRefreshBean|onChange|apollo触发变更|param:namespace={}", changeEvent.getNamespace());
- for (String key : changeEvent.changedKeys()) {
- ConfigChange change = changeEvent.getChange(key);
- logger.info("ApolloAutoRefreshBean|onChange|apollo数据key-value发生变更|key: {}, oldValue: {}, newValue: {}, changeType: {}",
- change.getPropertyName(), change.getOldValue(), change.getNewValue(),
- change.getChangeType());
- applicationContext.publishEvent(new LogLevelRefreshEvent(key, change.getOldValue(), change.getNewValue()));
- }
- applicationContext.publishEvent(new EnvironmentChangeEvent(changeEvent.changedKeys()));
- }
- }
配置中心配置的参数格式如下:
- {
- "com.lifengdi.util": "info",
- "com.lifengdi.tools": "warn"
- }
至此,动态配置日志级别算是搞定了。(转载自:李锋镝的博客)