log4j和log4j2怎么动态加载配置文件
应用场景与问题
当项目在运行时,我们如果需要修改log4j 1.X或者log4j2的配置文件,一般来说我们是不能直接将项目停止运行再来修改文件重新部署的。于是就有这样一个问题:如何在不停止当前项目的运行的情况下,让系统能够自动地监控配置文件的修改状况,从而实现动态加载配置文件的功能?而log4j 1.X和log4j2的差别略大,各自应该怎么实现这个功能?
log4j 1.X动态加载配置文件
log4j 1.X提供了动态加载配置文件的方法:
DOMConfigurator.configureAndWatch()
PropertyConfigurator.onfigureAndWatch()
DOMConfigurator对应的是xml配置文件,PropertyConfigurator对应的是properties配置文件。这两个类都有configureAndWatch这个方法,该方法有个重载方法,如下:
configureAndWatch(String configFilename)
configureAndWatch(String configFilename, long delay)
configureAndWatch方法用来监控配置文件是否被改动,监控的时间间隔是delay参数来决定,如果不传入该参数则使用默认的时间间隔1分钟(60000L)。configureAndWatch(String configFilename)实际上还是调用的configureAndWatch(String configFilename, long delay)。
log4j2动态加载配置文件
和log4j 1.X比起来,log4j2的动态加载配置很简单就能实现,不需要另外在代码中调用api,方法如下:
<configuration monitorInterval="30">
...
</configuration>
在log4j2.xml配置文件中的configuration节点添加monitorInterval的值,单位是秒,如果配置的值大于0,则会按照时间间隔来自动扫描配置文件是否被修改,并在修改后重新加载最新的配置文件。如果不配置该值,默认为0,即不扫描配置文件是否被修改。
Log4j 1.X动态加载配置文件的底层实现原理
DOMConfigurator#configureAndWatch源码解析
org.apache.log4j.xml.DOMConfigurator#configureAndWatch源码如下:
static public void configureAndWatch(String configFilename, long delay) {
XMLWatchdog xdog = new XMLWatchdog(configFilename);
xdog.setDelay(delay);
xdog.start();
}
这里new了一个XMLWatchdog对象,接着设置了delay参数,最后调用了start()方法。
watchdog是看门狗、检查者的意思,XMLWatchdog继承了FileWatchdog这个类,在XMLWatchdog中仅仅重写了doOnChange方法:
public void doOnChange() {
new DOMConfigurator().doConfigure(filename, LogManager.getLoggerRepository());
}
从方法名就可以看出来,如果XMLWatchdog监控到配置文件被改动了,就会调用这个doOnChange方法,用来重新加载配置文件。那么它又是怎么知道配置文件被改动过了呢?接着看其父类FileWatchdog的源码:
public abstract class FileWatchdog extends Thread {
/**
The default delay between every file modification check, set to 60
seconds. */
static final public long DEFAULT_DELAY = 60000;
/**
The name of the file to observe for changes.
*/
protected String filename;
/**
The delay to observe between every check. By default set {@link
#DEFAULT_DELAY}. */
protected long delay = DEFAULT_DELAY;
File file;
long lastModif = 0;
boolean warnedAlready = false;
boolean interrupted = false;
protected FileWatchdog(String filename) {
super("FileWatchdog");
this.filename = filename;
file = new File(filename);
setDaemon(true);
checkAndConfigure();
}
/**
Set the delay to observe between each check of the file changes.
*/
public void setDelay(long delay) {
this.delay = delay;
}
abstract protected void doOnChange();
protected void checkAndConfigure() {
boolean fileExists;
try {
fileExists = file.exists();
} catch(SecurityException e) {
LogLog.warn("Was not allowed to read check file existance, file:["+
filename+"].");
interrupted = true; // there is no point in continuing
return;
}
if(fileExists) {
long l = file.lastModified(); // this can also throw a SecurityException
if(l > lastModif) { // however, if we reached this point this
lastModif = l; // is very unlikely.
doOnChange();
warnedAlready = false;
}
} else {
if(!warnedAlready) {
LogLog.debug("["+filename+"] does not exist.");
warnedAlready = true;
}
}
}
public void run() {
while(!interrupted) {
try {
Thread.sleep(delay);
} catch(InterruptedException e) {
// no interruption expected
}
checkAndConfigure();
}
}
}
可以看到,FileWatchdog继承了Thread类,类里定义了几个成员变量,比如默认的监控时间间隔等。而在该类的构造方法中可以看到,首先该线程类将名字设定成FileWatchdog,接着根据传入的配置文件的路径new了一个File对象,然后该线程类又设置成了守护线程(daemon thread),最后调用了checkAndConfigure()。
在checkAndConfigure()中,则是对new出来的配置文件File对象进行检查是否存在该文件,若不存在该文件则会设置成员变量的值,这样就不会去监控不存在的配置文件了。如果该配置文件存在,则通过lastModified()来获取文件的最后更新时间,和上次的更新时间作对比,如果比上次更新时间大则会调用doOnChange()来重新加载配置文件。
而在FileWatchdog的run方法中,则是在无限循环中先让线程睡眠设置好的监控时间间隔,然后调用checkAndConfigure()。
总结
可以看出,在log4j 1.X的DOMConfigurator中,是通过创建一个守护线程来不停地扫描配置文件的最后更新时间,并和上次的更新时间进行对比,如果最后更新时间大于上次更新时间则会重新加载配置文件。
PropertyConfigurator#configureAndWatch源码解析
PropertyConfigurator的configureAndWatch()其实和DOMConfigurator差不多,区别是PropertyConfigurator在方法里new了一个PropertyWatchdog对象,PropertyWatchdog和XMLWatchdog一样继承了FileWatchdog,一样重写了doOnChange()方法。只是PropertyWatchdog是通过new PropertyConfigurator().doConfigure()来加载配置文件的。
从源码实现来看,无论是使用xml配置文件,还是使用properties配置文件,其动态加载配置文件的底层实现是基本一样的。可以通过解析配置文件的文件后缀来判断是xml还是properties文件,然后调用对应的方法即可,大概的思路如下:
boolean flag = true;
boolean isXml = StringUtils.equalsIgnoreCase("xml", StringUtils.substringAfterLast(filepath, "."));
ling delay = 30000;
if (isXml) {
if (flag) {
DOMConfigurator.configureAndWatch(filepath, delay);
} else {
DOMConfigurator.configure(filepath);
}
} else {
if (flag) {
PropertyConfigurator.configureAndWatch(filepath, delay);
} else {
PropertyConfigurator.configure(filepath);
}
}
log4j2底层实现动态加载配置文件的简单解析
虽然log4j2的动态加载配置很简单,但其底层实现比起log4j 1.X却要复杂很多,使用到了很多并发包下的类,具体也不是很了解,这里简单解释下流程。
对于log4j2.xml文件,对应的是org.apache.logging.log4j.core.config.xml.XmlConfiguration这个类。如果在log4j2.xml里配置了monitorInterval,在构建XmlConfiguration时会根据该值来走一段特定的逻辑:
for (final Map.Entry<String, String> entry : attrs.entrySet()) {
final String key = entry.getKey();
final String value = getStrSubstitutor().replace(entry.getValue());
if ("status".equalsIgnoreCase(key)) {
statusConfig.withStatus(value);
} else if ("dest".equalsIgnoreCase(key)) {
statusConfig.withDestination(value);
} else if ("shutdownHook".equalsIgnoreCase(key)) {
isShutdownHookEnabled = !"disable".equalsIgnoreCase(value);
} else if ("shutdownTimeout".equalsIgnoreCase(key)) {
shutdownTimeoutMillis = Long.parseLong(value);
} else if ("verbose".equalsIgnoreCase(key)) {
statusConfig.withVerbosity(value);
} else if ("packages".equalsIgnoreCase(key)) {
pluginPackages.addAll(Arrays.asList(value.split(Patterns.COMMA_SEPARATOR)));
} else if ("name".equalsIgnoreCase(key)) {
setName(value);
} else if ("strict".equalsIgnoreCase(key)) {
strict = Boolean.parseBoolean(value);
} else if ("schema".equalsIgnoreCase(key)) {
schemaResource = value;
} else if ("monitorInterval".equalsIgnoreCase(key)) {
final int intervalSeconds = Integer.parseInt(value);
if (intervalSeconds > 0) {
getWatchManager().setIntervalSeconds(intervalSeconds);
if (configFile != null) {
final FileWatcher watcher = new ConfiguratonFileWatcher(this, listeners);
getWatchManager().watchFile(configFile, watcher);
}
}
} else if ("advertiser".equalsIgnoreCase(key)) {
createAdvertiser(value, configSource, buffer, "text/xml");
}
}
可以看到,如果monitorInterval的值大于0,则会拿到WatchManager并设置扫描配置文件的时间间隔,如果配置文件存在,则会new一个ConfiguratonFileWatcher对象,并将配置文件和该对象一起传递给WatchManager的watchFile方法。这两个方法的底层实现很绕,比起log4j 1.X要复杂得多,不容易看懂。不过最终实现的效果还是一样的,依然会开启一个守护线程来监控配置文件是否被改动。
区别在于,log4j2使用线程池来启动线程,在WatchManager#start()里实现的:
@Override
public void start() {
super.start();
if (intervalSeconds > 0) {
future = scheduler.scheduleWithFixedDelay(new WatchRunnable(), intervalSeconds, intervalSeconds,
TimeUnit.SECONDS);
}
}
而该方法则是在启动配置文件时被调用的,AbstractConfiguration#start():
/**
* Start the configuration.
*/
@Override
public void start() {
// Preserve the prior behavior of initializing during start if not initialized.
if (getState().equals(State.INITIALIZING)) {
initialize();
}
LOGGER.debug("Starting configuration {}", this);
this.setStarting();
if (watchManager.getIntervalSeconds() > 0) {
watchManager.start();
}
...
}
这里只是简单解析了下主要的流程,具体的实现细节目前还看不太懂,有兴趣的可以自己去看看log4j2的源码。另外我在官方文档里看到说monitorInterval的最小值是5,但是在源码里也没看到这个,我觉得只要配置值大于0应该就是可以的。有不对之处,欢迎指出。
这是官方原文:
Automatic Reconfiguration
When configured from a File, Log4j has the ability to automatically detect changes to the configuration file and reconfigure itself. If the monitorInterval attribute is specified on the configuration element and is set to a non-zero value then the file will be checked the next time a log event is evaluated and/or logged and the monitorInterval has elapsed since the last check. The example below shows how to configure the attribute so that the configuration file will be checked for changes only after at least 30 seconds have elapsed. The minimum interval is 5 seconds.
参考链接
log4j和log4j2怎么动态加载配置文件的更多相关文章
- java动态加载配置文件(申明:来源于网络)
java动态加载配置文件 地址:http://blog.csdn.net/longvs/article/details/9361449
- NGINX的启停命令、以及动态加载配置文件的命令
-- 启动(不推荐):在nginx目录下有一个sbin目录,sbin目录下有一个nginx可执行程序../nginx -- 启动(指定配置文件,推荐)/usr/local/nginx/sbin/ngi ...
- java动态加载配置文件
最近项目中需要做定时任务,即定时数据库的备份.定时时间用户可以在界面中配置,要求配置修改好立即生效. 想不到什么好办法.下面是一种实现思路 把用户配置的时间存到properties配置文件中,定时任务 ...
- springmvc 动态加载配置文件
<import resource="classpath:config/spring-profile-properties.xml" /> <context:pro ...
- Nginx自动加载配置文件方案
nginx自动加载配置文件方案一.nginx+consul+consul-template实现过程:consul作为服务发现软件,consul-template作为nginx配置文件的模板,consu ...
- 关于设置SQLPLUS提示符样式的方法----登陆配置文件,动态加载提示符
工作中用到 sqlplus mdsoss/mdsoss, 所以来了解一下sqlplus (C shell .cshrc文件里中alisa) 关于设置SQLPLUS提示符样式的方法 12638阅读 1评 ...
- js动态加载js css文件,可以配置文件后辍,防止浏览器缓存
js的引用,在浏览器,或微信上访问经常会遇到文件改了,但就是没有更新的问题,使用此函数可以轻松解决缓存问题只需要把js的引用方式改为使用此函数加载即可 源码如下: /** * js动态加载js css ...
- spring动态加载(刷新)配置文件 [复制链接]
待验证 在程序开发时,通常会经常修改spring的配置文件,不得不重启tomcat来加载spring配,费时费力.如果能在不重启tomcat的情况下,手动动态加载spring 配置文件,动态重启读取s ...
- 页面加载异常 清除浏览器静态文件 js css 缓存 js动态加载js css文件,可以配置文件后辍,防止浏览器缓存
js清除浏览器缓存的几种方法 - 兔老霸夏 - 博客园 https://www.cnblogs.com/Mr-Rocker/p/6031096.html js清除浏览器缓存的几种方法 一.CSS和 ...
随机推荐
- socket基本使用
UDP发送和接收 MainRecv.cpp #include <iostream> #include <WinSock2.h> #include <sstream> ...
- javase练习题
偶然看到一份javase的练习题,mark一下,以后练习下 [程序1] 题目:古典问题:有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个 ...
- System.exit(0);和finish();,push原理
今天师姐问我安卓后台的问题,想起几年前做进制转换的时候特意研究了一下怎么才能「不驻留内存地退出」.虽然Android不推荐用户手动关闭进程,但是在那个内存捉襟见肘的年代,不得不考虑内存. 首先直接按b ...
- Java标准输入
Java: import java.util.*; public class Main{ public static void main(String args[]){ Scanner cin = n ...
- 最新版ADT(Build: v22.6.2)总是引用appcompat_v7的问题
昨天在ADT Manager里更新了一些组件,结果ADT不支持.索性直接下载了最新的ADT.但是发现无论创建什么类型的应用(无论支持的最低API是多少,或者是不是用模板),都会在创建应用的同时创建一个 ...
- BZOJ4936:match (不错的分治)
给你一个由小写字母组成的字符串s,要你构造一个字典序最小的(认为左括号的字典序比右括号小)合法的括号 序列与这个字符串匹配,字符串和括号序列匹配定义为:首先长度必须相等,其次对于一对匹配的左括号和右括 ...
- 「UVA10298」 Power Strings(KMP
题目描述 PDF 输入输出格式 输入格式: 输出格式: 输入输出样例 输入样例#1: 复制 abcd aaaa ababab . 输出样例#1: 复制 1 4 3 题解 Luogu的题解 这里是对目前 ...
- bzoj1013高斯消元
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1013 似乎是很明显的高斯消元: 第一次写高斯消元. 代码如下: #include<io ...
- HDU2896(AC自动机入门题)
病毒侵袭 Time Limit:1000MS Memory Limit:32768KB Description 当太阳的光辉逐渐被月亮遮蔽,世界失去了光明,大地迎来最黑暗的时刻....在这 ...
- eclipse编辑窗口不见了(打开左边的java、xml文件,中间不会显示代码)
转自:https://blog.csdn.net/u012062810/article/details/46729779?utm_source=blogxgwz4 1. windows-->re ...