溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Java日志中Slf4j、Log4J、Logback的原理總結(jié)

發(fā)布時間:2021-08-27 09:15:36 來源:億速云 閱讀:175 作者:chen 欄目:編程語言

本篇內(nèi)容主要講解“Java日志中Slf4j、Log4J、Logback的原理總結(jié)”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“Java日志中Slf4j、Log4J、Logback的原理總結(jié)”吧!

1.前奏:我是在研究mybatis源碼的過程中才意識到需要搞明白日志原理這回事,因為mybatis(和一些其他開源框架,比如rocketmq)都有自己的日志系統(tǒng),他們在框架內(nèi)部都使用的是自己的日志API,那么,為什么他們不像我們平常那樣配置一個log4j呢?根本原因我也不太清楚,不過我猜測可能有這么一些理由,這些框架比較老,當初還沒有slf4j這種事實上的標準,另一方面,有一些特殊的定制化的日志。徹底研究清楚mybatis的日志系統(tǒng)之后,個人覺得這一塊設(shè)計得不太好,至少今天看來,不太優(yōu)雅,因為本來一個slf4j就能搞定所有,非得在源碼中加入自己的org.apache.ibatis.logging這個包,里面包含一些適配器,雖然代碼并不復(fù)雜,但是有點多此一舉。

2.原理:slf4j是標準,也是門面,他對用戶提供統(tǒng)一的API,而下方對接各個日志框架。這有點類似JVM,我們Java開發(fā)者使用統(tǒng)一的API,而JVM對接各個操作系統(tǒng)。嚴格意義上說slf4j自身并不提供日志具體實現(xiàn)。

Java日志中Slf4j、Log4J、Logback的原理總結(jié)

3.slf4j采用的是SPI機制,指定一個標準的目錄結(jié)構(gòu):org.slf4j.impl.StaticLoggerBinder,然第三方的框架都必須存在一個這樣的類,用于和slf4j建立關(guān)系,比如slf4j-simple.jar,logback,這兩個直接實現(xiàn)了slf4j的接口,而對于log4j這種則需要一個中間適配器slf4j-log4j12。于是乎,當調(diào)用slf4j的Logger  logger =  LoggerFactory.getLogger(XXX.class)的時候,雖然使用的是slf4j的api,但是真正輸出日志的是具體的日志框架,這樣子做的好處就是,當某一天你希望更換日志框架了,只需要把具體日志框架的jar包替換掉,不需要更改任何一行代碼,就能實現(xiàn)日志框架的切換。

4.slf4j是如何發(fā)現(xiàn)具體日志框架的,這就得意于spi機制,前面說每個日志框架都需要存在一個org.slf4j.impl.StaticLoggerBinder類,log4j則是通過中間適配器slf4j-log4j12。當調(diào)用LoggerFactory.getLogger的時候,就會去classpath中尋找StaticLoggerBinder這個類,如果不存在或者存在超過1個,那么會報錯,classpath有且只能存在一個StaticLoggerBinder類。

5.分析mybatis的日志框架:mybatis有一套屬于自己的日志系統(tǒng),日志api是:Log log =  LogFactory.getLog(xxx.class),于此同時,封裝了幾個主流的日志框架適配器,包括:SLF4J | LOG4J | LOG4J2 |  JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING,當調(diào)用Log log =  LogFactory.getLog(xxx.class)時,會初始化眾多適配器中的一個,可以在mybatis的配置文件中通過logImpl指定具體的一個,如果不指定那么默認使用SLF4J,因為這里在LogFactory類中的靜態(tài)代碼快第一個就是SLF4J:

static {  tryImplementation(LogFactory::useSlf4jLogging);  tryImplementation(LogFactory::useCommonsLogging);  tryImplementation(LogFactory::useLog4J2Logging);  tryImplementation(LogFactory::useLog4JLogging);  tryImplementation(LogFactory::useJdkLogging);  tryImplementation(LogFactory::useNoLogging);  }

假設(shè)使用默認配置,那么就會初始化Slf4jImpl類,這個類內(nèi)部有個代理log,這個代理log就是Logger logger =  LoggerFactory.getLogger(clazz),這就回歸到slf4j的標準使用方式上面來了,mybatis打印日志,其實就是代理對象在打印,而代理對象就是classpath中配置的具體日志框架。

6.分析log4j是如何與slf4j整合的:前面說到,要使用log4j就必須引入slf4j-log4j12這個jar包,而這個jar包中同樣存在一個StaticLoggerBinder類,當我們調(diào)用LoggerFactory.getLogger(clazz)的時候,同樣是初始化StaticLoggerBinder,然后調(diào)用利用ILoggerFactory創(chuàng)建一個log4j的Logger實例,代碼如下:

public class Log4jLoggerFactory implements ILoggerFactory {    // key: name (String), value: a Log4jLoggerAdapter;  ConcurrentMap<String, Logger> loggerMap;      public Log4jLoggerFactory() {  loggerMap = new ConcurrentHashMap<String, Logger>();  }    /*  * (non-Javadoc)  *   * @see org.slf4j.ILoggerFactory#getLogger(java.lang.String)  */  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;  }  }  }

最關(guān)鍵的一行就是第27行Logger newInstance = new  Log4jLoggerAdapter(log4jLogger),slf4j的Logger對象實際上是一個log4j的適配器對象(也是代理對象),當slf4j調(diào)用比如debug方法的時候,實際上是代理對象(也就是真實的log4j對象)在調(diào)用debug方法。

到此,相信大家對“Java日志中Slf4j、Log4J、Logback的原理總結(jié)”有了更深的了解,不妨來實際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進入相關(guān)頻道進行查詢,關(guān)注我們,繼續(xù)學習!

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI