slf4j

作者在 2011-07-06 09:33:49 发布以下内容
准确的说,slf4j并不是一种具体的日志系统,而是一个用户日志系统的facade,允许用户在部署最终应用时方便的变更其日志系统。

使用方式:
       在系统开发中,统一按照slf4j的API进行开发,在部署时,选择不同的日志系统包,即可自动转换到不同的日志系统上。比如:选择JDK自带的日志系统,则只需要将slf4j-api-1.5.10.jar和slf4j-jdk14-1.5.10.jar放置到classpath中即可,如果中途无法忍受JDK自带的日志系统了,想换成log4j的日志系统,仅需要用slf4j-log4j12-1.5.10.jar替换slf4j-jdk14-1.5.10.jar即可(当然也需要log4j的jar及配置文件)
使用场景:
        我们开发的是类库或者嵌入式组件,可以考虑使用slf4j,因为我们并不能决定用户选择哪种日志系统(不同软件开发公司会钟情于不同的日志系统);但是如果我们开发独立应用,面向的是最终客户,则无需考虑slf4j,因为最终客户只关心功能实现,不会在意开发公司具体使用什么日志系统的。
应用举例
        我们先举个实际例子,让大家有个直观认识,建立一个简单测试类,如下:
view plaincopy to clipboardprint?
package chb.test.slf4j;  
import org.slf4j.Logger;  
import org.slf4j.LoggerFactory;  
/** 
 * @author chb 
 * 
 */ 
public class TestSlf4j {  
        Logger log = LoggerFactory.getLogger(TestSlf4j.class);  
          
        public void testLog(){  
                log.info("this is a test log");  
        }  
        /** 
         * @param args 
         */ 
        public static void main(String[] args) {  
                TestSlf4j slf = new TestSlf4j();  
                slf.testLog();  
        }  

package chb.test.slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
 * @author chb
 *
 */
public class TestSlf4j {
        Logger log = LoggerFactory.getLogger(TestSlf4j.class);
       
        public void testLog(){
                log.info("this is a test log");
        }
        /**
         * @param args
         */
        public static void main(String[] args) {
                TestSlf4j slf = new TestSlf4j();
                slf.testLog();
        }
}
1>JDK自带的log输出
       首先,我们在classpath中加入slf4j-api-1.5.10.jar和slf4j-jdk14-1.5.10.jar两个包,然后运行main函数,输出信息如下:
view plaincopy to clipboardprint?
2010-1-5 21:44:47 chb.test.slf4j.TestSlf4j testLog  
信息: this is a test log 
2010-1-5 21:44:47 chb.test.slf4j.TestSlf4j testLog
信息: this is a test log
 2>slg4j提供的simple log
      然后,我们用slf4j-simple-1.5.10.jar替换slf4j-jdk14-1.5.10.jar,选择使用slf4j提供的simple log,输出信息如下:
view plaincopy to clipboardprint?
0 [main] INFO chb.test.slf4j.TestSlf4j - this is a test log 
0 [main] INFO chb.test.slf4j.TestSlf4j - this is a test log
 3>log4j日志输出
    再然后,我们再用slf4j-log4j12-1.5.10.jar替换slf4j-simple-1.5.10.jar(记得classpath也需要增加log4j依赖jar包),同时增加一个log4j.properties文件,内容如下:
view plaincopy to clipboardprint?
log4j.debug=true  
log4j.rootLogger=DEBUG,stdout  
log4j.appender.stdout=org.apache.log4j.ConsoleAppender  
log4j.appender.stdout.Target=System.out  
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout  
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p - %m%n 
log4j.debug=true
log4j.rootLogger=DEBUG,stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p - %m%n
 我们再稍微修改一下main函数,加载一下log4j.properties,如;
view plaincopy to clipboardprint?
public static void main(String[] args) {  
        System.setProperty("log4j.configuration", "log4j.properties");  
        TestSlf4j slf = new TestSlf4j();  
        slf.testLog();  

        public static void main(String[] args) {
                System.setProperty("log4j.configuration", "log4j.properties");
                TestSlf4j slf = new TestSlf4j();
                slf.testLog();
        }
运行main函数,输出结果如下:
view plaincopy to clipboardprint?
log4j: Parsing for [root] with value=[DEBUG,stdout].  
log4j: Level token is [DEBUG].  
log4j: Category root set to DEBUG  
log4j: Parsing appender named "stdout".  
log4j: Parsing layout options for "stdout".  
log4j: Setting property [conversionPattern] to [%d{ABSOLUTE} %5p - %m%n  ].  
log4j: End of parsing for "stdout".  
log4j: Setting property [target] to [System.out].  
log4j: Parsed "stdout" options.  
log4j: Finished configuring.  
22:01:40,831  INFO - this is a test log 
log4j: Parsing for [root] with value=[DEBUG,stdout].
log4j: Level token is [DEBUG].
log4j: Category root set to DEBUG
log4j: Parsing appender named "stdout".
log4j: Parsing layout options for "stdout".
log4j: Setting property [conversionPattern] to [%d{ABSOLUTE} %5p - %m%n  ].
log4j: End of parsing for "stdout".
log4j: Setting property [target] to [System.out].
log4j: Parsed "stdout" options.
log4j: Finished configuring.
22:01:40,831  INFO - this is a test log
 原理介绍--静态绑定
        大家看到要使用哪种日志系统,只需要将对应的日志系统所需要的jar包文件(包括slf4j提供的jar包和日志系统自身依赖的jar包,例如:slf4j-log4j12-1.5.10.jar和log4j.1.2.jar)放入classpath即可,slf4j可以自动探测具体使用哪种日志系统,这种技术被称为静态绑定。
       在实际使用中,我们通过LoggerFactory.getLogger()获得logger,查看LoggerFactory的源代码会发现如下两点,
LoggerFactory通过StaticLoggerBinder.getSingleton().getLoggerFactory()获得LogFactory,然后再通过该LogFactory来获取logger的
但是StaticLoggerBinder类并不在slf4j-api-1.5.10.jar中,分析与具体日志系统相关的jar包,会发现每个jar包都有一个StaticLoggerBinder类的实现(如slf4j-log4j12-1.5.10.jar、slf4j-simple-1.5.10.jar、slf4j-jdk14-1.5.10.jar均有StaticLoggerBinder类实现),这就很明白了,slf4j在启动时会动态到classpath中查找StaticLoggerBinder类,找到之后就可以生成对应日志系统的日志文件了。
    这里就有一个问题了,slf4j是如何将自己的通用日志格式转成不同的日志系统的格式的呢?
    我们再分析每个日志系统相关的源代码,会发现不同日志系统包都会有一个Adapter,用来在slf4j和不同日志系统之间做转换。
 
   下面是LoggerFactory.java的源代码,大家可以参考一下,为了理解方便,我已经将代码顺序做了调整:
view plaincopy to clipboardprint?
/** 
 * Return a logger named corresponding to the class passed as parameter, using 
 * the statically bound {@link ILoggerFactory} instance. 
 *  
 * @param clazz 
 *          the returned logger will be named after clazz 
 * @return logger 
 */ 
public static Logger getLogger(Class clazz) {  
  return getLogger(clazz.getName());  
}  
 
 
public static Logger getLogger(String name) {  
  ILoggerFactory iLoggerFactory = getILoggerFactory();  
  return iLoggerFactory.getLogger(name);  
}  
 
/** 
 * Return the {@link ILoggerFactory} instance in use. 
 *  
 * <p> 
 * ILoggerFactory instance is bound with this class at compile time. 
 *  
 * @return the ILoggerFactory instance in use 
 */ 
public static ILoggerFactory getILoggerFactory() {  
  if (INITIALIZATION_STATE == UNINITIALIZED) {  
    INITIALIZATION_STATE = ONGOING_INITILIZATION;  
    performInitialization();  
  }  
  switch (INITIALIZATION_STATE) {  
  case SUCCESSFUL_INITILIZATION:  
    return getSingleton().getLoggerFactory();  
  case FAILED_INITILIZATION:  
    throw new IllegalStateException(UNSUCCESSFUL_INIT_MSG);  
  case ONGOING_INITILIZATION:  
    // support re-entrant behavior.  
    // See also http://bugzilla.slf4j.org/show_bug.cgi?id=106  
    return TEMP_FACTORY;  
  }  
  throw new IllegalStateException("Unreachable code");  
}  
 
 
private final static void performInitialization() {  
  bind();  
  versionSanityCheck();  
  singleImplementationSanityCheck();  
}  
 
private final static void bind() {  
  try {  
    // the next line does the binding  
    getSingleton();  
    INITIALIZATION_STATE = SUCCESSFUL_INITILIZATION;  
    emitSubstituteLoggerWarning();  
  } catch (NoClassDefFoundError ncde) {  
    INITIALIZATION_STATE = FAILED_INITILIZATION;  
    String msg = ncde.getMessage();  
    if (msg != null && msg.indexOf("org/slf4j/impl/StaticLoggerBinder") != -1) {  
      Util  
          .reportFailure("Failed to load class \"org.slf4j.impl.StaticLoggerBinder\".");  
      Util.reportFailure("See " + NO_STATICLOGGERBINDER_URL  
          + " for further details.");  
    }  
    throw ncde;  
  } catch (Exception e) {  
    INITIALIZATION_STATE = FAILED_INITILIZATION;  
    // we should never get here  
    Util.reportFailure("Failed to instantiate logger [" 
        + getSingleton().getLoggerFactoryClassStr() + "]", e);  
  }  
}  
 
 
private final static StaticLoggerBinder getSingleton() {  
  if (GET_SINGLETON_METHOD == GET_SINGLETON_INEXISTENT) {  
    return StaticLoggerBinder.getSingleton();  
  }  
  if (GET_SINGLETON_METHOD == GET_SINGLETON_EXISTS) {  
    return StaticLoggerBinder.getSingleton();  
  }  
  try {  
    StaticLoggerBinder singleton = StaticLoggerBinder.getSingleton();  
    GET_SINGLETON_METHOD = GET_SINGLETON_EXISTS;  
    return singleton;  
  } catch (NoSuchMethodError nsme) {  
    GET_SINGLETON_METHOD = GET_SINGLETON_INEXISTENT;  
    return StaticLoggerBinder.getSingleton();  
  }  

  /**
   * Return a logger named corresponding to the class passed as parameter, using
   * the statically bound {@link ILoggerFactory} instance.
   *
   * @param clazz
   *          the returned logger will be named after clazz
   * @return logger
   */
  public static Logger getLogger(Class clazz) {
    return getLogger(clazz.getName());
  }
 
 
  public static Logger getLogger(String name) {
    ILoggerFactory iLoggerFactory = getILoggerFactory();
    return iLoggerFactory.getLogger(name);
  }
 
  /**
   * Return the {@link ILoggerFactory} instance in use.
   *
   * <p>
   * ILoggerFactory instance is bound with this class at compile time.
   *
   * @return the ILoggerFactory instance in use
   */
  public static ILoggerFactory getILoggerFactory() {
    if (INITIALIZATION_STATE == UNINITIALIZED) {
      INITIALIZATION_STATE = ONGOING_INITILIZATION;
      performInitialization();
    }
    switch (INITIALIZATION_STATE) {
    case SUCCESSFUL_INITILIZATION:
      return getSingleton().getLoggerFactory();
    case FAILED_INITILIZATION:
      throw new IllegalStateException(UNSUCCESSFUL_INIT_MSG);
    case ONGOING_INITILIZATION:
      // support re-entrant behavior.
      // See also http://bugzilla.slf4j.org/show_bug.cgi?id=106
      return TEMP_FACTORY;
    }
    throw new IllegalStateException("Unreachable code");
  }
 
 
  private final static void performInitialization() {
    bind();
    versionSanityCheck();
    singleImplementationSanityCheck();
  }
 
  private final static void bind() {
    try {
      // the next line does the binding
      getSingleton();
      INITIALIZATION_STATE = SUCCESSFUL_INITILIZATION;
      emitSubstituteLoggerWarning();
    } catch (NoClassDefFoundError ncde) {
      INITIALIZATION_STATE = FAILED_INITILIZATION;
      String msg = ncde.getMessage();
      if (msg != null && msg.indexOf("org/slf4j/impl/StaticLoggerBinder") != -1) {
        Util
            .reportFailure("Failed to load class \"org.slf4j.impl.StaticLoggerBinder\".");
        Util.reportFailure("See " + NO_STATICLOGGERBINDER_URL
            + " for further details.");
      }
      throw ncde;
    } catch (Exception e) {
      INITIALIZATION_STATE = FAILED_INITILIZATION;
      // we should never get here
      Util.reportFailure("Failed to instantiate logger ["
          + getSingleton().getLoggerFactoryClassStr() + "]", e);
    }
  }
 
 
  private final static StaticLoggerBinder getSingleton() {
    if (GET_SINGLETON_METHOD == GET_SINGLETON_INEXISTENT) {
      return StaticLoggerBinder.getSingleton();
    }
    if (GET_SINGLETON_METHOD == GET_SINGLETON_EXISTS) {
      return StaticLoggerBinder.getSingleton();
    }
    try {
      StaticLoggerBinder singleton = StaticLoggerBinder.getSingleton();
      GET_SINGLETON_METHOD = GET_SINGLETON_EXISTS;
      return singleton;
    } catch (NoSuchMethodError nsme) {
      GET_SINGLETON_METHOD = GET_SINGLETON_INEXISTENT;
      return StaticLoggerBinder.getSingleton();
    }
  }

小小框架 | 阅读 1508 次
文章评论,共0条
游客请输入验证码
浏览290837次