前段时间在项目的过程中使用log4j来输出日志,但是在一个项目里我明明已经在src/main/resource目录下创建了log4j.properties。具体配置如下:
log4j.rootLogger = INFO, stdout
log4j.category.appcloud.approuter = INFO
log4j.category.appcloud.nginxcontroller = INFO
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Threshold = DEBUG
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = %d{ISO8601} %-5p [%F:%L] : %m%n
但是该模块就是没有日志输出。。于是乎就开始查看该模块引入的log4j的包是自己引入的还是引入上层模块的。于是发现该模块依赖的log4j是在另外一个工程里引入的,在eclipse中查看如下。
现在就大概可以猜出来原因了,大概就是因为配置文件加载顺序的问题。那我们来刨根问底一下:到底log4j加载配置文件的原则是什么?那我们来看log4j的源码吧。
我们使用log4j的时候,一般是通过Logger.getLogger(xxx)的方式来获取logger的实例的,而Logger.getLogger()方法其实是调用的LoggerManager.getLogger(xxx)方法。那我们就来看看那LoggerManager是怎么加载配置文件的。
static {
.....
/** Search for the properties file log4j.properties in the CLASSPATH. */
String override =OptionConverter.getSystemProperty(DEFAULT_INIT_OVERRIDE_KEY,
null);
// if there is no default init override, then get the resource
// specified by the user or the default config file.
if(override == null || "false".equalsIgnoreCase(override)) {
String configurationOptionStr = OptionConverter.getSystemProperty(
DEFAULT_CONFIGURATION_KEY,
null);
String configuratorClassName = OptionConverter.getSystemProperty(
CONFIGURATOR_CLASS_KEY,
null);
URL url = null; // if the user has not specified the log4j.configuration
// property, we search first for the file "log4j.xml" and then
// "log4j.properties"
if(configurationOptionStr == null) {
url = Loader.getResource(DEFAULT_XML_CONFIGURATION_FILE);
if(url == null) {
url = Loader.getResource(DEFAULT_CONFIGURATION_FILE);
}
} 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优先加载在classpath下的log4j.defaultInitOverride。如果没有default init override的话,就去加载用户自定义的配置类名称。(可以看出log4j支持log4j.defaultInitOverride 和log4j.configuration 和log4j.configuratorClass 系统属性配置)如果还是无法加载的话,就优先加载.xml的配置文件,最后才会去加载.properties文件。 所以我的项目里由于在引用maven依赖关系的时候,classpath下有.xml文件,便不会去加载本工程下的.properties文件。
这个问题找到了,怎么解决呢?在该工程下,写一个.xml文件,能把依赖的工程下的.xml文件覆盖掉吗?
其实关键的就是这个调用:url = Loader.getResource(DEFAULT_XML_CONFIGURATION_FILE);在这个调用里我抽出了debug的输出,可以清楚的看到,针对xml或者properties。log4j优先加载本工程中的配置文件,其次加载jar 包中的配置文件,最后加载系统类路径的配置文件。并且前者如果加载成功,后面的加载便不进行了。
LogLog.debug("Trying to find ["+resource+"] using context classloader "
+classLoader+".");
LogLog.debug("Trying to find ["+resource+"] using "+classLoader
+" class loader.");
LogLog.debug("Trying to find ["+resource+
"] using ClassLoader.getSystemResource().");