RM新时代网站-首页

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

奇怪!應(yīng)用的日志呢??

京東云 ? 來源:jf_75140285 ? 作者:jf_75140285 ? 2024-06-11 10:48 ? 次閱讀

1. 問題回顧

問題背景是在進行中臺應(yīng)用中間件遷移過程中,發(fā)現(xiàn)存在項目啟動失敗或者項目正常啟動(jsf正常掛載并正常運行,mq正常發(fā)送和消費)但是無任何日志打印現(xiàn)象。更奇怪的是不打印日志竟然是偶發(fā)的,在測試環(huán)境中多次部署都未出現(xiàn)項目啟動但無日志打印情況,而且玄學(xué)的是生產(chǎn)環(huán)境兩臺機器,其中一臺正常日志打印,另一臺無任何日志打印(應(yīng)用運行正常)。

通過多次重啟無日志打印機器仍未恢復(fù)日志打印,最終通過排查發(fā)現(xiàn)問題在于項目中引入的多個日志jar包沖突,進而導(dǎo)致無日志打印現(xiàn)象。

wKgaomZnuv2ABNQvAAXZtvdNTDo518.png

wKgZomZnuv6ADd4kAACVaeTQVZk678.png

圖1 場景1項目啟動失敗和場景2項目目正常啟動但是無日志打印

wKgaomZnuv-AHrAXAAAZi-CsEWg559.png

圖2 運行項目所包含的日志jar包

2. 日志框架

日志框架通常分為兩大類:

?

日志門面

(Logging Facade):如SLF4J(Simple Logging Facade for Java)和JCL(Apache Commons Logging),它們提供了一層抽象接口,使得開發(fā)者可以編寫與具體日志實現(xiàn)無關(guān)的代碼。這樣在不修改代碼的情況下,可以靈活地切換底層的日志實現(xiàn)框架。?

日志實現(xiàn)

(Logging Implementation):如Logback、Log4j、java.util.logging (JUL)等,它們是具體的日志庫,負責(zé)實際的日志生成、處理和存儲工作。這些實現(xiàn)直接響應(yīng)門面層的請求,執(zhí)行日志操作。

wKgZomZnuwKAYOQCAAC2t_uJH0s464.png

圖3 日志門面和日志實現(xiàn)

日志門面使用到了一種設(shè)計模式:門面模式,接下來簡單介紹下門面模式。下面是門面模式的一個典型調(diào)用過程,其核心為外部與一個子系統(tǒng)的通信必須通過一個統(tǒng)一的外觀對象進行,使得子系統(tǒng)更易于使用。 下圖中客戶端不需要直接調(diào)用幾個子系統(tǒng),只需要與統(tǒng)一的門面進行通信即可。

wKgZomZnuwSAM1V9AADtgT0D7_U549.png

圖4 門面模式的一個典型調(diào)用過程

門面模式的核心為Facade即門面對象,核心為幾個點:

?知道所有子角色的功能和責(zé)任。?將客戶端發(fā)來的請求委派到子系統(tǒng)中,沒有實際業(yè)務(wù)邏輯。?不參與子系統(tǒng)內(nèi)業(yè)務(wù)邏輯的實現(xiàn)。

舉個栗子

當你通過電話給商店下達訂單時, 接線員就是該商店的所有服務(wù)和部門的外觀。 接線員為你提供了一個同購物系統(tǒng)、 支付網(wǎng)關(guān)和各種送貨服務(wù)進行互動的簡單語音接口。

wKgaomZnuwWAf3fsAADZo_XhVD8308.png

注:具體想要了解門面模式的可以參看這篇文章。?

2.1 為什么要引入日志門面?

回答這個問題之前,我們先看看如果需要用上面幾個日志框架來打印日志,一般怎么做,具體代碼如下:

// 使用log4j,需要log4j.jar

import org.apache.log4j.Logger;

Logger logger_log4j = Logger.getLogger(Test.class);

logger_log4j.info("Hello World!");

// 使用log4j2,需要log4j-api.jar、log4j-core.jar

import org.apache.logging.log4j.LogManager;

import org.apache.logging.log4j.Logger;

Logger logger_log4j2 = LogManager.getLogger(Test.class);

logger_log4j2.info("Hello World!");

// logback,需要logback-classic.jar、logback-core.jar

import ch.qos.logback.classic.Logger;

import ch.qos.logback.classic.LoggerContext;

Logger logger_logback = new LoggerContext().getLogger(Test.class);

logger_logback.info("Hello World!");

從上面不難看出,使用不同的日志框架需要要引入不同的jar包,使用不同的代碼獲取Logger。如果項目升級需要更換不同的框架,那么就需要修改所有的地方來獲取新的Logger,這將會產(chǎn)生巨大的工作量。

基于此,我們需要一種接口來將不同的日志框架的使用統(tǒng)一起來,這也是為什么要使用SLF4J的原因。

日志門面——SLF4J

即簡單日志門面(Simple Logging Facade for Java),不是具體的日志解決方案,它只服務(wù)于各種各樣的日志系統(tǒng)。按照官方的說法,SLF4J是一個用于日志系統(tǒng)的簡單Facade,允許最終用戶在部署其應(yīng)用時使用其所希望的日志系統(tǒng)。

另一個常用的日志門面——JCL

常見的日志門面還有一個叫JCL(Jakarta Common logging),這個是在2001年左右旨在解決日志實現(xiàn)多樣性的問題,允許開發(fā)者編寫與具體日志實現(xiàn)無關(guān)的代碼,并作為第一個廣泛使用的日志門面被提出了。其中SLF4J日志門面是在2006年,由Ceki Gülcü,同時也是Log4j的創(chuàng)始人,推出了SLF4J,這是一個更為先進、設(shè)計更優(yōu)的日志門面,旨在克服JCL存在的問題,如類加載沖突和運行時綁定的不確定性。

2.2 SLF4J和JCL的主要區(qū)別?

1. 動態(tài)與靜態(tài)綁定

?JCL:采用

動態(tài)綁定

機制,意味著它在

運行時

通過類加載器查找并決定使用哪個日志實現(xiàn)(如Log4j、JUL等)。這種方式

可能導(dǎo)致類加載順序

問題,尤其是在類路徑復(fù)雜的應(yīng)用中,可能會引起

不確定性和潛在的類加載沖突

。?SLF4J:提倡

靜態(tài)綁

定,

即在編譯時就確定日志實現(xiàn)

。SLF4J要求

在類路徑中明確包含一個到具體日志實現(xiàn)的橋接器

(如slf4j-log4j12.jar),這樣在編譯時就能確切知道日志將如何被處理。這減少了運行時的不確定性,提高了性能,并且在日志實現(xiàn)未正確配置時能給出更明確的錯誤提示。

2. 錯誤處理與診斷

?JCL:如果日志實現(xiàn)沒有正確配置,可能會導(dǎo)致難以診斷的錯誤,比如

NoClassDefFoundError

ClassNotFoundException

,因為JCL在

運行時

才會發(fā)現(xiàn)日志實現(xiàn)不可用。?SLF4J:在

初始化

時,如果發(fā)現(xiàn)

不兼容的或缺失

的日志實現(xiàn),SLF4J會立即拋出一個明確的

警告或錯誤信息

,幫助開發(fā)者快速定位問題。

3. 性能

?SLF4J:通常被認為比JCL有更高的性能,尤其是當使用靜態(tài)綁定時,因為減少了解析和查找日志實現(xiàn)的開銷。

4. API設(shè)計

?SLF4J:提供了更簡潔、更易用的API,支持更靈活的日志級別控制和參數(shù)化日志消息,有助于減少字符串拼接的開銷。

5. 社區(qū)與支持、更新與活躍度

?SLF4J:隨著時間的推移,SLF4J因其設(shè)計優(yōu)勢獲得了更廣泛的社區(qū)支持和采納,許多現(xiàn)代的Java庫和框架直接支持或推薦使用SLF4J。?SLF4J:相比JCL,SLF4J持續(xù)得到維護和更新,提供了對新特性和日志實現(xiàn)更好的支持。

綜上所述,SLF4J在設(shè)計上克服了JCL的一些缺陷,提供了更穩(wěn)定、高效和易于使用的日志接口,因此在新項目中更受推崇。而JCL盡管仍在一些遺留系統(tǒng)中使用,但已逐漸被SLF4J取代。

2.3 常用的日志實現(xiàn)

一些趣聞

使用過Log4JLogBack的同學(xué)肯定能發(fā)現(xiàn),這兩個框架的設(shè)計理念極為相似,使用方法也如出一轍。其實這個兩個框架的作者都是一個人,Ceki Gülcü,土耳其軟件工程師。 Log4J 最初是基于Java開發(fā)的日志框架,發(fā)展一段時間后,作者Ceki Gülcü將Log4j捐獻給了Apache軟件基金會,使之成為了Apache日志服務(wù)的一個子項目。 又由于Log4J出色的表現(xiàn),后續(xù)又被孵化出了支持C, C++, C#, Perl, Python, Ruby等語言的子框架。 然而,偉大的程序員好像都比較有個性。Ceki Gülcü由于不滿Apache對Log4J的管理,決定不再參加Log4J的開發(fā)維護?!俺鲎摺焙蟮腃eki Gülcü另起爐灶,開發(fā)出了LogBack這個框架(SLF4J是和LogBack一起開發(fā)出來的)。LogBack改進了很多Log4J的缺點,在性能上有了很大的提升,同時使用方式幾乎和Log4J一樣,許多用戶開始慢慢開始使用LogBack。 由于受到LogBack的沖擊,Log4J開始式微。終于,2015年9月,Apache軟件基金業(yè)宣布,Log4j不再維護,建議所有相關(guān)項目升級到Log4j2。Log4J2是Apache開發(fā)的一個新的日志框架,改進了很多Log4J的缺點,同時也借鑒了LogBack,號稱在性能上也是完勝LogBack。性能這塊后續(xù)我會仔細分析。

根據(jù)這些日志實現(xiàn)的出現(xiàn)順序及特點整理出了一條時間線如下:

1999年: Log4j 1.x:由Ceki Gülcü(土耳其裔美國軟件工程師)創(chuàng)建,成為Java社區(qū)廣泛采用的第一個流行日志框架。它的出現(xiàn)使得開發(fā)者能夠更方便地控制日志記錄,包括日志級別、輸出格式和目的地。

2001年: JUL (Java Util Logging):隨著Java 1.4的發(fā)布,Oracle(當時是Sun Microsystems)引入了JUL作為標準的日志庫。雖然它是一個內(nèi)置的解決方案,但由于API相對復(fù)雜,開發(fā)者普遍認為它不如Log4j好用。

2003年: JCL (Apache Commons Logging):Apache軟件基金會推出JCL,作為日志門面,旨在提供一個統(tǒng)一的API,使得開發(fā)者可以編寫與具體日志實現(xiàn)無關(guān)的代碼。然而,JCL在運行時動態(tài)加載日志實現(xiàn)的方式導(dǎo)致了類加載問題和性能問題。

2006年: SLF4J (Simple Logging Facade for Java):Ceki Gülcü,也是Log4j的創(chuàng)建者,推出了SLF4J,作為對JCL的改進。SLF4J強調(diào)靜態(tài)綁定,提高了性能和穩(wěn)定性,并且支持更多的日志實現(xiàn),如Logback、Log4j 1.x等。

2007年: Logback:Ceki Gülcü同時推出了Logback,作為Log4j的替代,設(shè)計為SLF4J的首選實現(xiàn)。Logback提供了更高效、更靈活的日志記錄功能,包括異步日志記錄和豐富的配置選項。

2010年: Log4j 2.x:Apache Log4j項目在2010年代進行了重大升級,推出了Log4j 2,它修復(fù)了Log4j 1.x的一些問題,提供了更好的性能和更多特性,如異步日志記錄和更強大的配置能力。

2010年至今: 微服務(wù)和云原生日志:隨著微服務(wù)和云原生應(yīng)用的興起,日志收集和分析的需求變得更加復(fù)雜。工具如Loggly、Logstash、Fluentd、Elasticsearch、Kibana等開始流行,它們與各種日志實現(xiàn)配合,提供了日志的集中處理、搜索、分析和可視化。 現(xiàn)代輕量級日志框架:

TinyLog:針對簡單應(yīng)用和資源受限環(huán)境,輕量級的日志框架如TinyLog應(yīng)運而生,提供簡單易用的API,注重效率和小巧。

注:對TinyLog感興趣可參考這篇文章。?

wKgZomZnuwiAHwOWAABnWeRNRKU695.png

圖5 日志演變路線

3. 日志門面和日志實現(xiàn)結(jié)合

3.1 日志門面如何和日志實現(xiàn)結(jié)合使用呢?

以比較常用的SLF4J為例,并結(jié)合現(xiàn)有比較常用的日志實現(xiàn)可歸納出以下幾種組合依賴結(jié)構(gòu)(如圖6),即SLF4J綁定到具體日志實現(xiàn)時需要引入的jar包依賴。圖6最下方給出的不同顏色的含義,分別是抽象接口原生支持SLF4J的實現(xiàn)、適配層、非原生支持SLF4J的實現(xiàn)。

1.抽象接口層都是slf4j-api,很好理解,因為slf4j主要就是做日志門面。2.原生支持SLF4J的實現(xiàn):有l(wèi)ogback、slf4j-simple.jar、slf4j-nop.jar。3.非原生支持SLF4J的實現(xiàn),有l(wèi)og4j和jul,因為這兩個在SLF4J之前就出現(xiàn)了,后面SLF4j出現(xiàn)后,大家覺得這個日志門面很優(yōu)秀,所以出現(xiàn)了適配SLF4J和log4j、jul的橋接包,也就是下圖中的slf4j-reload4j.jar和slf4j-jdk14.jar4.log4j2是最后出現(xiàn)的,可以說吸取了前面一些日志框架的優(yōu)點,自成一體,所以未在下面的圖中出現(xiàn)。當然SLF4J和log4j2也可以搭配,使用log4j-slf4j-impl的橋接包。

注:logback、slf4j-simple.jar、slf4j-nop.jar之所以能天然支持SLF4J的接口是有原因的,slf4j-simple.jar、slf4j-nop.jar都是slf4j自帶的實現(xiàn)框架,本身就是按slf4j-api的接口開發(fā)的。logback之所以也天然適配SLF4J,有兩個原因,一是出現(xiàn)的先后原因,log4j ->JUL->JCL-> SLF4J -> logback -> log4j2,logback在SLF4J后面出現(xiàn),第二個是因為這兩個都是同一個作者寫的。

wKgZomZnuwqASEH0ABK--oKexVo012.png

圖6 SLF4J與日志實現(xiàn)結(jié)構(gòu)圖

總結(jié)來說主要的日志門面和日志實現(xiàn)的依賴搭配如下:

?

slf4j + logback

: slf4j-api.jar + logback-classic.jar + logback-core.jar?

slf4j + log4j 1.

x : slf4j-api.jar +

slf4j-log412.jar

+ log4j.jar?

slf4j + jul

: slf4j-api.jar + slf4j-jdk14.jar?

slf4j無日志實現(xiàn)

:slf4j-api.jar + slf4j-nop.jar

(日志不會被記錄:適合調(diào)試和測試環(huán)境,避免不必要的輸出)

注意到這里沒有l(wèi)og4j2依賴jar的關(guān)系,和log4j2配合需要導(dǎo)入log4j2的log4j-api.jar、log4j-core.jar和橋接包log4j-slf4j-impl.jar。

?

slf4j + log4j 2.x

:slf4j-api.jar + log4j-api.jar + log4j-core.jar + log4j-slf4j-impl.jar?

log4j 2.x

: log4j-core + log4j-api

(log4j 2.x 可單獨使用)

3.2 什么是橋接包?

聊起橋接包,需要回顧下之前提到的SLF4J。SLF4J通過定義一套API,使得應(yīng)用程序可以在不依賴具體日志實現(xiàn)的情況下進行日志記錄。為了實現(xiàn)這一目標,SLF4J引入了StaticLoggerBinder這個關(guān)鍵組件。

StaticLoggerBinder是SLF4J API與底層日志實現(xiàn)之間的一個接口,它是一個單例類,負責(zé)在運行時返回日志實現(xiàn)的LoggerFactory實例。這個類的存在使得SLF4J能夠在不直接引用具體日志庫的情況下,依然能夠找到并使用正確的日志實現(xiàn)。

橋接包(Bridge Package)的作用是解決已有代碼依賴特定日志框架(如Log4j 1.x)與SLF4J之間的兼容性問題。例如,slf4j-log4j12.jar橋接包包含了SLF4J的StaticLoggerBinder實現(xiàn),這個實現(xiàn)將SLF4J的調(diào)用適配到Log4j 1.x的API上。這意味著即使代碼中使用了SLF4J API,日志記錄仍然可以通過Log4j 1.x來完成。

對于支持SLF4J的日志實現(xiàn),如Logback和Log4j 2.x,它們自身就提供了StaticLoggerBinder的實現(xiàn)。例如,Logback的logback-classic.jar和Log4j 2.x的log4j-slf4j-impl.jar模塊,都包含了一個符合SLF4J規(guī)范的StaticLoggerBinder,使得它們可以直接作為SLF4J的實現(xiàn)。因此不需要額外的橋接包,SLF4J能夠識別并使用這些日志實現(xiàn)進行日志記錄。

總之,橋接包確保了SLF4J與傳統(tǒng)日志框架之間的兼容性,而StaticLoggerBinder則是SLF4J實現(xiàn)其核心功能的關(guān)鍵,即在運行時找到并使用正確的日志實現(xiàn)。

注:具體有關(guān)StaticLoggerBinder底層實現(xiàn)可參考這篇文章。?

常用的橋接包:如使用SLF4J的API進行編程,底層想使用log4j1來進行實際的日志輸出,這就是slf4j-log4j12干的事。

?

slf4j-jdk14

: 讓SLF4J使用Java內(nèi)置的日志系統(tǒng)(JUL)。?

slf4j-log4j12

: 將SLF4J與Log4j 1.x綁定。?

log4j-slf4j-impl

: 綁定SLF4J到Log4j 2。?

logback-classi

c: SLF4J的實現(xiàn),使用Logback作為日志引擎。?

slf4j-jcl

: 橋接SLF4J到Apache Commons Logging。

3.3 如何從其它日志實現(xiàn)/門面到SLF4J呢?

其實大致的實現(xiàn)就是兩步,一是選擇SLF4J和具體實現(xiàn),二是兼容舊的日志實現(xiàn)/門面到SLF4J。

例如項目之前是用的JCL的API,不可能因為要換一個日志框架,把原先的日志代碼都改掉吧(API的方法不一樣,入?yún)⒑褪褂梅椒ㄒ膊灰粯樱?,這個代價太大。 我們希望的是,原有的日志代碼可以不動,后續(xù)的代碼可以用新的SLF4J的API,橋接包就是為了達到這樣的效果。具體操作就三步:1、移除掉舊的日志依賴2、引入SLF4J提供的橋接依賴3、項目中引入SLF4J和新的日志實現(xiàn)。

wKgaomZnuwqAeJQDAAI9uRaDbss368.png

wKgZomZnuw6AAZZ7AAgXeBCiGN0816.png

圖7 SLF4J相關(guān)橋接包依賴

場景介紹:如 使用log4j1的API進行編程,但是想最終通過logback來進行輸出,所以就需要先將log4j1的日志輸出轉(zhuǎn)交給slf4j來輸出,slf4j 再交給logback來輸出。將log4j1的輸出轉(zhuǎn)給slf4j,這就是log4j-over-slf4j做的事。

?

jul-to-slf4j

:jdk-logging到slf4j的橋梁,將jul的日志輸出切換到slf4j。?

log4j-over-slf4

j:log4j1到slf4j的橋梁,將log4j1的日志輸出切換到slf4j。?

jcl-over-slf4

j:commons-logging到slf4j的橋梁,將commons-logging的底層日志輸出切換到slf4j。

注:更詳細的SLF4J和不同日志實現(xiàn)的搭配以及各個日志系統(tǒng)之間的切換所需引用的具體jar包可參考這篇文章。?

3.4 橋接包導(dǎo)致的沖突

場景1:jcl-over-slf4j 與 slf4j-jcl 沖突

?

jcl-over-slf4j

: 這個橋接器的作用是將Apache Commons Logging(JCL)的日志調(diào)用轉(zhuǎn)換為SLF4J API。

如果你的代碼或依賴項使用了JCL API,但你希望統(tǒng)一日志處理并利用SLF4J的靈活性,可以引入jcl-over-slf4j。這將使得JCL的日志記錄調(diào)用被重定向到SLF4J,從而可以選擇和配置任何SLF4J兼容的日志實現(xiàn)。

?

slf4j-jcl

: 這個橋接器則是將SLF4J API的調(diào)用橋接到Apache Commons Logging。

如果你的項目中使用了SLF4J,但希望日志輸出通過Commons Logging處理,可以使用slf4j-jcl。這將SLF4J的日志調(diào)用映射到JCL,使得你的日志記錄通過Commons Logging的實現(xiàn)進行。

如果這兩者共存的話,必然造成相互委托,造成內(nèi)存溢出

場景2:log4j-over-slf4j 與 slf4j-log4j12 沖突

?l

og4j-over-slf4j

: 這個庫的目的是將Log4j 1.x的日志API調(diào)用重定向到SLF4J API。

如果你的應(yīng)用程序原本使用Log4j 1.x進行日志記錄,但你想利用SLF4J的靈活性,可以選擇使用log4j-over-slf4j。它會模擬Log4j的API,使得Log4j的配置和調(diào)用能夠透明地轉(zhuǎn)換為SLF4J,這樣你就可以在運行時使用任何SLF4J兼容的日志實現(xiàn),如Logback或Log4j 2。

?

slf4j-log4j12

: 這個橋接器將SLF4J API的調(diào)用綁定到Log4j 1.x實現(xiàn)。

如果你的項目使用了SLF4J API,但希望日志輸出通過Log4j 1.x處理,那么可以引入slf4j-log4j12。這樣,所有的SLF4J調(diào)用都會被轉(zhuǎn)換為Log4j的具體操作。

如果這兩者共存的話,理論上必然造成相互委托,造成內(nèi)存溢出。但是log4j-over-slf4內(nèi)部做了一個判斷,可以防止造成內(nèi)存溢出。

注意:log4j-over-slf4j庫在啟動時會進行內(nèi)部檢查,以確保它不會與Log4j 1.x直接使用或者其他SLF4J綁定(如slf4j-log4j12)沖突。它會檢查是否存在多個SLF4J綁定,特別是org.slf4j.impl.StaticLoggerBinder的實例,因為這個類是SLF4J用來確定實際日志實現(xiàn)的標志。如果發(fā)現(xiàn)多個這樣的綁定,log4j-over-slf4j會拋出一個警告或異常,指出類路徑中存在沖突,并建議用戶清理類路徑以避免循環(huán)引用或日志記錄的不正確行為。 這個檢查通常在類加載時執(zhí)行,即當應(yīng)用程序啟動并嘗試加載log4j-over-slf4j時。如果檢測到類路徑中有其他SLF4J綁定,它會通過org.slf4j.LoggerFactory的靜態(tài)初始化來拋出錯誤信息,而不是在運行時導(dǎo)致內(nèi)存溢出。這種檢查機制有助于防止?jié)撛诘膯栴},并指導(dǎo)開發(fā)者如何解決日志庫的沖突。

場景3:jul-to-slf4j 與 slf4j-jdk14 沖突

?

jul-to-slf4j

: 這個橋接器的作用是將Java內(nèi)置的日志框架java.util.logging(JUL)的日志記錄調(diào)用轉(zhuǎn)換為SLF4J API。

如果你的Java應(yīng)用使用了JUL API,但希望將日志記錄委托給SLF4J,以便于選擇和切換不同的日志實現(xiàn),那么可以引入jul-to-slf4j。這使得JUL的日志記錄能夠被SLF4J的實現(xiàn)如Logback或Log4j處理。

?

slf4j-jdk14

: 這個橋接器則剛好相反,它將SLF4J API的調(diào)用重定向到JUL。

這意味著,即使你的代碼使用SLF4J API,日志記錄實際上會通過JDK的java.util.logging框架進行。這通常發(fā)生在你有一個使用SLF4J的庫,但希望使用JUL作為日志實現(xiàn)的場景。請注意,使用這個橋接器可能會限制你對日志系統(tǒng)的控制和配置,因為JUL通常不如SLF4J的其他實現(xiàn)那樣功能豐富。

如果這兩者共存的話,必然造成相互委托,造成內(nèi)存溢出

4. 處理日志包沖突

OK,到現(xiàn)在我們已經(jīng)清楚地知道日志門面日志實現(xiàn)的對應(yīng)關(guān)系,以及在多個日志實現(xiàn)jar包存在的情況下如何通過橋接包實現(xiàn)我們期望的最終日志輸出效果,那么有沒有一種方式能夠幫助我們在項目啟動的時候只管的發(fā)現(xiàn)是否存在日志jar包沖突呢(比如場景2情況能否提前感知呢)?回答這個問題之前,我們需要先解答下文中最初的問題,為什么同樣的應(yīng)用部署在不同的機器上面會出現(xiàn)不同的表征呢(一臺正常打印日志,另一臺雖正常啟動,但是無任何日志打印)?

4.1 無日志打印原因

答案:因為每個SLF4J的橋接包都有org.slf4j.impl.StaticLoggerBinder,SLF4J則會隨機選擇一個使用。當選擇的跟系統(tǒng)配置的一樣時就可以打印日志,否則就打印不出。

查看acccheck應(yīng)用的pom文件定位對應(yīng)的jar包,發(fā)現(xiàn)同時共存多套日志jar包(圖8)。

日志門面: slf4j-api、 commons-logging 日志實現(xiàn): log4j、logback(logback-classic和logback-core) 橋接包: jcl-over-slf4j、slf4j-log4j12 日志記錄配置文件: logback.xml

很明顯該應(yīng)用是通過logback的日志實現(xiàn)方式來進行日志記錄的,但是應(yīng)用中同時引用了日志門面SLF4JJCL,并且在日志實現(xiàn)中同時引用了log4jlogback,兩個橋接包的作用分別是jcl-over-slf4j(commons-logging到slf4j的橋梁,將commons-logging的底層日志輸出切換到slf4j),slf4j-log4j12(將SLF4J的日志門面與log4j 1.x日志實現(xiàn)綁定)。

但是日志記錄的配置文件是logback.xml,所以當SLF4J綁定到log4j日志實現(xiàn)時,無法正常找到相關(guān)配置文件,故而無法輸出日志,只有當SLF4J綁定到logback日志實現(xiàn)時才能夠正常進行日志打印。

wKgaomZnuxGAXxJGAACCWIC7_b8771.png

wKgZomZnuxSAFaMFAAB6-NXkfTE440.png

圖 8 acccheck應(yīng)用中應(yīng)用的相關(guān)日志jar包

4.2 解決方案

OK,現(xiàn)在已經(jīng)明確問題是因為SLF4J綁定到不同的日志實現(xiàn)導(dǎo)致的日志會出現(xiàn)無法記錄的表征,并且可以確定需要輸出的配置文件是logback.xml,那么處理方式已經(jīng)很清晰啦。

第一步:首先先確立需要使用的一套日志框架(日志門面+日志實現(xiàn)),在該應(yīng)用中使用SLF4J+Logback。不難發(fā)現(xiàn)滿足需求的日志jar包如下。

日志門面: slf4j-api 日志實現(xiàn): logback(logback-classic和logback-core)

第二步:需要對無用的日志jar包進行去除,在該應(yīng)用中需要去除掉JCL(該jar包已經(jīng)通過橋接包 jcl-over-slf4j實現(xiàn)功能替換),去除log4j相關(guān)依賴(log4j的日志實現(xiàn)和SLF4J到log4j 1.x的橋接包slf4j-log4j12)。

第三步:需要評估是否存在引用需要共存場景,在該應(yīng)用中存在JCL日志門面,發(fā)現(xiàn)在代碼中未直接引用JCL包中的類和接口,因此橋接包 jcl-over-slf4j也無需保留。如果代碼中對JCL有直接引用的話可以通過引入橋接包jcl-over-slf4j實現(xiàn)功能替換。

4.3 監(jiān)控日志jar包沖突

回到本節(jié)的問題,那么有沒有一種方式能夠幫助我們在項目啟動的時候只管的發(fā)現(xiàn)是否存在日志jar包沖突呢(比如場景2情況能否提前感知呢)?答案是可以。

注:由于這塊不是本文的重點,大家感興趣可以參考這篇文章。?

5. 總結(jié)

通過以上有關(guān)日志框架相關(guān)知識的介紹以及實踐,可以將解決日志框架共存/沖突問題概括為需遵循一下幾個原則:

1.

明確

需要使用的一套日志實現(xiàn)2.

刪除

多余的無用日志依賴jar包3.視應(yīng)用的引用是否必須共存情況

引入橋接包

如果有引用必須共存的話,那么就移除原始包,使用“over”類型的包(over類型的包復(fù)制了一份原始接口,重新實現(xiàn))

4. 使用日志抽象提供的指定方式

不能over的,使用日志抽象提供的指定方式,例如jboss-logging中,可以通過org.jboss.logging.provider環(huán)境變量指定一個具體的日志框架實現(xiàn)

項目里統(tǒng)一了日志框架之后,無論用那種日志框架打印,最終還是走向我們中轉(zhuǎn)/適配后的唯一一個日志框架。解決了共存/沖突之后,項目里就只剩一款日志框架。再也不會出現(xiàn)“日志打不出”,“日志配置不生效”之類的各種惡心問題。

最后補充一張以SLF4J為日志門面的適配方案圖(如圖9),目前SLF4J是適配方案中最核心的那個框架,也是圖9的中心樞紐。只要圍繞slf4j做適配/轉(zhuǎn)化,理論上就沒有處理不了的沖突。

wKgaomZnuxaAb-fEAAnN5GKwvWM226.png

圖 9 SLF4J的適配轉(zhuǎn)化流程圖

審核編輯 黃宇

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 接口
    +關(guān)注

    關(guān)注

    33

    文章

    8575

    瀏覽量

    151015
  • 日志
    +關(guān)注

    關(guān)注

    0

    文章

    138

    瀏覽量

    10639
  • JCL
    JCL
    +關(guān)注

    關(guān)注

    0

    文章

    2

    瀏覽量

    6420
收藏 人收藏

    評論

    相關(guān)推薦

    Nginx日志分割方案

    nginx 默認沒有提供對日志文件的分割功能,所以隨著時間的增長,access.log 和 error.log 文件會越來越大,尤其是 access.log,其日志記錄量比較大,更容易增長文件大小。影響日志寫入性能,分割 ngi
    發(fā)表于 06-19 15:05 ?465次閱讀
    Nginx<b class='flag-5'>日志</b>分割方案

    linux系統(tǒng)的日志怎么查看

    我們首先要明白,日志是程序本身產(chǎn)生的,那么日志儲存在哪里,該如何查看?
    發(fā)表于 07-15 06:21

    java 日志框架Spring Boot分析

    應(yīng)用程序中輸出相應(yīng)的日志。 在傳統(tǒng)Java應(yīng)用程序中,我們一般會使用類似Log4j這樣的日志框架來輸出日志,而不是直接在代碼中通過System.out.println()來輸出日志。為
    發(fā)表于 09-28 14:58 ?0次下載

    對于大規(guī)模系統(tǒng)日志日志模式提煉算法的優(yōu)化

    LARGE框架是部署在中國科學(xué)院超級計算環(huán)境中的日志分析系統(tǒng),通過日志收集、集中分析、結(jié)果反饋等步驟對環(huán)境中的各種日志文件進行監(jiān)控和分析。在對環(huán)境中系統(tǒng)日志的監(jiān)控過程中,系統(tǒng)維護人員需
    發(fā)表于 11-21 14:54 ?7次下載
    對于大規(guī)模系統(tǒng)<b class='flag-5'>日志</b>的<b class='flag-5'>日志</b>模式提煉算法的優(yōu)化

    ADC測試出現(xiàn)奇怪的FFT結(jié)果

    當采用ADC來進行測試時,一開始非常順利,但后來,卻突然得到一些奇怪的FFT結(jié)果。這究竟是怎么回事?
    的頭像 發(fā)表于 12-06 16:42 ?6268次閱讀

    日志行篩選工具

    日志行篩選工具 篩選日志的整行
    發(fā)表于 06-10 14:22 ?0次下載

    詳解MySQL三大日志的作用

    MySQL日志 主要包括錯誤日志、查詢日志、慢查詢日志、事務(wù)日志、二進制日志幾大類。其中,比較重
    的頭像 發(fā)表于 07-22 14:44 ?1331次閱讀

    淺談奇怪的,古怪的可穿戴設(shè)備

    奇怪的,古怪的,可穿戴的
    的頭像 發(fā)表于 12-28 09:51 ?1048次閱讀

    怎么使用Go重構(gòu)流式日志網(wǎng)關(guān)

    流式日志網(wǎng)關(guān)的主要功能是提供 HTTP 接口,接收 CDN 邊緣節(jié)點上報的各類日志(訪問日志/報錯日志/計費日志等),將
    的頭像 發(fā)表于 06-18 10:42 ?652次閱讀
    怎么使用Go重構(gòu)流式<b class='flag-5'>日志</b>網(wǎng)關(guān)<b class='flag-5'>呢</b>?

    工業(yè)智能網(wǎng)關(guān)日志有哪些?如何輸出和導(dǎo)出網(wǎng)關(guān)日志查看?

    日志主要看網(wǎng)關(guān)與平臺交互情況,判斷平臺數(shù)據(jù)是否正常,通道是否正常系統(tǒng)日志主要用于判斷網(wǎng)站和系統(tǒng)的異常如何輸出和導(dǎo)出工業(yè)智能網(wǎng)關(guān)的日志?1、采集日志
    的頭像 發(fā)表于 10-26 17:33 ?748次閱讀
    工業(yè)智能網(wǎng)關(guān)<b class='flag-5'>日志</b>有哪些?如何輸出和導(dǎo)出網(wǎng)關(guān)<b class='flag-5'>日志</b>查看<b class='flag-5'>呢</b>?

    MySQL三種日志講解

    MySQL 日志包含了錯誤日志、查詢日志、慢查詢日志、事務(wù)日志、二進制日志等,如果存儲引擎使用的
    的頭像 發(fā)表于 07-25 11:15 ?734次閱讀
    MySQL三種<b class='flag-5'>日志</b>講解

    C++異步日志實踐

    一個高效可拓展的異步C++日志庫:RING LOG,本文分享了了其設(shè)計方案與技術(shù)原理等內(nèi)容 導(dǎo)論 同步日志與缺點 傳統(tǒng)的日志也叫同步日志,每次調(diào)用一次打印
    的頭像 發(fā)表于 11-09 10:29 ?664次閱讀
    C++異步<b class='flag-5'>日志</b>實踐

    logcat命令抓取日志方法

    命令抓取日志 logcat -b main -b system -b crash -r 1024 -n 5 -f android.log -v threadtime -b:加載可供查看的緩沖區(qū)的日志
    的頭像 發(fā)表于 11-23 17:31 ?1151次閱讀
    logcat命令抓取<b class='flag-5'>日志</b>方法

    日志篇:模組日志總體介紹

    ?今天我們學(xué)習(xí)合宙模組日志總體介紹,以下進入正文。 一、本文討論的邊界 本文是對合宙 4G 模組, 以及 4G+GNSS 模組的日志功能的總體介紹。通過日志,可以對研發(fā)過程中,以及模組運行過程中
    的頭像 發(fā)表于 10-24 07:16 ?183次閱讀
    <b class='flag-5'>日志</b>篇:模組<b class='flag-5'>日志</b>總體介紹

    nginx日志配置方法

    access_log用來定義日志級別,日志位置。
    的頭像 發(fā)表于 10-24 17:43 ?223次閱讀
    RM新时代网站-首页