聊聊log4j动态变量配置 - wtstengshen/blog-page GitHub Wiki

  前两天业务端微信推送了 log4j自定义日志文件和动态配置路径 的文章,在项目中还是经常用到的,今天咱们就一起扒一扒log4j是如何 实现变量动态配置以及如何在的wf中使用; ####一,log4j动态变量解析源码实现   如果去百度或者Google搜一下log4j配置${log_path}这样的变量之后,如何设置log_path变量的值,基本上有两种方式:

1.JVM添加自定义参数,例如:-Dlog_path=

2.程序里调用System.setProperty("log_path","xxx");

  先看看代码中是如何进行变量赋值,log4j的加载properties配置文件解析过程方法调用如下:

// 方法调用顺序
PropertyConfigurator.configure(Properties properties) -->
doConfigure                         --> 
parseCatsAndRenderers               -->
parseCategory                       -->
parseAppender                       --> 
PropertySetter.setProperties        --> 
OptionConverter.findAndSubst(key, properties)  -->
OptionConverter.substVars  

解析properties配置value的主要方法是OptionConverter.substVars,关键代码片段如下:

String key = val.substring(j, k);
// 1,先调用System.getProperty获取变量对应的value值
String replacement = getSystemProperty(key, null);
// 2.如果为null,再从编写的log4j配置的properties文件中获取变量的值
if(replacement == null && props != null) {
    replacement =  props.getProperty(key);
}
// 3.如果value不为null,递归处理变量依赖
if(replacement != null) {
    String recursiveReplacement = substVars(replacement, props);
    sbuf.append(recursiveReplacement);
}

  可以看到代码中做了两次变量值的获取:先从JVM系统配置的自定义参数中获取,调用的是System.getProperty方法, 如果为null,再从配置的log4j的properties文件中获取是否有该变量值的配置。 意外发现,变量值的获取还可以在properties中获取,所以log4j.properties文件变量配置还可以这样写:

log_path=/Users/pc/home
log_work_path=/work

log4j.appender.file.File=${log_path}${log_work_path}/logs/log4j.log

  总结一下,源码中对变量的赋值主要调用了System.getProperty,如果不存在调用props.getProperty, 这里的props就是加载log4j.properties文件之后转换成的Properties对象。

####二,wf框架集成   前边我们了解了log4j是如何对我们配置的变量进行赋值,所以结合wf框架聊聊具体操作: #####2.1 如何在Java代码中动态指定变量的值   如果在代码中动态指定log4j配置变量的值,应该在加载log4j配置文件之前直接调用System.setProperty设置变量值即可。

  那么问题就来了,wf框架是在什么地方加载log4j配置文件的?

  wf框架在com.bj58.wf.core.WF类的static代码块中调用initConfigFile方法,进行加载log4j的配置文件,因为wf框架的入口类 是一个Filter,所以我们设置变量有以下几种解决方案:

1,在tomcat启动脚本中添加JVM自定义参数;

2,自定义一个Servlet的ServletContextListener,在listener中设置变量的值;

3,在System.getProperty方法之后,再次调用log4j的PropertyConfigurator方法再加载一次配置文件;

  不建议采用第3种方法,因为wf框架本身设置了一些properties属性,有兴趣可以看一下wf的源码;对于第1种方案,也能解决问题, 但是不够灵活,需要更改tomcat的启动脚本;所以一般情况下建议采用第2种方案;(因wf框架入口类是Filter,所以在Filter之前执行代码 可以是实现了ServletContextListener中的代码)

#####2.2 对于自定义输出文件的日志,是否有更好的方式来编写文件的路径   项目中我们基本上是对日志路径进行一次配置以后很少更改,在配置日志路径的时候需要一些规范的,比如:我们要把日志放到自己项目 的tomcat目录下,不要出现A项目的日志放到B项目或者公共目录下。 所以可以这样写:

log4j.appender.web.File=${catalina.base}/wf/logs/xxx.log
catalina.base路径在tomcat启动的时候已经指定了路径:-Dcatalina.base=/opt/web/xxx (如:jingzhun_yingxiao)

或者

root_log_path=/opt/web/项目部署名称
log4j.appender.web.File=${root_log_path}/wf/logs/xxx.log

  如果项目中的日志文件没有限制root log file path,写的输出到哪里都有,比如:

log4j.appender.xxx.File = /opt/wf/logs/xxx/xxx.log

  如果每个人都随意输出到不同的路径下,这不叫灵活,这叫乱,所以项目中一定要规范定义好root file path,在这个目录下,可以进行 灵活输出。这样做首先有一定的运维规范,打印的日志文件都放在自己项目的tomcat路径下,相互之间没有影响,日志输出也很清晰并且很有条理, 在定时删除日志的时候,脚本也很好写,不用到处找目录。

####三,总结   我们了解了log4j源码中到底是如何对我们配置的变量进行替换的,结合我们实际的应用的wf框架,应该如何对日志文件进行合理的规划, 欢迎大家交流和拍砖;

⚠️ **GitHub.com Fallback** ⚠️