Log日志框架学习-Slf4j
什么是slf4j
The Simple Logging Facade for Java (SLF4J) serves as a simple facade or abstraction for various logging frameworks (e.g. java.util.logging, logback, log4j) allowing the end user to plug in the desired logging framework at deployment time.
slf4j的全称是Simple Logging Facade for Java, 也就是简单的日志门面框架,只提供接口,没有具体的实现。具体的日志功能由具体的日志框架去实现,比如java.util.logging, logback, log4j,slf4j-simple等。
使用Slf4j有一个很大的好处是,当你想切换其他日志框架的时候,原来的代码是几乎不用改的
看一个简单的例子
引入相关的jar包,slf4j-api-xxx.jar 和 slf4j-simple-xxx.jar (这个是slf4j最简单的日志框架实现了)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.zzh.log</groupId>
<artifactId>log</artifactId>
<version>1.0-SNAPSHOT</version>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.25</version>
</dependency>
</dependencies>
</project>
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Test {
public static void main(String[] args) {
Logger logger = LoggerFactory.getLogger(Test.class);
logger.info("hahaahah");
}
}
上面就是最简单的例子了。
认识一些基础的类
-
Logger
Logger是一个接口,包含了所有的打印方法,像trace()、debug()、info()、warn()、error()等,是日志框架的主要入口。
LoggerLevel包括ERROR、WARN、INFO、DEBUG、TRACE
LoggerLevel有什么作用呢,通常默认是日志级别是DEBUG,那就会过滤logger.trace()的日志输出;如果就日志级别设置为INFO,那就会过滤logger.debug()和logger.trace()的日志输出,一次类推,ERROR是最高级别,将会过滤出logger.error()所有的日志打印。 - ILoggerFactory
public interface ILoggerFactory {
public Logger getLogger(String name);
}
实现ILoggerFactory这个接口,用来生成具体的Logger实现。
-
LoggerFactory
这个类会去类目录下去查找这个org/slf4j/impl/StaticLoggerBinder,并获得对应的ILoggerFactory, 再由ILoggerFactory去拿到对应的Logger。 -
StaticLoggerBinder
所有实现slf4j日志门面的日志框架必须实现这个类,因为在LoggerFactory这个类中写死从StaticLoggerBinder这个类中拿到ILoggerFactory。 -
Marker 中文不好翻译,Marker是日志输出更加细粒度的控制,个人理解,其实Marker就是一个过滤器,在日志级别前,将一些不需要的日志输出给过滤掉。
如果日志框架需要实现Marker的功能,需要实现MarkerFactoryBinder - MDC全称是"Mapped Diagnostic Context“,线程映射表。实际是日志框架维护的一个全局的map,存储key-value,可以将键值的信息插入到日志信息中。
public class SimpleMDC {
static public void main(String[] args) throws Exception {
// You can put values in the MDC at any time. Before anything else
// we put the first name
MDC.put("first", "Dorothy");
Logger logger = LoggerFactory.getLogger(SimpleMDC.class);
}
}
源码分析
getLogger() :入口
LoggerFactory.getLogger(Test.class);
public static Logger getLogger(Class<?> clazz) {
//1.获取对应的LOgger,接着进去看看
Logger logger = getLogger(clazz.getName());
//2. 检查日志器的名称是否匹配
if (DETECT_LOGGER_NAME_MISMATCH) {
Class<?> autoComputedCallingClass = Util.getCallingClass();
if (autoComputedCallingClass != null && nonMatchingClasses(clazz, autoComputedCallingClass)) {
Util.report(String.format("Detected logger name mismatch. Given name: \"%s\"; computed name: \"%s\".", logger.getName(),
autoComputedCallingClass.getName()));
Util.report("See " + LOGGER_NAME_MISMATCH_URL + " for an explanation");
}
}
return logger;
}
getLogger :获取Logger
public static Logger getLogger(String name) {
ILoggerFactory iLoggerFactory = getILoggerFactory(); //进去继续看
return iLoggerFactory.getLogger(name);
}
getILoggerFactory() : 获得ILoggerFactory
public static ILoggerFactory getILoggerFactory() {
// INITIALIZATION_STATE 初始化的状态
// UNINITIALIZED还没初始化,就开始初始化
if (INITIALIZATION_STATE == UNINITIALIZED) {
synchronized (LoggerFactory.class) {
if (INITIALIZATION_STATE == UNINITIALIZED) {
// 把INITIALIZATION_STATE状态设为正在初始化中
INITIALIZATION_STATE = ONGOING_INITIALIZATION;
// 初始化工作
performInitialization();
}
}
}
switch (INITIALIZATION_STATE) {
case SUCCESSFUL_INITIALIZATION:
//如果初始化成功,则通过StaticLoggerBinder这个类获取对应的ILoggerFactory,这也是上面说到为什么一定要实现StaticLoggerBinder这个接口
return StaticLoggerBinder.getSingleton().getLoggerFactory();
case NOP_FALLBACK_INITIALIZATION:
return NOP_FALLBACK_FACTORY;
case FAILED_INITIALIZATION:
throw new IllegalStateException(UNSUCCESSFUL_INIT_MSG);
case ONGOING_INITIALIZATION:
// support re-entrant behavior.
// See also http://jira.qos.ch/browse/SLF4J-97
return SUBST_FACTORY;
}
throw new IllegalStateException("Unreachable code");
}
performInitialization():进行初始化工作
private final static void performInitialization() {
//绑定
bind();
//如果初始化成功则进行版本检查
if (INITIALIZATION_STATE == SUCCESSFUL_INITIALIZATION) {
versionSanityCheck();
}
}
bind() : 绑定
private final static void bind() {
try {
//存储所有实现了staticLoggerBinder的路径
Set<URL> staticLoggerBinderPathSet = null;
// skip check under android, see also
// http://jira.qos.ch/browse/SLF4J-328
if (!isAndroid()) {
//查找实现了staticLoggerBinder的路径
staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();
//打印是否有多个实现
reportMultipleBindingAmbiguity(staticLoggerBinderPathSet);
}
// the next line does the binding
StaticLoggerBinder.getSingleton();
// 将初始化状态设为成功
INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION;
// 打印出来真实是将哪一个StaticLoggerBinder实现进行绑定
reportActualBinding(staticLoggerBinderPathSet);
fixSubstituteLoggers();
replayEvents();
// release all resources in SUBST_FACTORY
SUBST_FACTORY.clear();
} catch (NoClassDefFoundError ncde) {
//绑定失败的情况,将错误日志打印出来
String msg = ncde.getMessage();
if (messageContainsOrgSlf4jImplStaticLoggerBinder(msg)) {
INITIALIZATION_STATE = NOP_FALLBACK_INITIALIZATION;
Util.report("Failed to load class \"org.slf4j.impl.StaticLoggerBinder\".");
Util.report("Defaulting to no-operation (NOP) logger implementation");
Util.report("See " + NO_STATICLOGGERBINDER_URL + " for further details.");
} else {
failedBinding(ncde);
throw ncde;
}
} catch (java.lang.NoSuchMethodError nsme) {
//这里的异常是找不到StaticLoggerBinder的getSingleton()方法
String msg = nsme.getMessage();
if (msg != null && msg.contains("org.slf4j.impl.StaticLoggerBinder.getSingleton()")) {
INITIALIZATION_STATE = FAILED_INITIALIZATION;
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 nsme;
} catch (Exception e) {
failedBinding(e);
throw new IllegalStateException("Unexpected initialization failure", e);
}
}
** versionSanityCheck()** : 版本检查
private final static void versionSanityCheck() {
try {
//要求的版本
String requested = StaticLoggerBinder.REQUESTED_API_VERSION;
// 初始版本不匹配
boolean match = false;
//static private final String[] API_COMPATIBILITY_LIST = new String[] { "1.6", "1.7" }; 可以匹配的版本
for (String aAPI_COMPATIBILITY_LIST : API_COMPATIBILITY_LIST) {
//说明版本匹配成功
if (requested.startsWith(aAPI_COMPATIBILITY_LIST)) {
match = true;
}
}
//匹配不成功,打印错误日志
if (!match) {
Util.report("The requested version " + requested + " by your slf4j binding is not compatible with "
+ Arrays.asList(API_COMPATIBILITY_LIST).toString());
Util.report("See " + VERSION_MISMATCH + " for further details.");
}
} catch (java.lang.NoSuchFieldError nsfe) {
// given our large user base and SLF4J's commitment to backward
// compatibility, we cannot cry here. Only for implementations
// which willingly declare a REQUESTED_API_VERSION field do we
// emit compatibility warnings.
} catch (Throwable e) {
// we should never reach here
Util.report("Unexpected problem occured during version sanity check", e);
}
}
findPossibleStaticLoggerBinderPathSet() :查找实现了taticLoggerBinder的路径集合
static Set<URL> findPossibleStaticLoggerBinderPathSet() {
// use Set instead of list in order to deal with bug #138
// LinkedHashSet appropriate here because it preserves insertion order
// during iteration
// 用来存储staticLoggerBinder路径的集合
Set<URL> staticLoggerBinderPathSet = new LinkedHashSet<URL>();
try {
//拿到类加载器
ClassLoader loggerFactoryClassLoader = LoggerFactory.class.getClassLoader();
Enumeration<URL> paths;
//STATIC_LOGGER_BINDER_PATH = "org/slf4j/impl/StaticLoggerBinder.class";
if (loggerFactoryClassLoader == null) {
//如果为空,说明为当前类加载器为启动类加载器
paths = ClassLoader.getSystemResources(STATIC_LOGGER_BINDER_PATH);
} else {
//在类路径下去查找这个类
paths = loggerFactoryClassLoader.getResources(STATIC_LOGGER_BINDER_PATH);
}
//遍历
while (paths.hasMoreElements()) {
// 获取对应的URL资源
URL path = paths.nextElement();
// 将URL放入集合LinkedHashSet中
staticLoggerBinderPathSet.add(path);
}
} catch (IOException ioe) {
Util.report("Error getting resources from path", ioe);
}
return staticLoggerBinderPathSet;
}
reportMultipleBindingAmbiguity() :报告是否有多个绑定冲突
private static void reportMultipleBindingAmbiguity(Set<URL> binderPathSet) {
//判断Set集合里是否有多个StaticLoggerBinder类的URL
if (isAmbiguousStaticLoggerBinderPathSet(binderPathSet)) {
Util.report("Class path contains multiple SLF4J bindings.");
for (URL path : binderPathSet) {
Util.report("Found binding in [" + path + "]");
}
Util.report("See " + MULTIPLE_BINDINGS_URL + " for an explanation.");
}
}
reportActualBinding() : 报告实际绑定的StaticLoggerBinder
private static void reportActualBinding(Set<URL> binderPathSet) {
// binderPathSet can be null under Android
if (binderPathSet != null && isAmbiguousStaticLoggerBinderPathSet(binderPathSet)) {
Util.report("Actual binding is of type [" + StaticLoggerBinder.getSingleton().getLoggerFactoryClassStr() + "]");
}
}
哈,就这样子啦,不正之处,多多指正。