目標:讓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