目标:让log4j.xml配置文件中允许使用占位符(${key}).
使用场景:
在运行期决定一些动态的配置内容.
比如在我们项目中,希望一台物理机同一个应用跑多个实例.
因为多进程操作同一份log文件存在并发问题(打印,DailyRolling等),所以我希望配置如下:${loggingRoot}/${instance}/project.log
在运行脚本中,通过加入-Dinstance=instance1参数,来动态指定实例名.让同一份应用在不同的运行实例下,日志打印到不同的路径
Log4j分析:
我以为,Log4j天生就支持占位符的.请见:org.apache.log4j.helpers.OptionConverter.substVars(String val, Properties props)就有对占位符的处理.
org.apache.log4j.PropertyConfigurator (log4j.properties文件解析).默认就支持对占位符的处理.
org.apache.log4j.xml.DOMConfigurator挺怪异的.明明也有对占位符的处理.但是我们就是无法对其属性props进行赋值.
(当然,有可能是我误解了其props的用法--还没有完整读过他的源码)
处理方案:
继承org.apache.log4j.xml.DOMConfigurator,实现自己的DOMConfigurator.
public class PlaceHolderDOMConfigurator extends org.apache.log4j.xml.DOMConfigurator {
private Properties props;
public PlaceHolderDOMConfigurator(Properties props){
this.props = props;
}
public static void configure(String filename, Properties props) {
new PlaceHolderDOMConfigurator(props).doConfigure(filename, LogManager.getLoggerRepository());
}
//主要是覆写这个方案.传入properties对象
protected String subst(String value) {
try {
return OptionConverter.substVars(value, props);
} catch (IllegalArgumentException e) {
LogLog.warn("Could not perform variable substitution.", e);
return value;
}
}
}
测试代码:
log4j.xml片段:
<appender name="PROJECT" class="org.apache.log4j.DailyRollingFileAppender">
<param name="file" value="${loggingRoot}/${instance}/project.log"/>
<param name="append" value="false"/>
<param name="encoding" value="GB2312"/>
<param name="threshold" value="info"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d [%X{requestURIWithQueryString}] %-5p %c{2} - %m%n"/>
</layout>
</appender>
Run.java:
public static void main(String[] args) {
Properties props = new Properties();
props.setProperty("loggingRoot", "d:/tmp");
props.setProperty("instance", "instance1");
PlaceHolderDOMConfigurator.configure(LOG4J_PATH, props);
Logger rootLogger = LogManager.getRootLogger();
FileAppender fileAppender = (FileAppender) rootLogger.getAppender("PROJECT");
System.out.println(fileAppender.getFile());
}
输出结果:
d:/tmp/instance1/project.log
当然,你也可以通过在启动参数中加 -DloggingRoot=xxxx -Dinstance=yyyy动态指定内容.
特别说明:
本文:log4j版本为1.2.14
log4j 1.2.15测试不通过,原因见:
https://issues.apache.org/bugzilla/show_bug.cgi?id=43325