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;
    }
}

  

posted @   幻真0526  阅读(1781)  评论(0编辑  收藏  举报
努力加载评论中...
点击右上角即可分享
微信分享提示