要使用SLF4J,得包含对"org.slf4j:slf4j-api"的依赖。
二、两个简单的例子
2.1、具体日志框架是默认实现<dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.12</version> </dependency>2)Java代码片段
import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Main { public static void main(String[] args) { Logger logger = LoggerFactory.getLogger(Main.class); logger.error("Hello World"); System.out.println(logger.getClass()); } }
3)运行结果
如图1
图1
<dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.12</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-simple</artifactId> <version>1.7.12</version> </dependency>2)Java代码片段
import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Main { public static void main(String[] args) { Logger logger = LoggerFactory.getLogger(Main.class); logger.error("Hello World"); System.out.println(logger.getClass()); } }
3)运行结果
如图2
图2
import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Main { public static void main(String[] args) { Logger logger = LoggerFactory.getLogger(Main.class); logger.error("Hello World"); System.out.println(logger.getClass()); } }在不使用适配器类方案中,logger指向的类实例A独立实现日志操作功能;在使用适配器类方案中,logger指向的类实例A中包含有另外的日志框架中的日志记录类实例B,A的日志操作功能的实现是通过委托给B来实现的。
<dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.12</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.0.13</version> </dependency>以上代码中的logger指向的类实例A为"ch.qos.logback.classic.Logger"的实例对象,A独立实现日志操作功能
"SLF4J+Simple"方案和"SLF4J+NOP"方案都是以上这种类型。
<dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.12</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.12</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency>以上代码中的logger指向的类实例A为"org.slf4j.impl.Log4jLoggerAdapter"的实例对象,A中包含一个"org.apache.log4j.Logger"类实例B,A的日志操作委托给B来实现
<dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.12</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-jcl</artifactId> <version>1.7.12</version> </dependency> <!--Apache Commons Logging就是JCL--> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.1</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency>而根据 《Java日志框架——JCL》在"SLF4J+SLF4J-JCL+JCL+Log4J"方案中,以上代码中的logger指向的类实例A为"org.slf4j.impl.JCLLoggerAdapter"的实例对象,A中包含一个"org.apache.commons.logging.impl.Log4JLogger"类实例B,实例B中包含一个"org.apache.log4j.Logger“类实例C,A的日志操作委托给B来实现,B又委托给C来实现
”SLF4J+SLF4J-Jdk14+Java Logging API“方案也是以上这种类型
3.3、绑定关系图示
如图3
图3
<dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.12</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.12</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency>通过查看"org.slf4j:slf4j-log4j12:1.7.12"的pom.xml,可以发现其中有如下片段
<dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> </dependency>也就是说"org.slf4j:slf4j-log4j12:1.7.12"中含有对"org.slf4j:slf4j-api"和"log4j:log4j"的依赖,其实想想也是,在"org.slf4j:slf4j-log4j12:1.7.12"中的"org.slf4j.impl.Log4jLoggerAdapter"类既要继承实现"org.slf4j:slf4j-api"中的"org.slf4j.Logger"类,也要包含"log4j:log4j"中的"org.apache.log4j.Logger"类实例作为完成实际日志操作功能的对象,因而"org.slf4j:slf4j-log4j12:1.7.12"自然得包含对这两个包的依赖
<dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.12</version> </dependency>
但是为了更好的可读性和更高的掌控度,还是使用刚开始的完整的声明方案比较好。
五、SLF4J绑定具体的日志框架的原理
根据"2.2、具体日志框架是slf4j-simple"中的简单例子来进行说明。
执行"Logger logger = LoggerFactory.getLogger(Main.class);"语句的时候发生了什么?
1)查看"org.slf4j.LoggerFactory"的源代码可以发现,其中有如下这些方法
public static Logger getLogger(Class<?> clazz) { Logger logger = getLogger(clazz.getName()); if(DETECT_LOGGER_NAME_MISMATCH) { Class autoComputedCallingClass = Util.getCallingClass(); if(nonMatchingClasses(clazz, autoComputedCallingClass)) { Util.report(String.format("Detected logger name mismatch. Given name: \"%s\"; computed name: \"%s\".", new Object[]{logger.getName(), autoComputedCallingClass.getName()})); Util.report("See http://www.slf4j.org/codes.html#loggerNameMismatch for an explanation"); } } return logger; }
public static Logger getLogger(String name) { ILoggerFactory iLoggerFactory = getILoggerFactory(); return iLoggerFactory.getLogger(name); }
public static ILoggerFactory getILoggerFactory() { if(INITIALIZATION_STATE == 0) { INITIALIZATION_STATE = 1; performInitialization(); } switch(INITIALIZATION_STATE) { case 1: return TEMP_FACTORY; case 2: throw new IllegalStateException("org.slf4j.LoggerFactory could not be successfully initialized. See also http://www.slf4j.org/codes.html#unsuccessfulInit"); case 3: return StaticLoggerBinder.getSingleton().getLoggerFactory(); case 4: return NOP_FALLBACK_FACTORY; default: throw new IllegalStateException("Unreachable code"); } }
private static final void performInitialization() { bind(); if(INITIALIZATION_STATE == 3) { versionSanityCheck(); } }
private static final void bind() { String msg; try { Set e = findPossibleStaticLoggerBinderPathSet(); reportMultipleBindingAmbiguity(e); StaticLoggerBinder.getSingleton(); INITIALIZATION_STATE = 3; reportActualBinding(e); fixSubstitutedLoggers(); } catch (NoClassDefFoundError var2) { msg = var2.getMessage(); if(!messageContainsOrgSlf4jImplStaticLoggerBinder(msg)) { failedBinding(var2); throw var2; } INITIALIZATION_STATE = 4; Util.report("Failed to load class \"org.slf4j.impl.StaticLoggerBinder\"."); Util.report("Defaulting to no-operation (NOP) logger implementation"); Util.report("See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details."); } catch (NoSuchMethodError var3) { msg = var3.getMessage(); if(msg != null && msg.indexOf("org.slf4j.impl.StaticLoggerBinder.getSingleton()") != -1) { INITIALIZATION_STATE = 2; Util.report("slf4j-api 1.6.x (or later) is incompatible with this binding."); Util.report("Your binding is version 1.5.5 or earlier."); Util.report("Upgrade your binding to version 1.6.x."); } throw var3; } catch (Exception var4) { failedBinding(var4); throw new IllegalStateException("Unexpected initialization failure", var4); } }
依次调用执行的顺序为
Logger getLogger(Class<?> clazz)()4、在图03中的"native implementaion"和"non-native implementation"不是以”“是否以非Java语言实现”为区分标准,而是以“是否直接继承'org.slf4j.Logger'类”为区分标准
5、现在流行的日志框架解决方案是:SLF4J+Logback