动态调整日志级别

参考资料:
日志级别动态调整——小工具解决大问题
Java日志框架:slf4j作用及其实现原理

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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.impl.StaticLoggerBinder;

/**
* 动态修改日志级别
* <p/>
* <a href="https://tech.meituan.com/2017/02/17/change-log-level.html">日志级别动态调整——小工具解决大问题</a>
*
* @author <a href=mailto:ktyi@iflytek.com>伊开堂</a>
* @date 2020/5/8
*/
public class LoggerLevelModifier {
private static final Logger logger = LoggerFactory.getLogger(LoggerLevelModifier.class);

private static LogFrameworkType logFrameworkType;
private static ConcurrentMap<String, Object> loggerMap = new ConcurrentHashMap<>();

public static void init() {
// slf4j找org/slf4j/impl/StaticLoggerBinder.class类, 有些是默认实现(logback), 有些是桥接包(log4j/2)

String type = StaticLoggerBinder.getSingleton().getLoggerFactoryClassStr();
if (LogConstant.LOG4J_LOGGER_FACTORY.equals(type)) {
logFrameworkType = LogFrameworkType.LOG4J;
Enumeration enumeration = org.apache.log4j.LogManager.getCurrentLoggers();
while (enumeration.hasMoreElements()) {
org.apache.log4j.Logger logger = (org.apache.log4j.Logger) enumeration.nextElement();
if (logger.getLevel() != null) {
loggerMap.put(logger.getName(), logger);
}
}
org.apache.log4j.Logger rootLogger = org.apache.log4j.LogManager.getRootLogger();
loggerMap.put(rootLogger.getName(), rootLogger);
}
else if (LogConstant.LOGBACK_LOGGER_FACTORY.equals(type)) {
logFrameworkType = LogFrameworkType.LOGBACK;
ch.qos.logback.classic.LoggerContext loggerContext = (ch.qos.logback.classic.LoggerContext) LoggerFactory.getILoggerFactory();
for (ch.qos.logback.classic.Logger logger : loggerContext.getLoggerList()) {
if (logger.getLevel() != null) {
loggerMap.put(logger.getName(), logger);
}
}
ch.qos.logback.classic.Logger rootLogger = (ch.qos.logback.classic.Logger) LoggerFactory
.getLogger(Logger.ROOT_LOGGER_NAME);
loggerMap.put(rootLogger.getName(), rootLogger);
}
else if (LogConstant.LOG4J2_LOGGER_FACTORY.equals(type)) {
logFrameworkType = LogFrameworkType.LOG4J2;
org.apache.logging.log4j.core.LoggerContext loggerContext =
(org.apache.logging.log4j.core.LoggerContext) org.apache.logging.log4j.LogManager
.getContext(false);
Map<String, org.apache.logging.log4j.core.config.LoggerConfig> map = loggerContext.getConfiguration().getLoggers();
for (org.apache.logging.log4j.core.config.LoggerConfig loggerConfig : map.values()) {
String key = loggerConfig.getName();
if (isBlank(key)) {
key = "root";
}
loggerMap.put(key, loggerConfig);
}
}
else {
logFrameworkType = LogFrameworkType.UNKNOWN;
logger.error("Log框架无法识别: type={}", type);
}
}

private static boolean isBlank(CharSequence cs) {
int strLen;
if (cs != null && (strLen = cs.length()) != 0) {
for (int i = 0; i < strLen; ++i) {
if (!Character.isWhitespace(cs.charAt(i))) {
return false;
}
}

return true;
}
else {
return true;
}
}

public static Map<String, Object> getLoggerList() {
Map<String, Object> result = new HashMap<>();
result.put("logFramework", logFrameworkType);

List<Map<String, String>> loggerList = new ArrayList<>();
for (ConcurrentMap.Entry<String, Object> entry : loggerMap.entrySet()) {
Map<String, String> loggerInfo = new HashMap<>();
loggerInfo.put("loggerName", entry.getKey());
if (logFrameworkType == LogFrameworkType.LOG4J) {
org.apache.log4j.Logger targetLogger = (org.apache.log4j.Logger) entry.getValue();
loggerInfo.put("logLevel", targetLogger.getLevel().toString());
}
else if (logFrameworkType == LogFrameworkType.LOGBACK) {
ch.qos.logback.classic.Logger targetLogger = (ch.qos.logback.classic.Logger) entry.getValue();
loggerInfo.put("logLevel", targetLogger.getLevel().toString());
}
else if (logFrameworkType == LogFrameworkType.LOG4J2) {
org.apache.logging.log4j.core.config.LoggerConfig targetLogger = (org.apache.logging.log4j.core.config.LoggerConfig) entry.getValue();
loggerInfo.put("logLevel", targetLogger.getLevel().toString());
}
else {
loggerInfo.put("logLevel", "Logger的类型未知,无法处理!");
}
loggerList.add(loggerInfo);
}
result.put("loggerList", loggerList);

logger.info("getLoggerList: result={}", result);
return result;
}

public static String setLogLevel(String loggerName, String level) {
Object logger = loggerMap.get(loggerName);
if (logger == null) {
throw new RuntimeException("需要修改日志级别的Logger不存在");
}
if (logFrameworkType == LogFrameworkType.LOG4J) {
org.apache.log4j.Logger targetLogger = (org.apache.log4j.Logger) logger;
org.apache.log4j.Level targetLevel = org.apache.log4j.Level.toLevel(level);
targetLogger.setLevel(targetLevel);
}
else if (logFrameworkType == LogFrameworkType.LOGBACK) {
ch.qos.logback.classic.Logger targetLogger = (ch.qos.logback.classic.Logger) logger;
ch.qos.logback.classic.Level targetLevel = ch.qos.logback.classic.Level.toLevel(level);
targetLogger.setLevel(targetLevel);
}
else if (logFrameworkType == LogFrameworkType.LOG4J2) {
org.apache.logging.log4j.core.config.LoggerConfig loggerConfig =
(org.apache.logging.log4j.core.config.LoggerConfig) logger;
org.apache.logging.log4j.Level targetLevel = org.apache.logging.log4j.Level.toLevel(level);
loggerConfig.setLevel(targetLevel);
org.apache.logging.log4j.core.LoggerContext ctx =
(org.apache.logging.log4j.core.LoggerContext) org.apache.logging.log4j.LogManager
.getContext(false);
ctx.updateLoggers(); // This causes all Loggers to refetch information from their LoggerConfig.
}
else {
throw new RuntimeException("Logger的类型未知,无法处理!");
}
return "success";
}

private class LogConstant {
public static final String LOGBACK_LOGGER_FACTORY = "ch.qos.logback.classic.util.ContextSelectorStaticBinder";
public static final String LOG4J2_LOGGER_FACTORY = "org.apache.logging.slf4j.Log4jLoggerFactory";
public static final String LOG4J_LOGGER_FACTORY = "org.slf4j.impl.Log4jLoggerFactory";
}

private enum LogFrameworkType {
LOG4J,
LOGBACK,
LOG4J2,
UNKNOWN
}
}