log4j日志打印级别动态调整
1,为什么日志打印级别要动态调整?
随着项目越来越大,访问量也越来越高,遇到问题时想要排查,可是日志一打开却刷的太快太快,不好排查问题,有的时候甚至因为短时间打印日志太多,严重影响了性能,这个时候日志的打印级别的动态调整就相当有必要了,在不重启项目的情况,不改动代码的情况下,通过Apollo动态配置就可以通过配置动态的调整日志的级别,可以精确到配置具体的类的日志打印级别。
2,动态调整的方案
大致思路为在springboot项目启动之后,读取Apollo配置文件里的配置文件,总共有两个,一个是总的日志级别,一个是单独的类的配置,然后设置总的之后再设置具体到类的自定义的,同时注册一个监听器监听两个文件的变化,一旦有变化就重新设置一遍,是不是很简单呢?
在项目中使用日志的方式请统一使用Slf4j门面模式。具体代码如下,将该类在启动时注册入spring容器就行。值得注意的是该类中的initCustomClass()方法,该方法是因为有很多类在springboot启动时没有初始化,那么也就没有注册入LoggerContext的属性中,所以是无法设置的,通过手动初始化该类的形式来初始化之后重新设置一遍。在详细的配置文件中是支持正则表达式来匹配的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 | @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; } } |
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步