Log4j是java中非常广泛的日志记录组件,使用非常简单。在工程中加入Log4j的jar包后,再加上简单的Log4j.xml或者Log4j.properties等配置文件后就可以使用。其背后的实现原理是什么呢?假如我们的项目很大,各个配置文件的属性需要写在一个文件中,这样便于实施人员修改。那该怎么办?
先来讨论Log4j实例化原理。使用Logger logger = Logger.getLogger(Log4jTest.class)就能获得Logger的实例。进入getLogger方法(在eclipse中按住Ctrl键后左键单击方法名),可以看见Logger实例实例是由LogManager来创建的,它会读取Log4j.xml或者Log4j.properties等配置文件,其配置文件中的属性又是谁去读的呢,进一步跟踪,在OptionConverter.class类中selectAndConfigure方法中可以看到如下代码:
if(clazz == null && filename != null && filename.endsWith(".xml")) {
clazz = "org.apache.log4j.xml.DOMConfigurator";
}
if(clazz != null) {
LogLog.debug("Preferred configurator class: " + clazz);
configurator = (Configurator) instantiateByClassName(clazz,
Configurator.class,
null);
if(configurator == null) {
LogLog.error("Could not instantiate configurator ["+clazz+"].");
return;
}
} else {
configurator = new PropertyConfigurator();
}
表示其配置类是由org.apache.log4j.xml.DOMConfigurator来实现的,我们也可以在系统变量中设置。DOMConfigurator类中定义了很多常量,用来表示Log4j.xml等配置文件的各个属性。
现在我们假设项目中的Log4j.xml中的配置文件是下面这样的。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/
<appender name="DailyRollingFileAppender" class="org.apache.log4j.DailyRollingFileAppender">
<param name="File" value="${file}" />
<param name="DatePattern" value="'.'yyyy-MM-dd'.log'" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="[%d{dd HH:mm:ss,SSS\} %-5p] [%t] %c{1}.%M(%L) - %m%n" />
</layout>
</appender>
<root>
<level value="${level}" />
<appender-ref ref="CONSOLE" />
<appender-ref ref="DailyRollingFileAppender"/>
</root>
</log4j:configuration>
可以看到有两条红线选中的代码,其中File和level并没有直接赋值,这两个值需要从项目中总的配置文件中读取,其他开源组件的配置文件中经常修改的属性也写在这个总的配置文件中,这样不仅仅是对开发人员,还对实施人员都方便多了。那么如何实现这个需求呢?
只需写一个拓展类,继承自DOMConfigurator.class类,重写其中的某些方法,并设置系统变量log4j.configuratorClass为继承自DOMConfigurator.class的拓展类,代码如下:
拓展类:parseLevel和setParameter方法中的逻辑代码可以自行处理。
package com.lsc.config;
publicclass ExtDOMConfigurator extends DOMConfigurator{
staticpublicvoid configure(URL url) throws FactoryConfigurationError {
new ExtDOMConfigurator().doConfigure(url, LogManager.getLoggerRepository());
}
@Override
publicvoid parseLevel(Element element, Logger logger, boolean isRoot) {
if (element.getAttribute("value").contains("level")) {
element.setAttribute("value", "info");
}
}
@Override
protectedvoid setParameter(Element elem, PropertySetter propSetter) {
if (elem.getAttribute("value").contains("f")) {
elem.setAttribute("value", "E:/bizfuse/log/bizfuse.log");
}
super.setParameter(elem, propSetter);
}
}
测试类:
package com.lsc;
publicclass Log4jTest {
publicstaticvoid main(String[] args) {
URL url = Log4j.class.getResource("/log4j.xml");
System.setProperty("log4j.configuratorClass", "com.lsc.config.ExtDOMConfigurator");
ExtDOMConfigurator.configure(url);
Logger logger = Logger.getLogger(Log4jTest.class);
logger.info(new Date() + " test");
}
}