Logback scan 原理分析

Logback scan 使用

Logback 可以在运行时动态加载配置文件,默认是一分钟加载一次(配置文件有修改才会进行加载)。用户可以更具实际情况自己修改加载的时间。

1
2
3
<configuration scan="true" scanPeriod="30 seconds" > 
...
</configuration>

scan=”true” 启用运行时加载功能,scanPeriod=”30 seconds” 设置定时加载周期。

Logback scan 原理解析

如果启用 scan 功能,Logback 会创建一个 ReconfigureOnChangeTask,在单独的线程中执行。

参考 https://logback.qos.ch/manual/configuration.html

ReconfigureOnChangeTask 源码分析

ReconfigureOnChangeTask 本身是一个Runable接口, 定时执行 run 方法。

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
@Override
public void run() {
fireEnteredRunMethod();
ConfigurationWatchList configurationWatchList = ConfigurationWatchListUtil.getConfigurationWatchList(context);
if (configurationWatchList == null) {
addWarn("Empty ConfigurationWatchList in context");
return;
}

List<File> filesToWatch = configurationWatchList.getCopyOfFileWatchList();
if (filesToWatch == null || filesToWatch.isEmpty()) {
addInfo("Empty watch file list. Disabling ");
return;
}
// 检查配置文件是否发生变化,如果变化进行配置更新
if (!configurationWatchList.changeDetected()) {
return;
}
// 触发配置变更事件
fireChangeDetected();
URL mainConfigurationURL = configurationWatchList.getMainURL();

addInfo(DETECTED_CHANGE_IN_CONFIGURATION_FILES);
addInfo(CoreConstants.RESET_MSG_PREFIX + "named [" + context.getName() + "]");

LoggerContext lc = (LoggerContext) context;
if (mainConfigurationURL.toString().endsWith("xml")) {
// 重新进行配置
performXMLConfiguration(lc, mainConfigurationURL);
} else if (mainConfigurationURL.toString().endsWith("groovy")) {
if (EnvUtil.isGroovyAvailable()) {
lc.reset();
// avoid directly referring to GafferConfigurator so as to avoid
// loading groovy.lang.GroovyObject . See also http://jira.qos.ch/browse/LBCLASSIC-214
// GafferUtil.runGafferConfiguratorOn(lc, this, mainConfigurationURL);
addError("Groovy configuration disabled due to Java 9 compilation issues.");

} else {
addError("Groovy classes are not available on the class path. ABORTING INITIALIZATION.");
}
}
// 触发 重新配置完成 事件
fireDoneReconfiguring();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private void performXMLConfiguration(LoggerContext lc, URL mainConfigurationURL) {
JoranConfigurator jc = new JoranConfigurator();
jc.setContext(context);
StatusUtil statusUtil = new StatusUtil(context);
List<SaxEvent> eventList = jc.recallSafeConfiguration();

URL mainURL = ConfigurationWatchListUtil.getMainWatchURL(context);
lc.reset();
long threshold = System.currentTimeMillis();
try {
jc.doConfigure(mainConfigurationURL);
if (statusUtil.hasXMLParsingErrors(threshold)) {
// 如果配置失败回滚到上次配置
fallbackConfiguration(lc, eventList, mainURL);
}
} catch (JoranException e) {
fallbackConfiguration(lc, eventList, mainURL);
}
}

ReconfigureOnChangeTask 依赖 ConfigurationWatchListUtil 判断配置文件是否发生变化, ConfigurationWatchList 执行文件监控

ConfigurationWatchList 源码分析

1
2
3
4
5
6
7
8
// 添加监控的文件,保存文件最后的修改时间
private void addAsFileToWatch(URL url) {
File file = convertToFile(url);
if (file != null) {
fileWatchList.add(file);
lastModifiedList.add(file.lastModified());
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
// 判断 文件是否变化,依据当前的文件的最后修改时间和上次保存的最后修改时间。
public boolean changeDetected() {
int len = fileWatchList.size();
for (int i = 0; i < len; i++) {
long lastModified = lastModifiedList.get(i);
File file = fileWatchList.get(i);
if (lastModified != file.lastModified()) {
return true;
}
}
return false;
// return (lastModified != fileToScan.lastModified() && lastModified != SENTINEL);
}