首先来看log4j是怎么使用的,首先在web.xml中配置listener在初始化的时候对日志系统进行初始化:
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>/WEB-INF/classes/log4j.properties</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>
其中Log4jConsigListener是一个ServletContextListener,用来监听web应用的生命周期,那么就可以在web应用初始化的时候完成日志的初始化。在log4j.xml的配置文件中有下面几种角色:
- appender
- logger
- root
在appender中定制了写文件的方式:buffer(缓存)、file(文件)、encoding(编码)、layout(格式),举个例子:
<appender name="PROJECT" class="com.alibaba.common.logging.spi.log4j.DailyRollingFileAppender">
<param name="file" value="project.log"/>
<param name="append" value="true"/>
<param name="encoding" value="GBK"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d [%X{requestURIWithQueryString}] %t %-5p %c{2} - %m%n"/>
</layout>
</appender>
可以配置多个logger,用来在不同的地方使用:
<logger name="com.my.project" additivity="false">
<level value="info"/>
<appender-ref ref="PROJECT"/>
</logger>
然后就可以在日志中使用它来打印日志:
static Logger logger = LoggerFactory.getLogger(getClass());
logger.info("message");
而在root中接受所有日志,比如可以通过拦截所有ERROR的日志来将所有的错误日志。
---------- ---------- ---------- ---------- 分割线 ---------- ---------- ---------- ----------
日志的初始化的过程就是解析log4j.xml然后构造内存中的数据,解析的过程在org.apache.log4j.xml.DOMConfigurator#doConfigure中,得到的数据有:
LoggerRepository是Logger的容器,是一个以RootLogger为根的树,Logger之间的关系和层次由name决定,Hierarchy是log4j中默认实现。在使用是根据name查找Logger,然后依次调用Logger中的Appender.doAppend方法来执行真正的日志输出。
---------- ---------- ---------- ---------- 分割线 ---------- ---------- ---------- ----------
log4j是早期成功的日志系统,该开发团队因为对其不满意又开发了logback。在系统中强依赖某个日志不是一件好事情,这样便有了日志框架(比如slf4j),它提供了统一的记录日志的接口,允许最终用户在部署应用的时候使用自己喜欢的日志系统。slf4j的结构如下:
如果一个日志系统要使得用户可以通过slf4j框架使用,要提供一个StaticLoggerBinder来进行绑定,绑定的方法是直接让ClassLoader找到类,如下:
private static void singleImplementationSanityCheck() {
try {
ClassLoader loggerFactoryClassLoader = LoggerFactory.class.getClassLoader();
Enumeration paths;
// 是用ClassLoader找到所有的相同的"类文件"
if (loggerFactoryClassLoader == null) {
paths = ClassLoader.getSystemResources(STATIC_LOGGER_BINDER_PATH);
} else {
paths = loggerFactoryClassLoader.getResources(STATIC_LOGGER_BINDER_PATH);
}
List implementationList = new ArrayList();
while (paths.hasMoreElements()) {
URL path = (URL) paths.nextElement();
implementationList.add(path);
}
// 如果有多个"类文件",那么说明是要矛盾的,打印错误信息
if (implementationList.size() > 1) {
Util.report("Class path contains multiple SLF4J bindings.");
for (int i = 0; i < implementationList.size(); i++) {
Util.report("Found binding in [" + implementationList.get(i) + "]");
}
Util.report("See " + MULTIPLE_BINDINGS_URL + " for an explanation.");
}
} catch (IOException ioe) {
Util.report("Error getting resources from path", ioe);
}
}
这种绑定的方式叫静态绑定,当有多个相同的类Binder的时候,用的是哪个日志系统就要看先加载的是哪个类了。如果代码中已经使用了common logging的方式进行日志处理,这时候如果想改用slf4j来做,可以使用
jcl-over-slf4j
来实现。
---------- ---------- ---------- ---------- END ---------- ---------- ---------- ----------