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和 ...
随机推荐
- UI标签库专题四:JEECG智能开发平台 Upload(上传标签)
1. Upload(上传标签) 1.1. 參数 属性名 类型 描写叙述 是否必须 默认值 id string 上传控件唯一标示 是 null name string 控件name 是 null ...
- STM32 ~ ili9341 横屏驱动代码
void ili9341_Initializtion(void) { u16 i; RCC->APB2ENR|=<<; //使能PORTB时钟 GPIOB->CRH&= ...
- 数组/字符串/ Math / 方法示例
数组 Array concat 数组的合并 <script> var north = ["北京","上海","深圳"]; va ...
- leetcode 750. Number Of Corner Rectangles
Given a grid where each entry is only 0 or 1, find the number of corner rectangles. A corner rectang ...
- hdu 1391 Number Steps(规律)
题意:找规律 思路:找规律 #include<iostream> #include<stdio.h> using namespace std; int main(){ int ...
- 烂笔头——JAVA.String
人们说 Sting+ 的效率很低 String+的编译步骤是: new StringBuilder() new String.valueof() StringBuilder.<init> ...
- 机器学习: Canonical Correlation Analysis 典型相关分析
Canonical Correlation Analysis(CCA)典型相关分析也是一种常用的降维算法.我们知道,PCA(Principal Component Analysis) 主分量分析将数据 ...
- BeginPaint/EndPaint(CPaintDC)与GetDC(CClientDC)的区别
在OnPaint函数中,用CClientDC dc(this)代替CPaintDC(this)后,界面不断闪烁. 说明:CClientDC是对GetDC的使用封装, CPaintDC是对BeginPa ...
- oracle下 启动subversion命令 及 oracle相关服务启动备忘
linux shell下 svnserve - d -r + 目录 例如:svnserve -d -r /svn 启动 svn服务. 访问svn://192.168.0.120/kjcg 测试. ...
- Redis简介,安装和配置,停止,卸载(图解方式)
Redis是一个Key-value的数据结构存储系统,可以已数据库的形式,缓存系统,消息处理器使用,它支持的存储类型很多,例如,String(字符串),list(列表),set(集合),zset(有序 ...