Log4j提供了一个路径替换(subst-mechanism)功能,可以实现运行期替换log4j配置文件里${xx}的标记为外部指定的值。
例如:log4j.xml可以指定file的值为"${loggingRoot}/project.log",此时就可以利用log4j的subst功能,把${loggingRoot}替换成具体的路径,如d:/app/logs
<appender name="PROJECT" class="log4j.DailyRollingFileAppender"> <param name="file" value="${loggingRoot}/project.log"/> <param name="append" value="true"/> <param name="encoding" value="GBK"/> <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>
那怎么在自己的应用里实现呢? 我们看看org.apache.log4j.xml.DOMConfigurator的类图,发现它提供了一个protected String subst()方法,protected是一个很好的暗示,它暗示着我们可以重载此方法,用自己的props。看看自己写的类吧:
log4j的DOMConfigurator的subst实现
/**
* Substitutes property value for any references in expression.
*
* @param value value from configuration file, may contain
* literal text, property references or both
* @return evaluated expression, may still contain expressions
* if unable to expand.
*/
protected String subst(final String value) {
try {
//props是DOMConfigurator的一个成员变量
return OptionConverter.substVars(value, props);
} catch (IllegalArgumentException e) {
LogLog.warn("Could not perform variable substitution.", e);
return value;
}
}
public class DOMConfigurator extends org.apache.log4j.xml.DOMConfigurator { private Properties props; /** * 创建新对象。 */ public DOMConfigurator() { this(null); } /** * 创建新对象。 * * @param props 可在配置文件中被引用的属性 */ public DOMConfigurator(Properties props) { this.props = props; } /** * 使用XML文件配置log4j。 * * @param filename 配置文件名 * @param props 可在配置文件中被引用的属性 */ public static void configure(String filename, Properties props) { new DOMConfigurator(props).doConfigure(filename, LogManager.getLoggerRepository()); } /** * 设置属性,这些属性可以在配置文件中被引用。 * * @param props 属性 */ public void setProperties(Properties props) { this.props = props; } /** * 替换字符串值,将其中的${xxx}替换成具体的值。 * * @param value 要替换的值 * * @return 替换后的值 */ protected String subst(String value) { try { return OptionConverter.substVars(value, props); } catch (IllegalArgumentException e) { LogLog.warn("Could not perform variable substitution.", e); return value; } } }
忽然某一天,你发现这个功能不能用了,想了半天,哪也没改吗? 没办法,Debug吧,找问题根源的利器,最后你发现别人升级了log4j的版本,log4j 1.2.15。 那就对了,这是log4j-1.2.15的一个bug,它导致了路径替换功能的失效,因为1.2.15版本的DOMConfigurator又提供了一套static的方法实现,如下面为它的代码:
protected void setParameter(Element elem, PropertySetter propSetter) { //调用static方法,这里的props变成了log4j内部DOMConfigurator的成员变量,它现在是null setParameter(elem, propSetter, props); } public static void setParameter(final Element elem, final PropertySetter propSetter, final Properties props) { //传递props到static subst,到此路径替换就失效了 String name = subst(elem.getAttribute("name"), props); String value = (elem.getAttribute("value")); value = subst(OptionConverter.convertSpecialChars(value), props); propSetter.setProperty(name, value); } public static String subst(final String value, final Properties props) { try { return OptionConverter.substVars(value, props); } catch (IllegalArgumentException e) { LogLog.warn("Could not perform variable substitution.", e); return value; } }
在1.2.16后的版本已经解决了此问题,官网还有此bug的记录:https://issues.apache.org/bugzilla/show_bug.cgi?id=43325
Log4j的路径替换很好用,有点类似它内置的MDC功能,从而帮助我们分离开发、生产环境的配置。
----以下无内容----