系列
SLF4J介紹
?slf4j,simple logging facade for java的縮寫,翻譯為java的簡單日志外觀。slf4j是一個(gè)開源項(xiàng)目,它提供我們一個(gè)一致的API來使用不同的日志框架(如java.util.logging,logback,log4j等)。slf4j使用戶可以在運(yùn)行時(shí)嵌入他們想使用的日志框架。從名字中可以看出,它其實(shí)使用的是facade設(shè)計(jì)模式來實(shí)現(xiàn)的。
?使用slf4j,只有一個(gè)強(qiáng)制性的依賴,就是slf4j-api-x.x.x.jar,我們在編寫代碼的時(shí)候,只會使用這個(gè)jar包里的API,應(yīng)用程序在運(yùn)行時(shí)去類路徑下查找綁定的具體日志框架,并使用該綁定的日志框架進(jìn)行實(shí)際的日志操作,如果在應(yīng)用程序的類路徑下面沒有找到合適的綁定的話,slf4j默認(rèn)使用一個(gè)沒有任何操作的實(shí)現(xiàn)。
?slf4j bindings(以slf4j-api的1.7.7版本為例)slf4j為不同的日志框架提供了不同的綁定,在slf4j提供的分發(fā)包里包含了一些綁定。
- slf4j-log4j12-x.x.x.jar是使用org.apache.log4j.Logger提供的驅(qū)動(dòng)。
- slf4j-jdk14-x.x.x.jar是使用java.util.logging提供的驅(qū)動(dòng)。
- slf4j-simple-x.x.x.jar直接綁定System.err。
- slf4j-jcl-x.x.x.jar是使用commons-logging提供的驅(qū)動(dòng)。
- logback-classic-x.x.x.jar是使用logback提供的驅(qū)動(dòng)。
SLF4J基本用法
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.30</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.30</version>
</dependency>
</dependencies>
- 使用slf4j需要依賴slf4j-api和其他日志框架(這里以slf4j-log4j12-x.x.x.jar)框架為例。
public class Demo {
public static void main(String[] args) {
LoggerFactory.getLogger(Demo.class).error("+++++++");
}
}
- 使用slf4j通過LoggerFactory.getLogger的api獲取Logger對象進(jìn)而打印日志即可。
SLF4J核心邏輯介紹
SLF4J的原理圖

- 應(yīng)用application通過slf4j橋接器橋接不同的日志框架,通過slf4j api進(jìn)行日志打印的操作。
- 從本質(zhì)上來講slf4j主要是通過不同的橋接器對接了不同的日志框架,具體的日志打印仍然由日志框架去實(shí)現(xiàn)。
SLF4J的工作流程
?SLF4J通過slf4j-api訪問橋接器slf4j-log4j12生成LoggerFactory對象,通過LoggerFactory對象的getLogger返回橋接器的LoggerAdapter對象,通過LoggerAdapter的info方法輸出日志,整體的執(zhí)行過程如下圖。

slf4j時(shí)序圖原理.png
- SLF4J的工作流程核心包含獲取Logger對象流程和打印日志流程。
- SLFTJ通過slf4j-api調(diào)用log4j橋接器slf4j-log4j12的StaticLoggerBinder生成LoggerFactory對象。
- SLFTJ通過slf4j-api的getLogger方法訪問橋接器生產(chǎn)Logger對象。
- SLFTJ通過橋接器的Logger對象實(shí)現(xiàn)日志的格式化和打印。
SLF4J的流程源碼
public final class LoggerFactory {
public static ILoggerFactory getILoggerFactory() {
if (INITIALIZATION_STATE == UNINITIALIZED) {
synchronized (LoggerFactory.class) {
if (INITIALIZATION_STATE == UNINITIALIZED) {
INITIALIZATION_STATE = ONGOING_INITIALIZATION;
// 執(zhí)行LoggerFactory的初始化過程
performInitialization();
}
}
}
switch (INITIALIZATION_STATE) {
case SUCCESSFUL_INITIALIZATION:
// 返回獲取的LoggerFactory對象
return StaticLoggerBinder.getSingleton().getLoggerFactory();
}
throw new IllegalStateException("Unreachable code");
}
}
- getILoggerFactory通過performInitialization完成橋接器LoggerFactory的初始化過程。
- 通過橋接器的StaticLoggerBinder.getSingleton().getLoggerFactory()返回LoggerFactory對象。
public final class LoggerFactory {
private final static void performInitialization() {
// 核心的bind過程
bind();
}
private final static void bind() {
try {
Set<URL> staticLoggerBinderPathSet = null;
if (!isAndroid()) {
// 查詢所有的StaticLoggerBinder的類
staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();
reportMultipleBindingAmbiguity(staticLoggerBinderPathSet);
}
// 通過靜態(tài)方法調(diào)用構(gòu)造LoggerFactory對象
StaticLoggerBinder.getSingleton();
} catch (Exception e) {
}
}
static Set<URL> findPossibleStaticLoggerBinderPathSet() {
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);
}
// 查找所有的StaticLoggerBinder類對象
while (paths.hasMoreElements()) {
URL path = paths.nextElement();
staticLoggerBinderPathSet.add(path);
}
} catch (IOException ioe) {
}
return staticLoggerBinderPathSet;
}
- performInitialization的核心邏輯是初始化slf4j橋接器的LoggerFactory對象。
- findPossibleStaticLoggerBinderPathSet加載所有橋接器的StaticLoggerBinder的類對象。
- slf4j的必須配置橋接器,存在多個(gè)橋接器的情況下會選擇其中一個(gè)橋接器進(jìn)行工作。
- slf4j的核心依賴slf4j-api和slf4j-log412橋接器,兩者必須同時(shí)使用。
SLF4J設(shè)計(jì)模式
public class StaticLoggerBinder implements LoggerFactoryBinder {
private static final StaticLoggerBinder SINGLETON = new StaticLoggerBinder();
public static final StaticLoggerBinder getSingleton() {
return SINGLETON;
}
private StaticLoggerBinder() {
loggerFactory = new Log4jLoggerFactory();
try {
@SuppressWarnings("unused")
Level level = Level.TRACE;
} catch (NoSuchFieldError nsfe) {
}
}
public ILoggerFactory getLoggerFactory() {
return loggerFactory;
}
}
- StaticLoggerBinder作為適配器的核心類,核心思想是通過單例模式返回面向接口的對象ILoggerFactory。
- 其中個(gè)人認(rèn)為可以參考的點(diǎn)在于單例的設(shè)計(jì)模式和面向接口的編程模式,SLF4J-API定義ILoggerFactory接口,各適配類具體實(shí)現(xiàn)。
public interface ILoggerFactory {
public Logger getLogger(String name);
}
public class Log4jLoggerFactory implements ILoggerFactory {
ConcurrentMap<String, Logger> loggerMap;
public Log4jLoggerFactory() {
loggerMap = new ConcurrentHashMap<String, Logger>();
}
public Logger getLogger(String name) {
Logger slf4jLogger = loggerMap.get(name);
if (slf4jLogger != null) {
return slf4jLogger;
} else {
org.apache.log4j.Logger log4jLogger;
if(name.equalsIgnoreCase(Logger.ROOT_LOGGER_NAME))
log4jLogger = LogManager.getRootLogger();
else
log4jLogger = LogManager.getLogger(name);
Logger newInstance = new Log4jLoggerAdapter(log4jLogger);
Logger oldInstance = loggerMap.putIfAbsent(name, newInstance);
return oldInstance == null ? newInstance : oldInstance;
}
}
}
- Log4jLoggerFactory實(shí)現(xiàn)接口ILoggerFactory并提供getLogger來返回Logger對象。
- getLogger內(nèi)部通過適配不同日志框架的獲取Logger的方法,以slf4j-log4j12為例適配log4j,內(nèi)部封裝LogManager.getLogger(name)來獲取Logger對象。