2.1 第一個記錄器——log4j
如您所知,日誌的歷史始於System.err.println()
向控制台輸出一條記錄。它仍然積極用於調試,例如,Intellij IDEA 使用它向控制台顯示錯誤消息。但是這個選項沒有任何設置,所以讓我們繼續。
第一個也是最受歡迎的記錄器被稱為Log4j
. 這是一個很好的、高度可定制的解決方案。由於各種情況,這個決定沒有進入 JDK,這極大地擾亂了整個社區。
這個記錄器不僅能夠記錄,它是由程序員為程序員創建的,並允許他們解決與日誌記錄相關的不斷出現的問題。
正如您已經知道的那樣,日誌是最後寫入的,以便有人閱讀它們並試圖了解程序運行期間發生的事情 - 如預期的那樣出錯的原因和時間。
log4j
為此有三件事:
- 子包日誌記錄;
- 一組附加程序(結果);
- 熱重載設置。
首先,log4j
可以以這樣一種方式編寫設置,即在一個包中啟用日誌記錄並在另一個包中禁用它。例如,可以在 中啟用日誌記錄com.codegym.server
,但在 中禁用它com.codegym.server.payment
。這使得從日誌中快速刪除不必要的信息成為可能。
其次,log4j
它允許一次將日誌記錄結果寫入多個日誌文件。每個輸出都可以單獨配置。例如,在一個文件中可以只寫入有關嚴重錯誤的信息,在另一個文件中 - 來自特定模塊的日誌,在第三個文件中 - 特定時間的日誌。
因此,每個日誌文件都針對特定類型的預期問題進行了調整。這極大地簡化了不喜歡手動查看千兆字節日誌文件的程序員的生活。
最後,第三,log4j
它允許在程序運行時直接更改日誌設置,而無需重新啟動。當需要更正日誌的工作以查找有關特定錯誤的附加信息時,這非常方便。
重要的!日誌有兩個版本log4j
:1.2.x和2.xx,彼此不兼容。
您可以使用代碼將記錄器連接到項目:
<dependencies>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.17.2</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.17.2</version>
</dependency>
</dependencies>
2.2 第一個官方記錄器 - JUL:java.util.logging
在 Java 社區中出現大量記錄器之後,開發人員JDK
決定製作一個標準的記錄器,供所有人使用。這就是記錄器出現的方式JUL
:包java.util.logging
。
然而,在其開發過程中,記錄器的創建者不是基於log4j
,而是 IBM 記錄器的變體,這影響了它的發展。好消息是JUL
包含了記錄器JDK
,壞消息是很少有人使用它。

開發人員不僅JUL
制定了“另一個通用標準”,還為其製定了自己的日誌級別,這與當時流行的日誌程序所接受的級別不同。
這是一個大問題。畢竟,產品Java
通常是從大量圖書館收集的,而每個這樣的圖書館都有自己的記錄器。因此有必要配置應用程序中的所有記錄器。
雖然記錄器本身很不錯。創建一個記錄器或多或少是一樣的。為此,您需要導入:
java.util.logging.Logger log = java.util.logging.Logger.getLogger(LoggingJul.class.getName());
類名是專門傳遞的,以便知道日誌記錄的來源。
只有發布,開發人員解決了重要問題,之後JUL
使用起來才真正方便。在此之前,它是某種二流記錄器。
此記錄器還支持 lambda 表達式和惰性求值。從 開始Java 8
,你可以通過Supplier<String>
。這有助於僅在真正需要時才讀取和創建字符串,而不是像以前那樣每次都讀取和創建。
帶有參數的方法Supplier<String> msgSupplier
如下所示:
public void info(Supplier
msgSupplier
)
{
log
(
Level
.INFO
, msgSupplier
)
;
}
2.3 第一個記錄器包裝器——JCL:jakarta commons logging
很長一段時間,記錄器之間沒有單一的標準,它JUL
應該成為一個,但更糟糕的是log4j
,所以單一的標準從未出現過。但是出現了一個完整的伐木者動物園,每個伐木者都想變得一樣。

然而,普通的 Java 開發人員並不喜歡幾乎每個庫都有自己的記錄器,並且需要以某種特殊方式進行配置。因此,社區決定為其他記錄器創建一個特殊的包裝器——這就是JCL: jakarta commons logging
再一次,這個項目是作為一個領導者而創建的,但並沒有成為一個領導者。你無法創造贏家,你只能成為贏家。功能JCL
很差,沒有人願意使用它。旨在取代所有記錄器的記錄器遇到了同樣的命運,因為它JUL
沒有被使用。
儘管它已被添加到 Apache 社區發布的許多庫中,但記錄器的數量卻有增無減。
2.4 First last logger - Logback
但這還不是全部。開發人員log4j
認為他是最聰明的(好吧,畢竟大多數人都使用他的記錄器)並決定編寫一個新的改進記錄器,它將結合log4j
其他記錄器的優點。
新的記錄器被稱為Logback
. 正是這個記錄器本應成為未來每個人都會使用的單一記錄器。它基於與log4j
.
您可以使用以下代碼將此記錄器連接到項目:
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.6</version>
</dependency>
不同之處在於Logback
:
- 提高性能;
- 添加本機支持
slf4j
; - 擴展過濾選項。
這個記錄器的另一個優點是它有很好的默認設置。只有當你想改變其中的某些東西時,你才必須配置記錄器。此外,設置文件更適合企業軟件 - 它的所有配置都設置為xml/
.
默認情況下,Logback
它不需要任何設置並記錄該級別DEBUG
及以上級別的所有日誌。如果您需要不同的行為,可以通過xml
配置進行配置:
<configuration>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>app.log</file>
<encoder>
<pattern>%d{HH:mm:ss,SSS} %-5p [%c] - %m%n</pattern>
</encoder>
</appender>
<logger name="org.hibernate.SQL" level="DEBUG" />
<logger name="org.hibernate.type.descriptor.sql" level="TRACE" />
<root level="info">
<appender-ref ref="FILE" />
</root>
</configuration>
2.5 最新的通用記錄器 - SLF4J: Simple Logging Facade for Java
多久才能找到中庸...
2006 年,其中一位創建者log4j
離開了該項目,並決定再次嘗試創建一個通用記錄器。但這一次不是新的記錄器,而是新的通用標準(包裝器),可以讓不同的記錄器一起交互。
這個記錄器被稱為,它是, ,slf4j — Simple Logging Facade for Java
的包裝器。這個記錄器解決了一個真正的問題——管理一個記錄器動物園,所以每個人都立即開始使用它。log4j
JUL
common-loggins and logback
我們英勇地解決我們為自己製造的問題。正如您所看到的,進展已經到了我們已經在包裝器之上創建了一個包裝器的地步......
包裝本身由兩部分組成:
API
, 在應用程序中使用;- 作為每個記錄器的單獨依賴項添加的實現。
您可以使用代碼將記錄器連接到項目:
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.17.2</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.17.2</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.17.2</version>
</dependency>
連接正確的實現就足夠了,僅此而已:整個項目都將使用它。
2.6 slf4j中的優化
Slf4j
支持所有新功能,例如用於日誌記錄的字符串格式。在此之前就有這樣的問題。假設您要將消息打印到日誌中:
log.debug("User " + user + " connected from " + request.getRemoteAddr());
這段代碼有問題。假設您的應用程序正在運行production
並且沒有向日誌寫入任何內容DEBUG-messages
,但是該方法log.debug()
仍然會被調用,並且在調用它時,還會調用以下方法:
user.toString();
request.getRemoteAddr();
調用這些方法會降低應用程序的速度。只有在調試期間才需要調用它們,但無論如何都會調用它們。
從邏輯的角度來看,這個問題必須在日誌庫本身中解決。在 log4j 的第一個版本中,解決方案出現了:
if (log.isDebugEnabled()) {
log.debug("User " + user + " connected from " + request.getRemoteAddr());
}
日誌不再是一行,現在需要寫三行。這極大地惡化了代碼的可讀性,並降低了log4j
.
通過提供智能日誌記錄器,記錄器slf4j
能夠稍微改善這種情況。它看起來像這樣:
log.debug("User {} connected from {}", user, request.getRemoteAddr());
其中{}
表示插入方法中傳遞的參數。也就是說,第一個{}
對應於用戶,第二個{}
對應於request.getRemoteAddr()
。
僅當日誌記錄級別允許記錄時,這些參數才會連接成一條消息。不完美,但比所有其他選項都要好。
之後,SLF4J
它開始迅速流行起來,目前這是最好的解決方案。
因此,我們將考慮使用 bundle 的示例進行日誌記錄slf4j-log4j12
。
GO TO FULL VERSION