slf4j简介

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. publicclass TestSlf4j {
  9. Logger log = LoggerFactory.getLogger(TestSlf4j.class);
  10. publicvoid testLog(){
  11. log.info("this is a test log");
  12. }
  13. /**
  14. * @param args
  15. */
  16. publicstaticvoid main(String[] args) {
  17. TestSlf4j slf = new TestSlf4j();
  18. slf.testLog();
  19. }
  20. }
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函数,输出信息如下:
[javascript] view plain copy print ?
  1. 2010-1-5 21:44:47 chb.test.slf4j.TestSlf4j testLog
  2. 信息: 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,输出信息如下:
[javascript] view plain copy print ?
  1. 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文件,内容如下:
[python] view plain copy print ?
  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
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,如;
  1. publicstaticvoid main(String[] args) {
  2. System.setProperty("log4j.configuration", "log4j.properties");
  3. TestSlf4j slf = new TestSlf4j();
  4. slf.testLog();
  5. }
public static void main(String[] args) { System.setProperty("log4j.configuration", "log4j.properties"); TestSlf4j slf = new TestSlf4j(); slf.testLog(); }
运行main函数,输出结果如下:
[javascript] view plain copy print ?
  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
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的源代码,大家可以参考一下,为了理解方便,我已经将代码顺序做了调整:
  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. publicstatic Logger getLogger(Class clazz) {
  10. return getLogger(clazz.getName());
  11. }
  12. publicstatic Logger getLogger(String name) {
  13. ILoggerFactory iLoggerFactory = getILoggerFactory();
  14. return iLoggerFactory.getLogger(name);
  15. }
  16. /**
  17. * Return the {@link ILoggerFactory} instance in use.
  18. *
  19. * <p>
  20. * ILoggerFactory instance is bound with this class at compile time.
  21. *
  22. * @return the ILoggerFactory instance in use
  23. */
  24. publicstatic ILoggerFactory getILoggerFactory() {
  25. if (INITIALIZATION_STATE == UNINITIALIZED) {
  26. INITIALIZATION_STATE = ONGOING_INITILIZATION;
  27. performInitialization();
  28. }
  29. switch (INITIALIZATION_STATE) {
  30. case SUCCESSFUL_INITILIZATION:
  31. return getSingleton().getLoggerFactory();
  32. case FAILED_INITILIZATION:
  33. thrownew IllegalStateException(UNSUCCESSFUL_INIT_MSG);
  34. case ONGOING_INITILIZATION:
  35. // support re-entrant behavior.
  36. // See also http://bugzilla.slf4j.org/show_bug.cgi?id=106
  37. return TEMP_FACTORY;
  38. }
  39. thrownew IllegalStateException("Unreachable code");
  40. }
  41. privatefinalstaticvoid performInitialization() {
  42. bind();
  43. versionSanityCheck();
  44. singleImplementationSanityCheck();
  45. }
  46. privatefinalstaticvoid bind() {
  47. try {
  48. // the next line does the binding
  49. getSingleton();
  50. INITIALIZATION_STATE = SUCCESSFUL_INITILIZATION;
  51. emitSubstituteLoggerWarning();
  52. } catch (NoClassDefFoundError ncde) {
  53. INITIALIZATION_STATE = FAILED_INITILIZATION;
  54. String msg = ncde.getMessage();
  55. if (msg != null && msg.indexOf("org/slf4j/impl/StaticLoggerBinder") != -1) {
  56. Util
  57. .reportFailure("Failed to load class /"org.slf4j.impl.StaticLoggerBinder/".");
  58. Util.reportFailure("See " + NO_STATICLOGGERBINDER_URL
  59. + " for further details.");
  60. }
  61. throw ncde;
  62. } catch (Exception e) {
  63. INITIALIZATION_STATE = FAILED_INITILIZATION;
  64. // we should never get here
  65. Util.reportFailure("Failed to instantiate logger ["
  66. + getSingleton().getLoggerFactoryClassStr() + "]", e);
  67. }
  68. }
  69. privatefinalstatic StaticLoggerBinder getSingleton() {
  70. if (GET_SINGLETON_METHOD == GET_SINGLETON_INEXISTENT) {
  71. return StaticLoggerBinder.getSingleton();
  72. }
  73. if (GET_SINGLETON_METHOD == GET_SINGLETON_EXISTS) {
  74. return StaticLoggerBinder.getSingleton();
  75. }
  76. try {
  77. StaticLoggerBinder singleton = StaticLoggerBinder.getSingleton();
  78. GET_SINGLETON_METHOD = GET_SINGLETON_EXISTS;
  79. return singleton;
  80. } catch (NoSuchMethodError nsme) {
  81. GET_SINGLETON_METHOD = GET_SINGLETON_INEXISTENT;
  82. return StaticLoggerBinder.getSingleton();
  83. }
  84. }
/** * 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(); } }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值