日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

java log4j logback jcl_Java 日志二三事

發布時間:2024/9/27 java 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java log4j logback jcl_Java 日志二三事 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前言

Java 擁有功能和性能都非常強大的日志庫,但另一方面,Java 日志庫依賴看起來豐富的讓人眼花繚亂。相信大家或多或少都有這樣的疑問,Log4j,SLF4J,Logback,Log4j2 這些日志框架我該如何選擇?它們彼此間又有什么關系?本篇文章將介紹這些日志庫的歷史演進和之間的關系,方便你選擇最適合的日志庫。文章最后還有日志庫使用的最佳實踐。

歷史

Log4j (Log For Java) 可以當之無愧地說是 Java 日志框架的元老,1999 年發布首個版本,2012 年發布最后一個版本,2015 年正式宣布終止,至今還有無數的系統在使用 Log4j,甚至很多新系統的日志框架選型仍在選擇 Log4j。

然而老的不等于好的,在 IT 技術層面更是如此。盡管 Log4j 有著出色的歷史戰績,但早已不是 Java 日志框架的最優選擇。

在 Log4j 被 Apache Foundation 收入門下之后,由于理念不合, Log4j 的作者 Ceki Gülcü 離開并開發了 SLF4J 和 Logback。

SLF4J (Simple Log Facade For Java) 因其優秀的性能和理念很快受到了廣泛歡迎,2016 年的統計顯示,GitHub 上的熱門 Java 項目中,SLF4J 是使用率第二名的類庫(第一名是 Junit)。

Logback 則吸取了 Log4j 的經驗,實現了很多強大的新功能,再加上它和 SLF4J 能夠無縫集成,也受到了歡迎。

在這期間,Apache Logging 則一直在關門憋大招,Log4j2 在 beta 版鼓搗了幾年,終于在 2014 年發布了 GA 版,不僅吸收了 Logback 的先進功能,更通過優秀的鎖機制、LMAX Disruptor、"無垃圾"機制等先進特性,在性能上全面超越了 Log4j 和 Logback。

Log4j 1.x

Log4j (Log For Java) 是在 Logback 出現之前被廣泛使用的日志庫,由 Gülcü 于 2001 年發布,后來成為 Apache 基金會的頂級項目。Log4j 在設計上非常優秀,對后續的 Java Log 框架有長久而深遠的影響,也產生了 Log4c、Log4s、Log4perl 等到其他語言的移植。Log4j 的短板在于性能,在Logback 和 Log4j2 出來之后,Log4j 的使用也減少了。

Commons Logging

Commons Logging,簡稱 JCL,是 Apache 下屬項目。JCL 是一個 Log Facade,只提供 Log API,不提供實現,然后有 Adapter 來使用 Log4j 或者 JDK 中自帶的 JUL(Java Util Logging)作為 Log Implementation。

不同的項目可能各自使用了不同的日志庫,如果你的項目依賴的其他項目各自使用了不同的日志庫,你想控制日志行為,就需要針對每個日志庫都寫一個配置文件,那豈不是很麻煩?所以這個時候 JCL 就出現了。

在程序中日志創建和記錄都是用 JCL 中的接口,而真正運行時會搜索當前 ClassPath 中有什么實現,如果有 Log4j 就是用 Log4j,如果啥都沒有則使用 JDK 的 JUL。這樣,在你的項目中,還有第三方的項目中,大家記錄日志都使用 JCL 的接口,然后最終運行程序時,可以按照自己的需求(或者喜好)來選擇使用合適的 Log Implementation。比如你想使用 Log4j,就添加 Log4j 的依賴并編寫一個 Log4j 的配置文件(通常命名為 log4j.properties)。

SLF4J/Logback

SLF4J (Simple Logging Facade for Java) 和 Logback 也是 Gülcü 創立的項目,其創立主要是為了提供更高性能的實現。其中,SLF4j 是類似于 JCL 的 Log Facade,Logback 是類似于 Log4j 的 Log Implementation。

SLF4J 出現的緣由是 Gülcü 認為 JCL 的 API 設計得不好,容易讓使用者寫出性能有問題的代碼。比如在用 JCL 輸出一個 debug 級別的 log:

logger.debug("start process request, url: " + url);

這個有什么問題呢?一般生產環境 log 級別都會設到 info 或者以上,那這條 log 是不會被輸出的。然而不管會不會輸出,這其中都會做一個字符串連接操作,然后生產一個新的字符串。如果這條語句在循環或者被調用很多次的函數中,就會多做很多無用的字符串連接,影響性能。所以 JCL 的最佳實踐推薦這么寫:

if (logger.isDebugEnabled()) {logger.debug("start process request, url: " + url); }

顯然作為 API 來說這太為繁瑣,所以 SLF4J 提供了新的 API,方便開發者使用:

logger.debug("start process request, url: {}", url);

這樣的話,在不輸出 log 的時候避免了字符串拼接的開銷;在輸出的時候需要做一個字符串 format,代價比手工拼接字符串大一些,但是可以接受。

而 Logback 則是作為 Log4j 的繼承者來開發的,提供了性能更好的實現,異步 logger,Filter等更多的特性。

Log4j2

現在有了更好的 SLF4J 和 Logback 正慢慢取代 JCL 和 Log4j,然而維護 Log4j 的人不想坐視用戶一點點被 SLF4J /Logback 蠶食,所以 Log4j2 誕生了。Log4j2 和 Log4j1.x 并不兼容,設計上很大程度上模仿了 SLF4J/Logback,性能上也獲得了很大的提升。Log4j2 也做了 Facade/Implementation 分離的設計,分成了 log4j-api 和 log4j-core。

Facade & Implementation

JCL、SLF4J 和 Log4j2 日志框架都使用了 GoF 設計模式中的門面模式(Facade Pattern),將接口和實現分離,定義統一的接口,而實現可以由用戶自由選擇?,F在我們有了三個流行的 Log Facade,以及多個 Log Implementation,那么該如何配合使用呢?

SLF4J

Gülcü 是個追求完美的人,他決定讓 SLF4J 和這些 Log 之間都能夠方便的互相替換,所以做了各種 Adapter 和 Bridge 來連接:

有趣的是,唯獨沒有 slf4j-over-log4j2 的橋接庫,而且 log4j-to-slf4j 和 log4j-slf4j-impl 也是由 Apache 自己開發的。

slf4j-api 只是 Log Facade 的依賴,添加了該依賴意味著在編碼時你能夠使用 Logger log = LoggerFactory.getLogger(Main.class); 和 log.info("hello, {}", "world"); 這種方式。除此之外,還需要添加 Log Implementation 的依賴。

下圖是 SLF4J官網 介紹可以綁定的日志實現框架。其中 slf4j-simple 是為小項目提供的簡單實現,logback-classic 是官方的原生實現,不需要額外的適配器。而 slf4j-log4j12 和 slf4j-jdk14 分別是適配到 Log4j 和 JUL 的依賴,JUL 由于是 JDK 自帶所以不需要額外依賴,而 Log4j 還需要自己的底層實現依賴。

下面這張圖展示了 SLF4J 綁定不同日志實現框架需要的依賴:

Log4j2

關于 Log Facade 選擇 SLF4J 還是 Log4j2,個人覺得要看項目需求??偟膩碚f SLF4J 的兼容性更好,日志實現可以隨意搭配使用;雖然 Log4j2 可以通過 log4j-to-slf4j 橋接到 SLF4J 再使用其他的 Log Implementation,但這必然帶來多余的性能消耗。

而 Log4j2 的優點則在于性能,在 Is it worth to use slf4j with log4j2 這個問題中推薦直接面向 Log4j2 API 編程,理由如下:

  • Message API
  • Lambdas for lazy logging
  • Log any Object instead of just Strings
  • Garbage-free: avoid creating varargs or creating Strings where possible
  • CloseableThreadContext automatically removes items from the MDC when you’re finished with them

Logback 和 Log4j2 都宣稱自己是 Log4j 的后代,一個是出自同一作者,另一個則是在名字上根正苗紅。撇開血統不談,比較一下 Log4j2 和 Logback:

  • Log4j2 比 Logback 更新。Log4j2 的 GA 版在 2014 年底才推出,比 Logback 晚了好幾年,這期間 Log4j2 確實吸收了 SLF4J 和 Logback 的一些優點(比如日志模板),同時應用了不少的新技術
  • 由于采用了更先進的鎖機制和 LMAX Disruptor 庫,Log4j2 的性能優于 Logback,尤其是在多線程環境下和使用異步日志的環境下
  • 二者都支持 Filter(應該說是 Log4j2 借鑒了 Logback 的 Filter),能夠實現靈活的日志記錄規則(例如僅對一部分用戶記錄 DEBUG 級別的日志)
  • 二者都支持對配置文件的動態更新
  • 二者都能夠適配 SLF4J, Logback 與 SLF4J 的適配應該會更好一些,畢竟省掉了一層適配庫
  • Logback 能夠自動壓縮/刪除舊日志
  • Logback 提供了對日志的 HTTP 訪問功能
  • Log4j2 實現了“無垃圾”和“低垃圾”模式。簡單地說,Log4j2 在記錄日志時,能夠重用對象(如String等),盡可能避免實例化新的臨時對象,減少因日志記錄產生的垃圾對象,減少垃圾回收帶來的性能下降

這是 Apache 官方提供的同步和異步寫日志時的性能對比圖:

所以綜上所訴,個人的看法是:如果對性能有要求,且 Log Implementation 想選用 Log4j2 的話,推薦 Log Facade 直接使用 Log4j2 API。

最佳實踐

1. 總是使用 Log Facade,而不是具體 Log Implementation

正如之前所說的,使用 Log Facade 可以方便的切換具體的日志實現。而且,如果依賴多個項目,使用了不同的 Log Facade,還可以方便的通過 Adapter 轉接到同一個實現上。如果依賴項目使用了多個不同的日志實現,就麻煩的多了。

具體來說,現在推薦使用 Log4j-API 或者 SLF4j,不推薦繼續使用 JCL。

2. 只添加一個 Log Implementation 依賴

毫無疑問,項目中應該只使用一個具體的 Log Implementation,建議使用 Logback 或者 Log4j2。如果有依賴的項目中,使用的 Log Facade 不支持直接使用當前的 Log Implementation,就添加合適的橋接器依賴。

3. 總是為 Log Implementation 依賴設置 optional 和 runtime scope

在項目中,Log Implementation 的依賴強烈建議設置為 runtime scope,并且設置為 optional。例如項目中使用了 SLF4J 作為 Log Facade,然后想使用 Logback 作為 Implementation,那么使用 POM 文件應該這么寫:

<dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>${slf4j.version}</version> </dependency> <dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>${logback.version}</version><optional>true</optional><scope>runtime</scope> </dependency>

設為 optional,依賴不會傳遞,這樣如果你的項目被別的項目依賴,它就不會引入不想要的 Log Implementation 依賴,即使用你提供的庫的用戶可以自定義 Log Implementation;

Scope 設置為 runtime,是為了防止開發人員在項目中直接使用 Log Implementation 中的類,而不適用 Log Facade 中的類,即編碼時程序員只可見 Log Facade 層面而不必關注實現層面。

4. 如果有必要,排除依賴的第三方庫中的 Log Impementation 依賴

這是很常見的一個問題,第三方庫的開發者未必會把具體的日志實現或者橋接器的依賴設置為 optional,然后你的項目繼承了這些依賴。然而具體的日志實現未必是你想使用的,比如他依賴了 Log4j,你想使用 Logback,這樣程序在運行時會檢測到有多個日志實現類,如下圖。另外,如果不同的第三方依賴使用了不同的橋接器和 Log 實現,也容易形成環。

SLF4J: Class path contains multiple SLF4J bindings. SLF4J: Found binding in [jar:file:/Users/s1mple/.m2/repository/org/slf4j/slf4j-log4j12/1.7.5/slf4j-log4j12-1.7.5.jar!/org/slf4j/impl/StaticLoggerBinder.class] SLF4J: Found binding in [jar:file:/Users/s1mple/.m2/repository/com/caacitc/slf4j-jdk14-1.6.1.jar/1.0.2/slf4j-jdk14-1.6.1.jar-1.0.2.jar!/org/slf4j/impl/StaticLoggerBinder.class]

這種情況下,推薦的處理方法,是使用 exclude 來排除所有的這些 Log 實現和橋接器的依賴,只保留第三方庫里面對 Log Facade 的依賴。

<dependency><groupId>com.alibaba.jstorm</groupId><artifactId>jstorm-core</artifactId><version>2.1.1</version><exclusions><exclusion><groupId>org.slf4j</groupId><artifactId>log4j-over-slf4j</artifactId></exclusion><exclusion><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId></exclusion></exclusions> </dependency>

另外,在 IntelliJ IDEA 中,可以使用 Show Maven Dependencies 查看依賴關系圖,可以方便的搜索依賴并 exclude 掉。

5. 避免為不會輸出的 log 付出代價

Log 庫都可以靈活的設置輸出界別,所以每一條程序中的 log,都是有可能不會被輸出的。這時候要注意不要額外的付出代價。

先看兩個有問題的寫法:

logger.debug("start process request, url: " + url); logger.debug("receive request: {}", toJson(request));

第一條是直接做了字符串拼接,所以即使日志級別高于 debug 也會做一個字符串連接操作;第二條雖然用了 SLF4J/Log4j2 中的懶求值方式來避免不必要的字符串拼接開銷,但是 toJson() 這個函數卻是都會被調用并且開銷更大。

推薦的寫法如下:

logger.debug("start process request, url:{}", url); // SLF4J/LOG4J2 if (logger.isDebugEnabled()) { // SLF4J/LOG4J2logger.debug("receive request: " + toJson(request)); } logger.debug("receive request: {}", () -> toJson(request)); // LOG4J2 logger.debug(() -> "receive request: " + toJson(request)); // LOG4J2

6. 日志中盡量避免輸出行號,函數名等字段

原因是,為了獲取語句所在的函數名,或者行號,log 庫的實現都是獲取當前的 stacktrace,然后分析取出這些信息,而獲取 stacktrace 的代價是很昂貴的。如果有很多的日志輸出,就會占用大量的 CPU。在沒有特殊需要的情況下,建議不要在日志中輸出這些這些字段。

正確做法是使用日志打印的類名和內容定位到代碼位置。

public class Main {private static final Logger log = LoggerFactory.getLogger(Main.class);public static void main(String[] args) {log.info("hello world");} }// 16:08:14.913 [main] INFO com.github.s1mplecc.log.Main - hello world

參考

  • Java 日志框架解析(上) - 歷史演進
  • Java 日志框架解析(下) - 最佳實踐
  • 面向log4j2 API編程而不是slf4j
  • SLF4J 官方文檔
  • Apache Log4j2 官方文檔

總結

以上是生活随笔為你收集整理的java log4j logback jcl_Java 日志二三事的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。