目標(biāo):讓log4j.xml配置文件中允許使用占位符(${key}).
使用場景:
在運(yùn)行期決定一些動(dòng)態(tài)的配置內(nèi)容.
比如在我們項(xiàng)目中,希望一臺(tái)物理機(jī)同一個(gè)應(yīng)用跑多個(gè)實(shí)例.
因?yàn)槎噙M(jìn)程操作同一份log文件存在并發(fā)問題(打印,DailyRolling等),所以我希望配置如下:${loggingRoot}/${instance}/project.log
在運(yùn)行腳本中,通過加入-Dinstance=instance1參數(shù),來動(dòng)態(tài)指定實(shí)例名.讓同一份應(yīng)用在不同的運(yùn)行實(shí)例下,日志打印到不同的路徑
Log4j分析:
我以為,Log4j天生就支持占位符的.請(qǐng)見:org.apache.log4j.helpers.OptionConverter.substVars(String val, Properties props)就有對(duì)占位符的處理.
org.apache.log4j.PropertyConfigurator (log4j.properties文件解析).默認(rèn)就支持對(duì)占位符的處理.
org.apache.log4j.xml.DOMConfigurator挺怪異的.明明也有對(duì)占位符的處理.但是我們就是無法對(duì)其屬性props進(jìn)行賦值.
(當(dāng)然,有可能是我誤解了其props的用法--還沒有完整讀過他的源碼)
處理方案:
繼承org.apache.log4j.xml.DOMConfigurator,實(shí)現(xiàn)自己的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());
}
//主要是覆寫這個(gè)方案.傳入properties對(duì)象
protected String subst(String value) {
try {
return OptionConverter.substVars(value, props);
} catch (IllegalArgumentException e) {
LogLog.warn("Could not perform variable substitution.", e);
return value;
}
}
}
測(cè)試代碼:
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());
}
輸出結(jié)果:
d:/tmp/instance1/project.log
當(dāng)然,你也可以通過在啟動(dòng)參數(shù)中加 -DloggingRoot=xxxx -Dinstance=yyyy動(dòng)態(tài)指定內(nèi)容.
特別說明:
本文:log4j版本為1.2.14
log4j 1.2.15測(cè)試不通過,原因見:
https://issues.apache.org/bugzilla/show_bug.cgi?id=43325