深入源码之SLF4J
Commons Logging+Log4J一直是Java日志的经典组合,以至于很多服务器都使用了类似的配置,像WebSphere、以前的Tomcat都使用Commons Logging作为日志输出框架,而据说JBoss则直接Commons Logging和Log4J一起使用了(这个估计是为了解决Commons Logging中经常在这类服务器上遇到的ClassLoader问题)。然而Log4J的开发团队对Commons Logging貌似不满意(可以从Log4J Manual中看出一些端倪),因而Log4J团队开发了自己的日志门面框架SLF4J(Simple Logging Façade For Java),貌似他们对自己开发的Log4J的性能也不满意,然后又弄出了个LogBack,关键执行语句的性能要比Log4J快10倍以上(官网资料,我本人还没有仔细看过LogBack的代码,更没有测试过,不知道具体性能能提高多少),这是后话,等过几年看LogBack代码后再仔细讨论。
以我个人理解,SLF4J的出现是为了解决Commons Logging存在的两个问题:
1. Commons Logging存在的ClassLoader问题,即当服务器本身引入Commons Logging时,如果在服务器中载入Commons Logging包,则该包中的类由服务器的ClassLoader加载,从而在加载Log4J中的Logger类时会出现ClassNotFoundException,因为服务器中的ClassLoader没法找到Web App下的jar包。对于父ClassLoader优先的类加载机制来说,目前的一个解决方案是使用commons-logging-api-1.1.1.jar包,该包的Log实现类只包含Jdk14Logger、SimpleLog、NoOpLog三个类,因而在加载这几个类时会使用Web App中的Commons Logging包,从而解决ClassNotFoundException的问题,然而这种方式对那些实现Child ClassLoader First的服务器来说,由WebClassLoader父ClassLoader加载的类在使用日志时会存在问题,因为它们的Log接口由加载自身类的ClassLoader加载,而Log4JLogger类却由WebClassLoader加载。具体关于Commons Logging中存在的问题我会在另外一篇文章中详细说明。
2. 在使用Commons Logging时,我们经常会看到以下方法的写法:
if (logger.isDebugEnabled()) {
logger.info("Loading XML bean definitions from " + encodedResource.getResource());
}
存在isDebugEnabled()的判断逻辑是为了在避免多余的字符串拼接,即如果不存在isDebugEnabled()判断,即使当前日志级别为ERROR时,在遇到logger.info()调用时,它还会先拼接日志消息的字符串,然后进入该方法内,才发现这个日志语句不用打印。而这种多余的拼接不仅浪费了多余的CPU操作,而且会增加GC的负担。SLF4J则提供以下的方式来解决这个问题:
logger.info("Loading XML bean definitions from {}", encodedResource.getResource());
SLF4J综述
类似Commons Logging,SLF4J在使用时通过LoggerFactory得到命名的Logger实例,然后通过该Logger实例调用相应的方法打印日志:
final Logger logger = LoggerFactory.getLogger("levin.logging.slf4j");
logger.info("Using slf4j, current time is {}", new Date());
然而不同于Commons Logging的动态绑定机制,SLF4J则采用了一种静态绑定的机制,即每个支持SLF4J的Logging框架必须存在一个继承自LoggerFactoryBinder接口的StaticLoggerBinder类:
public inte易做图ce LoggerFactoryBinder {
public ILoggerFactory getLoggerFactory();
public String getLoggerFactoryClassStr();
}
LoggerFactory调用StaticLoggerBinder类中的getLoggerFactory()方法返回相应的ILoggerFactory实例:
public inte易做图ce ILoggerFactory {
public Logger getLogger(String name);
}
最后通过ILoggerFactory实例获取Logger实例:
public inte易做图ce Logger {
public String getName();
...(trace)
public boolean isDebugEnabled();
public void debug(String msg);
public void debug(String format, Object arg);
public void debug(String format, Object arg1, Object arg2);
public void debug(String format, Object... arguments);
public void debug(String msg, Throwable t);
public boolean isDebugEnabled(Marker marker);
public void debug(Marker marker, String msg);
public void debug(Marker marker, String format, Object arg);
public void debug(Marker marker, String format, Object arg1, Object arg2);
public void debug(Marker marker, String format, Object... arguments);
public void debug(Marker marker, String msg, Throwable t);
...(info)
...(warn)
...(error)
}
也正是因为这个设计,SLF4J在classpath下只支持一个桥接包(slf4j-易做图-<version>.jar、slf4j-log4j12-<version>.jar、slf4j-jdk14-<version>.jar、logback-classic-<version>.jar等)。如果在classpath下存在多个桥接包,则具体用哪个就要看这几个桥接包的加载顺序了,实际中会使用先加载的桥接包。同时SLF4J会打印使用哪个桥接包,哪些桥接包没有使用。这种静态绑定的设计比Commons Logging在可扩展性上具有更加灵活的机制,对“可插拔”的支持也更加高效。如果要支持一个新的Logging框架,Commons Logging需要通过在属性配置文件、或虚拟机属性中配置支持这个新的Logging框架的实现类(实现Log接口);而SLF4J则只需要编写一个五个相应的类:
1. 实现Logger接口的类
2. 实现ILoggerFactory接口的类
3. 实现LoggerFactoryBinder接口的类StaticLoggerBinder类(必须使用StaticLoggerBinder类名),并且存在一个静态的getSingleton()方法。
4. 实现MarkerFactoryBinder类的StaticMarkerBinder类(必须使用StaticMarkerBinder类名),可选。一般也会存在一个静态的SINGLETON字段,不过也是可选的。
5. 实现StaticMDCBinder类,可选。一般也会存在一个静态的SINGLETON字段,也可选。
SLF4J的类设计也相对比较简单(也感觉有点零散):
SLF4J实现实例,SLF4J API与SLF4J Simple
由于采用了静态绑定的方式,而不是像Commons Logging中的动态绑定,SLF4J中LoggerFactory的实现要比Commons Logging中LogFactory的实现要简单的多。即LoggerFactory调用getILoggerFactory()方法,该方易做图初始化LoggerFactory,即通过在bind()方法中加载classpath中的StaticLoggerBinder类,并根据加载结果设置当前LoggerFactory的初始化状态,从而在getILoggerFactory()方法中通过当前LoggerFactory的状态判断返回的ILoggerFactory实例。简单的示意图如下:
bind()方法的主要源码如下:
private final static void bind() {
try {
...
//实现绑定
StaticLoggerBinder.getSingleton();
INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION;
...
} catch (NoClassDefFoundError ncde) {
String msg = ncde.getMessage();
//判断是否是因为没有找到StaticLoggerBinder类引起的异常
//此时,使用NOPLoggerFactory类返回给getILoggerFactory(),不打印任何日志
if (messageContainsOrgSlf4jImplStaticLoggerBinder(msg)) {
INITIALIZATION_STATE = NOP_FALLBACK_INITIALIZATION;
...
} else {
// INITIALIZATION_STATE = FAILED_INITIALIZATION
failedBinding(ncde);
throw ncde;
}
} catch (java.lang.NoSuchMethodError nsme) {
String msg = nsme.getMessage();
if (msg != null && msg.indexOf("org.slf4j.impl.StaticLoggerBinder.getSingleton()") != -1) {
INITIALIZATION_STATE = FAILED_INITIALIZATION;
...
}
throw nsme;
} catch (Exception e) {
&n
补充:综合编程 , 其他综合 ,