Java日志之Slf4j,Log4J,logback原理总结

  几乎任何应用,必定是须要日志的。html

  那么,面对种类繁多的日志框架和配置,咱们该何去何从?java

  1.前奏:我是在研究mybatis源码的过程当中才意识到须要搞明白日志原理这回事,由于mybatis(和一些其余开源框架,好比rocketmq)都有本身的日志系统,他们在框架内部都使用的是本身的日志API,那么,为何他们不像咱们日常那样配置一个log4j呢?根本缘由我也不太清楚,不过我猜想可能有这么一些理由,这些框架比较老,当初尚未slf4j这种事实上的标准,另外一方面,有一些特殊的定制化的日志。完全研究清楚mybatis的日志系统以后,我的以为这一块设计得不太好,至少今天看来,不太优雅,由于原本一个slf4j就能搞定全部,非得在源码中加入本身的org.apache.ibatis.logging这个包,里面包含一些适配器,虽然代码并不复杂,可是有点画蛇添足。apache

  2.原理:slf4j是标准,也是门面,他对用户提供统一的API,而下方对接各个日志框架。这有点相似JVM,咱们Java开发者使用统一的API,而JVM对接各个操做系统。严格意义上说slf4j自身并不提供日志具体实现。图片来自:http://www.javashuo.com/article/p-ujrqvegx-mo.htmlapi

 

  3.slf4j采用的是SPI机制,指定一个标准的目录结构:org.slf4j.impl.StaticLoggerBinder,然第三方的框架都必须存在一个这样的类,用于和slf4j创建关系,好比slf4j-simple.jar,logback,这两个直接实现了slf4j的接口,而对于log4j这种则须要一个中间适配器slf4j-log4j12。因而乎,当调用slf4j的Logger logger = LoggerFactory.getLogger(XXX.class)的时候,虽然使用的是slf4j的api,可是真正输出日志的是具体的日志框架,这样子作的好处就是,当某一天你但愿更换日志框架了,只须要把具体日志框架的jar包替换掉,不须要更改任何一行代码,就能实现日志框架的切换。mybatis

  4.slf4j是如何发现具体日志框架的,这就得意于spi机制,前面说每一个日志框架都须要存在一个org.slf4j.impl.StaticLoggerBinder类,log4j则是经过中间适配器slf4j-log4j12。当调用LoggerFactory.getLogger的时候,就会去classpath中寻找StaticLoggerBinder这个类,若是不存在或者存在超过1个,那么会报错,classpath有且只能存在一个StaticLoggerBinder类。框架

  5.分析mybatis的日志框架:mybatis有一套属于本身的日志系统,日志api是:Log log = LogFactory.getLog(xxx.class),于此同时,封装了几个主流的日志框架适配器,包括:SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING,当调用Log log = LogFactory.getLog(xxx.class)时,会初始化众多适配器中的一个,能够在mybatis的配置文件中经过logImpl指定具体的一个,若是不指定那么默认使用SLF4J,由于这里在LogFactory类中的静态代码快第一个就是SLF4J:spa

  static {
    tryImplementation(LogFactory::useSlf4jLogging);
    tryImplementation(LogFactory::useCommonsLogging);
    tryImplementation(LogFactory::useLog4J2Logging);
    tryImplementation(LogFactory::useLog4JLogging);
    tryImplementation(LogFactory::useJdkLogging);
    tryImplementation(LogFactory::useNoLogging);
  }

假设使用默认配置,那么就会初始化Slf4jImpl类,这个类内部有个代理log,这个代理log就是Logger logger = LoggerFactory.getLogger(clazz),这就回归到slf4j的标准使用方式上面来了,mybatis打印日志,其实就是代理对象在打印,而代理对象就是classpath中配置的具体日志框架。操作系统

  6.分析log4j是如何与slf4j整合的:前面说到,要使用log4j就必须引入slf4j-log4j12这个jar包,而这个jar包中一样存在一个StaticLoggerBinder类,当咱们调用LoggerFactory.getLogger(clazz)的时候,一样是初始化StaticLoggerBinder,而后调用利用ILoggerFactory建立一个log4j的Logger实例,代码以下:debug

 1 public class Log4jLoggerFactory implements ILoggerFactory {
 2 
 3   // key: name (String), value: a Log4jLoggerAdapter;
 4   ConcurrentMap<String, Logger> loggerMap;
 5 
 6 
 7   public Log4jLoggerFactory() {
 8     loggerMap = new ConcurrentHashMap<String, Logger>();
 9   }
10 
11   /*
12    * (non-Javadoc)
13    * 
14    * @see org.slf4j.ILoggerFactory#getLogger(java.lang.String)
15    */
16   public Logger getLogger(String name) {
17     Logger slf4jLogger = loggerMap.get(name);
18     if (slf4jLogger != null) {
19       return slf4jLogger;
20     } else {
21       org.apache.log4j.Logger log4jLogger;
22       if(name.equalsIgnoreCase(Logger.ROOT_LOGGER_NAME))
23         log4jLogger = LogManager.getRootLogger();
24       else
25         log4jLogger = LogManager.getLogger(name);
26 
27       Logger newInstance = new Log4jLoggerAdapter(log4jLogger);
28       Logger oldInstance = loggerMap.putIfAbsent(name, newInstance);
29       return oldInstance == null ? newInstance : oldInstance;
30     }
31   }
32 }

最关键的一行就是第27行Logger newInstance = new Log4jLoggerAdapter(log4jLogger),slf4j的Logger对象其实是一个log4j的适配器对象(也是代理对象),当slf4j调用好比debug方法的时候,其实是代理对象(也就是真实的log4j对象)在调用debug方法。设计

相关文章
相关标签/搜索