目标:让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