Slf4J作为一种日志门面,拥有极其强大的整合能力,可以与当前流行的Log4J2、Logback完美的融合在一起,
很多初学者不明白为什么要使用日志门面,需要打印什么直接使用日志实现不就可以了吗?
开发者可以通过Slf4J提供的同一套接口去使用不同的日志实现,可能很多萌新还是不太明白这句话的意思,我可以打个简单的比方,现在有这么一个场景,小明同学很喜欢吃棒棒糖,每天都会去隔壁超市买一根棒棒糖,超市每次进货都会在不同的厂家之间选成本最低的一个,超市可以频繁更换厂家,只需要保证能进到相同的棒棒糖就可以了,但是对于小明来说,他并不关心超市的进货厂家,只知道每天都可以拿同样的钱买到同样的棒棒糖,即使厂家在怎么变化,小明也不需要更换任何购买方式。在这里小明可以比作开发者,买棒棒糖其实就是开发者打印日志,超市就是日志门面,厂家是日志实现,开发者不需要关心日志实现是Log4J2还是Logback或者是其他,开发者只需要使用相同的方法进行日志的打印就可以了,即使底层的日志实现更换掉了,开发者也不需要改动任何代码。
通过slf4j中LoggerFactory类提供的getLogger(Class> clazz)()接口获取一个Logger对象,我这里得到的是logback的对象,因为我在项目中引用的是logback实现。下面是demo代码和pom依赖。
public class TestLogback {
public static void main(String[] args) {
//获取一个Logger对象
Logger logger = LoggerFactory.getLogger(TestLogback.class);
logger.trace("trace");
logger.debug("debug");
logger.info("info");
logger.warn("warn");
logger.error("error");
}
}
<dependencies>
<dependency>
<groupId>ch.qos.logbackgroupId>
<artifactId>logback-classicartifactId>
<version>1.3.0-alpha12version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
<scope>testscope>
dependency>
dependencies>
进入到getLogger(Class> clazz)()方法中,继续往下层跟
public static Logger getLogger(Class<?> clazz) {
//这里继续往下跟
Logger logger = getLogger(clazz.getName());
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;
}
进入上面这个方法之后,可以看到获取Logger对象是分两部,首先获得一个日志工厂接口ILoggerFactory的实现类对象,然后通过日志工厂实现类对象得到对应的Logger。这里的重点是这个日志工厂实现类对象是怎么得到的,也就第一行代码,继续跟进。
public static Logger getLogger(String name) {
//获取ILoggerFactory的实现类对象
ILoggerFactory iLoggerFactory = getILoggerFactory();
//通过工厂类实现对象获得Logger
return iLoggerFactory.getLogger(name);
}
进入getILoggerFactory()方法之后,看到这里获取ILoggerFactory的实现类对象也是分两部进行的,
public static ILoggerFactory getILoggerFactory() {
return getProvider().getLoggerFactory();
}
进入getProvider()方法之后,可以看到底层使用静态变量INITIALIZATION_STATE标记初始化状态,该变量有五种状态,最初状态为UNINITIALIZED(尚未初始化),
系统则会进入*performInitialization()*方法进行初始化;
static SLF4JServiceProvider getProvider() {
//INITIALIZATION_STATE为初始化状态,有五种状态如下:
//UNINITIALIZED = 0;//尚未初始化
//ONGOING_INITIALIZATION = 1;//正在初始化
//FAILED_INITIALIZATION = 2;//初始化失败
//SUCCESSFUL_INITIALIZATION = 3;//初始化成功,这意味着slf4j找到了一种日志实现
//NOP_FALLBACK_INITIALIZATION = 4;//没有日志实现
if (INITIALIZATION_STATE == UNINITIALIZED) {
synchronized (LoggerFactory.class) {
if (INITIALIZATION_STATE == UNINITIALIZED) {
INITIALIZATION_STATE = ONGOING_INITIALIZATION;
//如果没有初始化,则执行performInitialization()进行初始化
//使用的日志实现是Logback还是Log4j或者是其他,主要就是在这一步
performInitialization();
}
}
}
//如果已经初始化完成,则执行直接返回SLF4JServiceProvider的对象
switch (INITIALIZATION_STATE) {
case SUCCESSFUL_INITIALIZATION:
return PROVIDER;
//没有使用其他日志实现,则返回一个空实现
case NOP_FALLBACK_INITIALIZATION:
return NOP_FALLBACK_SERVICE_PROVIDER;
case FAILED_INITIALIZATION:
throw new IllegalStateException(UNSUCCESSFUL_INIT_MSG);
case ONGOING_INITIALIZATION:
return SUBST_PROVIDER;
}
throw new IllegalStateException("Unreachable code");
}
进入*performInitialization()方法后可以看到一个bind()方法,这个方法就是去给日志门面绑定一个日志实现,具体内容,稍后再看,完成绑定之后,会执行versionSanityCheck()*方法校验日志实现的版本使得否与当前的日志门面版本相符合。
private final static void performInitialization() {
bind();
if (INITIALIZATION_STATE == SUCCESSFUL_INITIALIZATION) {
versionSanityCheck();
}
}
下面来看上面说到的*bind()*方法。
private final static void bind() {
try {
//查询当前系统加载到的日志实现列表
List<SLF4JServiceProvider> providersList = findServiceProviders();
//这个方法的作用是检测日志实现是否为多个,如果是则打印警告信息
reportMultipleBindingAmbiguity(providersList);
if (providersList != null && !providersList.isEmpty()) {
//如果有多个日志实现,则使用第一个。
PROVIDER = providersList.get(0);
//该方法则是slf4j定义的api,需要日志实现框架实现该方法。目的就是对日志实现进行初始化工作,这里不是slf4j的工作,就不进行详细分析了。
PROVIDER.initialize();
//初始化完成之后,把状态标记为初始化成功
INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION;
//打印当前使用的日志实现框架
reportActualBinding(providersList);
} else {
/*
这里我没有看明白,希望大佬有可以留言指导
*/
INITIALIZATION_STATE = NOP_FALLBACK_INITIALIZATION;
Util.report("No SLF4J providers were found.");
Util.report("Defaulting to no-operation (NOP) logger implementation");
Util.report("See " + NO_PROVIDERS_URL + " for further details.");
Set<URL> staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();
reportIgnoredStaticLoggerBinders(staticLoggerBinderPathSet);
}
postBindCleanUp();
} catch (Exception e) {
failedBinding(e);
throw new IllegalStateException("Unexpected initialization failure", e);
}
}
Up();
} catch (Exception e) {
failedBinding(e);
throw new IllegalStateException("Unexpected initialization failure", e);
}
}