Java日誌組件 slf4j 使用案例及原理分析

                                                               Java日誌組件 slf4j 使用案例及原理分析

一、slf4j 日誌組件介紹

      項目開發,部署運行過程中,記錄運行日誌的重要性不言而喻。在項目線上運行的過程中,日誌基本成爲我們瞭解系統運行狀態的唯一工具,因此,瞭解好Java中常見日誌組件的使用以及原理,對我們的項目開發有着重要的意義。

       在Java中,經常聽到的關於日誌的組件有很多,比如:slf4j,commons-logging,log4j,logback,slf4j-simple, jdk自帶的java.util.Logging 等等。梳理清楚這些組件之間的關係對於組件的熟練使用來說至關重要。在我們的項目逐漸複雜的過程中,我們可能會越來越多的引入第三方的開發包,這些開發包中也不可避免的會使用一些不同的日誌組件,但有沒有發現,我們的項目仍然可以正常的打印的日誌,正常的運行,不出問題,這就完全得益於Java中日誌系統的優良設計模式。

       slf4j 、commons-logging和java中的jdbc有點像,這兩個組件設計了日誌系統的通用接口,其他的日誌組件如 log4j 等是日誌功能的具體實現。說白了也就是,slf4j定義了該做什麼,並指明瞭這麼做需要遵守什麼規則,log4j這些組件實現了怎麼做,也就是該如何記錄日誌,並遵守了相關的規則。

       slf4j的全稱 Simple Logging Facade for Java(簡單日誌門面),這個組件的設計採用了Facade 設計模式,它爲Java程序提供了一個統一的日誌輸出接口,但是這個組件不提供具體的日誌實現方案,因此,如果準備讓程序記錄日誌,還必須要引入具體的實現了日誌記錄功能的組件,在這裏,以 log4j 爲例,來爲程序實現日誌記錄功能。

二、slf4j + log4j 實現程序的日誌記錄功能

   2.1 引入相關jar包

       要使用這個組合,需要引入的jar包有:slf4j-api-x.x.x.jar,log4j-x.x.x.jar,slf4j-log4j12-x.x.x.jar。slf4j-log4j12是slf4j與log4j之間的橋接包,這個包的存在是和slf4j設計方式、log4j實現思想密切相關的。

      jar 包的maven位置座標如下:

<dependency>
	<groupId>org.slf4j</groupId>
	<artifactId>slf4j-api</artifactId>
	<version>1.7.25</version>
</dependency>
<dependency>
	<groupId>log4j</groupId>
	<artifactId>log4j</artifactId>
	<version>1.2.16</version>
</dependency>
<dependency>
	<groupId>org.slf4j</groupId>
	<artifactId>slf4j-log4j12</artifactId>
	<version>1.6.4</version>
</dependency>

2.2 創建日誌配置文件( log4j.properties)

      在項目目錄下創建日誌的屬性文件 log4j.properties(名字自定義),配置好日誌文件輸出路徑,輸出級別等信息。

#定義日誌輸出的級別,輸出的位置;主要的輸出級別有:DEBUG , INFO , WARN, ERROR , FATAL, ALL。
#這裏定義了 WARN 輸出級別,比WARN級別高的ERROR、FATAL級別的日誌會輸出來,比這個級別低的不會輸出來
log4j.rootLogger= WARN, ServerDailyRollingFile, stdout

# 1,配置Appenders輸出到文件
log4j.appender.ServerDailyRollingFile=org.apache.log4j.DailyRollingFileAppender
#日期形式
log4j.appender.ServerDailyRollingFile.DatePattern='.'yyyy-MM-dd
# 日誌文件保存位置
log4j.appender.ServerDailyRollingFile.File=E:/logs/slf/slf.log
log4j.appender.ServerDailyRollingFile.layout=org.apache.log4j.PatternLayout
log4j.appender.ServerDailyRollingFile.layout.ConversionPattern=%d - %m%n
#每日新建一個日誌文件
log4j.appender.ServerDailyRollingFile.Append=true

# 2,配置Appenders輸出到Console控制檯
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH\:mm\:ss} %p [%c] %m%n

2.3 創建項目demo,輸出日誌

     使用eclipse開發工具創建一個maven項目,pom文件填寫好jar地址,log4j.properties放置於項目目錄下,創建好demo類,創建log對象,輸出日誌信息。

項目結構如下圖:

 測試demo如下,由於設置的日誌輸出級別是 WARN 級別,因此,logger.info()這個函數不會輸出任何信息:

 public static void main( String[] args )
    {
        Logger logger = LoggerFactory.getLogger(App.class);
        logger.info("slf4j項目使用了日誌系統,這裏是項目info日誌信息");
        logger.warn("警告日誌信息");
        logger.error("錯誤日誌信息輸出 {} ",new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
    }

輸出結果如下:

 

三、 slf4j 組件運行原理分析

        slf4j 組件對外提供統一的日誌對象訪問接口,log4j 這類組件會實現這個接口中的相關標準規定。獲取真正的記錄日誌的Logger對象,都是通過LoggerFactory的getLogger()方法來獲取的 :Logger logger = LoggerFactory.getLogger(App.class) 。這個方法會bind(綁定)具體的實現了slf4j組件標準的類,所有的實現了slf4j標準的類都必定有一約定好的類:org/slf4j/impl/StaticLoggerBinder.class。通過 loggerFactoryClassLoader.getResources("org/slf4j/impl/StaticLoggerBinder.class") 獲取到類的完整路徑,從而獲取到具體實現類,然後就會調用這個類對 log4j 來說就是Log4jLoggerFactory這個類的getLogger()方法獲取到真正記錄日誌的Logger對象,從而實現日誌的記錄。*** 注意,在這裏不是一定要加載一個類(App.class),但是加載一個類也是有好處的。如果類有了包聲明後,在log4j的配置文件中,可以聲明屬於某個包下的類用什麼方式來顯示日誌,或只顯示某個包下的類的日誌。

       想知道這個方法是如何一步一步獲取到不同的組件的 Logger 對象的,我們可以一步一步調試跟蹤程序的運行來查看。代碼的大致運行邏輯是這樣的:1,主程序先調用slf4j組件的方法 Logger logger = LoggerFactory.getLogger(App.class);2,調用slf4j組件的 getILoggerFactory()方法開始獲取具體的實現類log4j;3,getILoggerFactory會調用bind方法,bind會再調用findPossibleStaticLoggerBinderPathSet()方法嘗試獲取實現了標準的類,loggerFactoryClassLoader.getResources("org/slf4j/impl/StaticLoggerBinder.class") 會去classpath查找這個類,如果有,說明項目引入了具體的日誌組件,可以進行日誌的記錄,這是會返回具體的對象,否則就會提示沒有找到相關的類;4,獲取到具體對象後(這裏是Log4jLoggerFactory),會調用該對象的getLogger方法,獲取到具體的日誌對象,至此,開始輸出日誌,程序結束。

具體的函數實現,可以在調試中看的清清楚楚:

第一步:

第二步:

第三步:

第四步:

 第五步:開始綁定可能存在的實現類

第六步:搜索可能存在的實現類

 第七步:調動clasLoader的getResources()獲取這些具體的類

第八步:獲取到log4j的Log4jLoggerFactory,然後調用這個類的getLogger()方法,從而獲取Logger對象

 

到這裏之後,就是log4j做的事情了, 和slf4j沒太大關係了,再順着調試的腳步,往下面看看log4j是如何來生成這麼一個logger對象的

第九步:Log4jLoggerFactory 的getLogger()方法,這個函數會調用LogManager.getLogger()方法

第十步:Logger在日誌系統裏面由 Hierarchy 進行統一管理,如果ht中沒有存儲當前這個類的Logger,則會新建一個,如果有的話,則會進行取出,不會再重新建立一個實例。

 

 Logger對象會返回,這時候就可以調用這個對象的方法進行日誌的記錄了。至此,調試結束。

四、總結

       日誌在系統中扮演着一個非常重要的角色,因此,瞭解好常見的日誌系統,對我們的項目有很大的意義。通過日誌快速定位到問題所在,也會減少因爲某些錯誤對系統造成巨大的損失。

     瞭解 slf4j 和 log4j 的關係有利於我們加深對 Facade 設計模式的理解,加深對編程的認識。歡迎一起學習交流此方面的知識。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章