之前已经看到了使用properties文件或者xml文件配置Log4J的方式。但是,在这两种配置方式中,我们看到,都必须要使用者在整个应用启动之前,使用代码的方式完成Log4J相应配置的读取和加载。我们已经学习了Log4j这么久了,一直会有一个疑问,为什么Log4J就必须让我们自己来配置Log4J?为什么必须让我们自己去写那一段初始化代码?原因其实很简单,在Log4J中,所有的日志总的有一个输出的方式,换句话说,必须有一个Appender。可能大部分的人会说,那么如果什么都没有配置,那直接把日志输出到Console上啊?其实不是这样的,有一些JAVA工具其实默认没有提供Console,这对我们来说可能有点不可思议,不过,还真有这样的工具。Log4J考虑到这些问题,所以没有提供默认的Logger配置。在没有任何配置的情况下,直接启动Log4j就会出现我们最开始看到的那段警告。
既然要求我们自己来配置Log4j,那么又会出现相关的问题,不管我们采用哪种配置方式,Log4J总会要求我们在应用启动的最开始,完成Log4J的配置,所以我们不得不在一个静态类的静态代码块中完成相关的代码配置。这对程序员和Log4j框架本身的使用,都是一个不友好的设计。
考虑到这些因素,Log4J提供了一个默认初始化流程。这个默认的流程作为静态代码块注册在LogManager类中。下面结合LogManager的源代码,来看看Log4J提供的初始化流程:
Hierarchy h = new Hierarchy(new RootLogger((Level) Level.DEBUG));
repositorySelector = new DefaultRepositorySelector(h);
创建一个日志体系对象,并新建一个RootLogger作为这个日志体系的rootLogger,并将rootLogger的日志级别设置为DEBUG;
String override =OptionConverter.getSystemProperty(DEFAULT_INIT_OVERRIDE_KEY,
null);
DEFAULT_INIT_OVERRIDE_KEY的值为log4j.defaultInitOverride,这句代码代表,在系统环境中找名字为log4j.defaultInitOverride的系统变量;
if(override == null || "false".equalsIgnoreCase(override)) {
如果这个系统变量没有设置值,如果有值且设置为false,则按照Log4J的默认初始化流程进行,否则就跳过初始化配置流程;
String configurationOptionStr = OptionConverter.getSystemProperty( DEFAULT_CONFIGURATION_KEY, null);
String configuratorClassName = OptionConverter.getSystemProperty(
CONFIGURATOR_CLASS_KEY, null);
URL url = null;
DEFAULT_CONFIGURATION_KEY=log4j.configuration
CONFIGURATOR_CLASS_KEY=log4j.configuratorClass
得到系统log4j.configuration和log4j.configuratorClass两个系统变量;
if (configurationOptionStr == null) {
url = Loader.getResource(DEFAULT_XML_CONFIGURATION_FILE);
if (url == null) {
url = Loader.getResource(DEFAULT_CONFIGURATION_FILE);
}
}
DEFAULT_XML_CONFIGURATION_FILE=log4j.xml
DEFAULT_CONFIGURATION_FILE=log4j.properties
如果没有设置log4j.configuration变量,则先尝试使用ClassPath下的log4j.xml文件,如果没有log4j.xml文件,则使用ClassPath下的log4j.properties文件。
else {
try {
url = new URL(configurationOptionStr);
} catch (MalformedURLException ex) {
// so, resource is not a URL:
// attempt to get the resource from the class path
url = Loader.getResource(configurationOptionStr);
}
}
如果设置了log4j.configuration环境变量,则直接使用log4j.configuration环境变量指定的配置文件来加载配置。
if(url != null) {
LogLog.debug("Using URL ["+url+"] for automatic log4j configuration.");
try {
OptionConverter.selectAndConfigure(url, configuratorClassName, LogManager.getLoggerRepository());
} catch (NoClassDefFoundError e) {
LogLog.warn("Error during default initialization", e);
}
} else {
LogLog.debug("Could not find resource: ["+configurationOptionStr+"].");
}
经过前面的配置,如果最终找到一个配置文件,则根据配置文件的类型XML或properties选择指定的DOMConfigurator来完成配置,如果配置文件是properties,则使用PropertyConfigurator来完成配置。另外,如果环境变量log4j.configuratorClass设置了一个确定的配置文件类型,则使用给定的Configurator对象来完成配置。
有了Log4J提供的初始化流程,我们就可以直接使用Log4j,而不需要再提供自己的初始化代码了:
@Test
public void testXmlConf() {
Logger log = Logger.getLogger("cd.itcast.log");
log.debug("log debug..");
log.info("log info..");
}
在这段测试代码中,我们删除了所有的初始化方法,直接使用Logger完成日志。在classpath下,我们提供两个配置文件,一个XML,一个properties:
log4j.properties:
log4j.rootLogger=INFO,stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.SimpleLayout
log4j.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration>
<appender name="console" class="org.apache.log4j.ConsoleAppender">
<layout class="org.apache.log4j.PatternLayout">
<param name="conversionPattern" value="%r [%t] %p %c %x - %m%n"/>
</layout>
</appender>
<logger name="cd.itcast.log">
<level value="DEBUG" />
<appender-ref ref="console"/>
</logger>
</log4j:configuration>
可以看到,在properties文件中,我们使用simpleLayout输出日志级别INFO;在xml文件中,我们使用patternLayout输出日志级别DEBUG。我们运行测试,控制台输出:
0 [main] DEBUG cd.itcast.log - log debug..
0 [main] INFO cd.itcast.log - log info..
可以看到,这时候,log4j.xml起了作用。如果删除log4j.xml,再次运行:
INFO - log info..
可以看到,这个时候,log4j.properties起了作用。
上面的所有的应用,都是在application方式下的测试。
既然要求我们自己来配置Log4j,那么又会出现相关的问题,不管我们采用哪种配置方式,Log4J总会要求我们在应用启动的最开始,完成Log4J的配置,所以我们不得不在一个静态类的静态代码块中完成相关的代码配置。这对程序员和Log4j框架本身的使用,都是一个不友好的设计。
考虑到这些因素,Log4J提供了一个默认初始化流程。这个默认的流程作为静态代码块注册在LogManager类中。下面结合LogManager的源代码,来看看Log4J提供的初始化流程:
Hierarchy h = new Hierarchy(new RootLogger((Level) Level.DEBUG));
repositorySelector = new DefaultRepositorySelector(h);
创建一个日志体系对象,并新建一个RootLogger作为这个日志体系的rootLogger,并将rootLogger的日志级别设置为DEBUG;
String override =OptionConverter.getSystemProperty(DEFAULT_INIT_OVERRIDE_KEY,
null);
DEFAULT_INIT_OVERRIDE_KEY的值为log4j.defaultInitOverride,这句代码代表,在系统环境中找名字为log4j.defaultInitOverride的系统变量;
if(override == null || "false".equalsIgnoreCase(override)) {
如果这个系统变量没有设置值,如果有值且设置为false,则按照Log4J的默认初始化流程进行,否则就跳过初始化配置流程;
String configurationOptionStr = OptionConverter.getSystemProperty( DEFAULT_CONFIGURATION_KEY, null);
String configuratorClassName = OptionConverter.getSystemProperty(
CONFIGURATOR_CLASS_KEY, null);
URL url = null;
DEFAULT_CONFIGURATION_KEY=log4j.configuration
CONFIGURATOR_CLASS_KEY=log4j.configuratorClass
得到系统log4j.configuration和log4j.configuratorClass两个系统变量;
if (configurationOptionStr == null) {
url = Loader.getResource(DEFAULT_XML_CONFIGURATION_FILE);
if (url == null) {
url = Loader.getResource(DEFAULT_CONFIGURATION_FILE);
}
}
DEFAULT_XML_CONFIGURATION_FILE=log4j.xml
DEFAULT_CONFIGURATION_FILE=log4j.properties
如果没有设置log4j.configuration变量,则先尝试使用ClassPath下的log4j.xml文件,如果没有log4j.xml文件,则使用ClassPath下的log4j.properties文件。
else {
try {
url = new URL(configurationOptionStr);
} catch (MalformedURLException ex) {
// so, resource is not a URL:
// attempt to get the resource from the class path
url = Loader.getResource(configurationOptionStr);
}
}
如果设置了log4j.configuration环境变量,则直接使用log4j.configuration环境变量指定的配置文件来加载配置。
if(url != null) {
LogLog.debug("Using URL ["+url+"] for automatic log4j configuration.");
try {
OptionConverter.selectAndConfigure(url, configuratorClassName, LogManager.getLoggerRepository());
} catch (NoClassDefFoundError e) {
LogLog.warn("Error during default initialization", e);
}
} else {
LogLog.debug("Could not find resource: ["+configurationOptionStr+"].");
}
经过前面的配置,如果最终找到一个配置文件,则根据配置文件的类型XML或properties选择指定的DOMConfigurator来完成配置,如果配置文件是properties,则使用PropertyConfigurator来完成配置。另外,如果环境变量log4j.configuratorClass设置了一个确定的配置文件类型,则使用给定的Configurator对象来完成配置。

有了Log4J提供的初始化流程,我们就可以直接使用Log4j,而不需要再提供自己的初始化代码了:
@Test
public void testXmlConf() {
Logger log = Logger.getLogger("cd.itcast.log");
log.debug("log debug..");
log.info("log info..");
}
在这段测试代码中,我们删除了所有的初始化方法,直接使用Logger完成日志。在classpath下,我们提供两个配置文件,一个XML,一个properties:
log4j.properties:
log4j.rootLogger=INFO,stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.SimpleLayout
log4j.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration>
<appender name="console" class="org.apache.log4j.ConsoleAppender">
<layout class="org.apache.log4j.PatternLayout">
<param name="conversionPattern" value="%r [%t] %p %c %x - %m%n"/>
</layout>
</appender>
<logger name="cd.itcast.log">
<level value="DEBUG" />
<appender-ref ref="console"/>
</logger>
</log4j:configuration>
可以看到,在properties文件中,我们使用simpleLayout输出日志级别INFO;在xml文件中,我们使用patternLayout输出日志级别DEBUG。我们运行测试,控制台输出:
0 [main] DEBUG cd.itcast.log - log debug..
0 [main] INFO cd.itcast.log - log info..
可以看到,这时候,log4j.xml起了作用。如果删除log4j.xml,再次运行:
INFO - log info..
可以看到,这个时候,log4j.properties起了作用。
上面的所有的应用,都是在application方式下的测试。