zoukankan      html  css  js  c++  java
  • SLF4J介绍[转]

    准确的说,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,因为最终客户只关心功能实现,不会在意开发公司具体使用什么日志系统的。

    应用举例

            我们先举个实际例子,让大家有个直观认识,建立一个简单测试类,如下:
    1. package chb.test.slf4j;  
    2. import org.slf4j.Logger;  
    3. import org.slf4j.LoggerFactory;  
    4. /** 
    5.  * @author chb 
    6.  * 
    7.  */  
    8. public class TestSlf4j {  
    9.         Logger log = LoggerFactory.getLogger(TestSlf4j.class);  
    10.           
    11.         public void testLog(){  
    12.                 log.info("this is a test log");  
    13.         }  
    14.         /** 
    15.          * @param args 
    16.          */  
    17.         public static void main(String[] args) {  
    18.                 TestSlf4j slf = new TestSlf4j();  
    19.                 slf.testLog();  
    20.         }  
    21. }  
     
    1>JDK自带的log输出
           首先,我们在classpath中加入slf4j-api-1.5.10.jar和slf4j-jdk14-1.5.10.jar两个包,然后运行main函数,输出信息如下:
    1. 2010-1-5 21:44:47 chb.test.slf4j.TestSlf4j testLog  
    2. 信息: this is a test log  
     
     2>slg4j提供的simple log
          然后,我们用slf4j-simple-1.5.10.jar替换slf4j-jdk14-1.5.10.jar,选择使用slf4j提供的simple log,输出信息如下:
    1. 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文件,内容如下:
    1. log4j.debug=true  
    2. log4j.rootLogger=DEBUG,stdout  
    3. log4j.appender.stdout=org.apache.log4j.ConsoleAppender  
    4. log4j.appender.stdout.Target=System.out  
    5. log4j.appender.stdout.layout=org.apache.log4j.PatternLayout  
    6. log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p - %m%n  
     
     我们再稍微修改一下main函数,加载一下log4j.properties,如;
    1. public static void main(String[] args) {  
    2.         System.setProperty("log4j.configuration""log4j.properties");  
    3.         TestSlf4j slf = new TestSlf4j();  
    4.         slf.testLog();  
    5. }  
     
    运行main函数,输出结果如下:
    1. log4j: Parsing for [root] with value=[DEBUG,stdout].  
    2. log4j: Level token is [DEBUG].  
    3. log4j: Category root set to DEBUG  
    4. log4j: Parsing appender named "stdout".  
    5. log4j: Parsing layout options for "stdout".  
    6. log4j: Setting property [conversionPattern] to [%d{ABSOLUTE} %5p - %m%n  ].  
    7. log4j: End of parsing for "stdout".  
    8. log4j: Setting property [target] to [System.out].  
    9. log4j: Parsed "stdout" options.  
    10. log4j: Finished configuring.  
    11. 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的源代码,大家可以参考一下,为了理解方便,我已经将代码顺序做了调整:
    1. /** 
    2.  * Return a logger named corresponding to the class passed as parameter, using 
    3.  * the statically bound {@link ILoggerFactory} instance. 
    4.  *  
    5.  * @param clazz 
    6.  *          the returned logger will be named after clazz 
    7.  * @return logger 
    8.  */  
    9. public static Logger getLogger(Class clazz) {  
    10.   return getLogger(clazz.getName());  
    11. }  
    12.   
    13.   
    14. public static Logger getLogger(String name) {  
    15.   ILoggerFactory iLoggerFactory = getILoggerFactory();  
    16.   return iLoggerFactory.getLogger(name);  
    17. }  
    18.   
    19. /** 
    20.  * Return the {@link ILoggerFactory} instance in use. 
    21.  *  
    22.  * <p> 
    23.  * ILoggerFactory instance is bound with this class at compile time. 
    24.  *  
    25.  * @return the ILoggerFactory instance in use 
    26.  */  
    27. public static ILoggerFactory getILoggerFactory() {  
    28.   if (INITIALIZATION_STATE == UNINITIALIZED) {  
    29.     INITIALIZATION_STATE = ONGOING_INITILIZATION;  
    30.     performInitialization();  
    31.   }  
    32.   switch (INITIALIZATION_STATE) {  
    33.   case SUCCESSFUL_INITILIZATION:  
    34.     return getSingleton().getLoggerFactory();  
    35.   case FAILED_INITILIZATION:  
    36.     throw new IllegalStateException(UNSUCCESSFUL_INIT_MSG);  
    37.   case ONGOING_INITILIZATION:  
    38.     // support re-entrant behavior.   
    39.     // See also http://bugzilla.slf4j.org/show_bug.cgi?id=106   
    40.     return TEMP_FACTORY;  
    41.   }  
    42.   throw new IllegalStateException("Unreachable code");  
    43. }  
    44.   
    45.   
    46. private final static void performInitialization() {  
    47.   bind();  
    48.   versionSanityCheck();  
    49.   singleImplementationSanityCheck();  
    50. }  
    51.   
    52. private final static void bind() {  
    53.   try {  
    54.     // the next line does the binding   
    55.     getSingleton();  
    56.     INITIALIZATION_STATE = SUCCESSFUL_INITILIZATION;  
    57.     emitSubstituteLoggerWarning();  
    58.   } catch (NoClassDefFoundError ncde) {  
    59.     INITIALIZATION_STATE = FAILED_INITILIZATION;  
    60.     String msg = ncde.getMessage();  
    61.     if (msg != null && msg.indexOf("org/slf4j/impl/StaticLoggerBinder") != -1) {  
    62.       Util  
    63.           .reportFailure("Failed to load class /"org.slf4j.impl.StaticLoggerBinder/".");  
    64.       Util.reportFailure("See " + NO_STATICLOGGERBINDER_URL  
    65.           + " for further details.");  
    66.     }  
    67.     throw ncde;  
    68.   } catch (Exception e) {  
    69.     INITIALIZATION_STATE = FAILED_INITILIZATION;  
    70.     // we should never get here   
    71.     Util.reportFailure("Failed to instantiate logger ["  
    72.         + getSingleton().getLoggerFactoryClassStr() + "]", e);  
    73.   }  
    74. }  
    75.   
    76.   
    77. private final static StaticLoggerBinder getSingleton() {  
    78.   if (GET_SINGLETON_METHOD == GET_SINGLETON_INEXISTENT) {  
    79.     return StaticLoggerBinder.getSingleton();  
    80.   }  
    81.   if (GET_SINGLETON_METHOD == GET_SINGLETON_EXISTS) {  
    82.     return StaticLoggerBinder.getSingleton();  
    83.   }  
    84.   try {  
    85.     StaticLoggerBinder singleton = StaticLoggerBinder.getSingleton();  
    86.     GET_SINGLETON_METHOD = GET_SINGLETON_EXISTS;  
    87.     return singleton;  
    88.   } catch (NoSuchMethodError nsme) {  
    89.     GET_SINGLETON_METHOD = GET_SINGLETON_INEXISTENT;  
    90.     return StaticLoggerBinder.getSingleton();  
    91.   }  
    92. }  
  • 相关阅读:
    lintcode-60-搜索插入位置
    lintcode-57-三数之和
    lintcode-55-比较字符串
    lintcode-52-下一个排列
    lintcode-51-上一个排列
    lintcode-49-字符大小写排序
    lintcode-47-主元素 II
    lintcode-45-最大子数组差
    lintcode-44-最小子数组
    Charles抓包
  • 原文地址:https://www.cnblogs.com/duanxz/p/2938132.html
Copyright © 2011-2022 走看看