• log4j日志打印级别动态调整


    1,为什么日志打印级别要动态调整?

      随着项目越来越大,访问量也越来越高,遇到问题时想要排查,可是日志一打开却刷的太快太快,不好排查问题,有的时候甚至因为短时间打印日志太多,严重影响了性能,这个时候日志的打印级别的动态调整就相当有必要了,在不重启项目的情况,不改动代码的情况下,通过Apollo动态配置就可以通过配置动态的调整日志的级别,可以精确到配置具体的类的日志打印级别。

    2,动态调整的方案

      大致思路为在springboot项目启动之后,读取Apollo配置文件里的配置文件,总共有两个,一个是总的日志级别,一个是单独的类的配置,然后设置总的之后再设置具体到类的自定义的,同时注册一个监听器监听两个文件的变化,一旦有变化就重新设置一遍,是不是很简单呢?

      在项目中使用日志的方式请统一使用Slf4j门面模式。具体代码如下,将该类在启动时注册入spring容器就行。值得注意的是该类中的initCustomClass()方法,该方法是因为有很多类在springboot启动时没有初始化,那么也就没有注册入LoggerContext的属性中,所以是无法设置的,通过手动初始化该类的形式来初始化之后重新设置一遍。在详细的配置文件中是支持正则表达式来匹配的。

    @Service
    @Slf4j
    public class LoggerConfiguration implements ConfigChangeListener, ApplicationListener<ContextRefreshedEvent> {
    
        private static final String LOGGER_LEVEL = "logger_level";
    
        private static final String LOGGER_LEVEL_DETAIL = "logger_level_detail";
    
        private static final String DEFAULT_LEVEL = "error";
    
        private static final String INFO_LEVEL = "info";
    
        private Config applicationConfig;
    
        public LoggerConfiguration(Config applicationConfig) {
            this.applicationConfig = applicationConfig;
        }
    
        @Override
        public void onChange(ConfigChangeEvent changeEvent) {
            if (changeEvent.changedKeys().contains(LOGGER_LEVEL)) {
                String newValue = changeEvent.getChange(LOGGER_LEVEL).getNewValue();
                try {
                    log.info("update rootLoggerLevel {}", newValue);
                    setRootLoggerLevel(newValue);
                } catch (Exception e) {
                    log.error("loggerLevel onChange failed {}", ExceptionUtil.stacktraceToString(e));
                }
            }
            if (changeEvent.changedKeys().contains(LOGGER_LEVEL_DETAIL)) {
                String newValue = changeEvent.getChange(LOGGER_LEVEL_DETAIL).getNewValue();
                try {
                    log.info("update loggerLevel detail {}", newValue);
                    parseLoggerConfig(newValue);
                } catch (Exception e) {
                    log.error("loggerLevel detail onChange failed {}", ExceptionUtil.stacktraceToString(e));
                }
            }
        }
    
        @Override
        public void onApplicationEvent(ContextRefreshedEvent event) {
            try {
                // 初始化风控监听action配置
                String level = applicationConfig.getProperty(LOGGER_LEVEL, DEFAULT_LEVEL);
                log.info("init root loggerLevel {}", level);
                setRootLoggerLevel(level);
                // 注册配置监听
                applicationConfig.addChangeListener(this);
            } catch (Exception e) {
                log.error("loggerLevel init failed {}", ExceptionUtil.stacktraceToString(e));
            }
        }
    
        /**
         * 将未注册进日志容器的类处初始化
         *
         * @param className
         */
        private boolean initCustomClass(String className) {
            try {
                Class.forName(className);
                return true;
            } catch (Exception e) {
                log.error("init {} failed", className);
                return false;
            }
        }
    
    
        private void setRootLoggerLevel(String level) {
            try {
                Level newLevel = Level.valueOf(level);
                LoggerContext logContext = LoggerContext.getContext(false);
                Configuration configuration = logContext.getConfiguration();
                LoggerConfig loggerConfig = configuration.getRootLogger();
                loggerConfig.setLevel(newLevel);
                logContext.updateLoggers();
                //update后会覆盖定制化的
                setLoggerLevel(this.getClass().getName(), INFO_LEVEL);
                reConfig();
                log.info("update rootLoggerLevel {}", level);
            } catch (Exception e) {
                log.error("setRootLoggerLevel failed {}", ExceptionUtil.stacktraceToString(e));
            }
    
        }
    
        private void setLoggerLevel(String name, String level) {
            try {
                Level newLevel = Level.valueOf(level);
                LoggerContext logContext = LoggerContext.getContext(false);
    
    
                //是否没有匹配到
                boolean flag = false;
    
                if (logContext.hasLogger(name)) {
                    //精确匹配
                    Logger logger = logContext.getLogger(name);
                    logger.setLevel(newLevel);
                    log.info("update {} logger level {}", name, level);
                    flag = true;
                } else {
                    //正则匹配
                    Collection<Logger> loggers = logContext.getLoggers();
                    for (Logger logger : loggers) {
                        if (Pattern.matches(name, logger.getName())) {
                            logger.setLevel(newLevel);
                            log.info("update {} logger level {}", name, level);
                            flag = true;
                        }
                    }
                }
    
                //该类未注册就注册,注册失败那么也就不再继续设置
                if (!flag && initCustomClass(name)) {
                    //初始化未注册的类
                    setLoggerLevel(name, level);
                }
    
            } catch (Exception e) {
                log.error("setLoggerLevel failed {}", ExceptionUtil.stacktraceToString(e));
            }
        }
    
        private void reConfig() {
            String detail = applicationConfig.getProperty(LOGGER_LEVEL_DETAIL, "");
            if (StringUtils.isNotEmpty(detail)) {
                parseLoggerConfig(detail);
            }
        }
    
        private void parseLoggerConfig(String value) {
            Map<String, String> config = JSON.parseObject(value, Map.class);
            if (config == null) {
                return;
            }
            config.forEach((k, v) -> setLoggerLevel(k, v));
        }
    
        public void setApplicationConfig(Config applicationConfig) {
            this.applicationConfig = applicationConfig;
        }
    }
    

      

  • 相关阅读:
    随笔之过账模版
    随笔之转移日记账
    模式窗口的处理方式
    生产领料的问题
    询问对话框,缓存用户设置
    AX2009 连接外部Orcal与SQL区别
    AX2012全新的批处理方式
    AX调用.dll
    AX在query中添加自己的函数
    Java通过代理上传文件到Azure blob
  • 原文地址:https://www.cnblogs.com/huanzhen/p/13381690.html
Copyright © 2020-2023  润新知