2.1 第一個記錄器——log4j

如您所知,日誌的歷史始於System.err.println()向控制台輸出一條記錄。它仍然積極用於調試,例如,Intellij IDEA 使用它向控制台顯示錯誤消息。但是這個選項沒有任何設置,所以讓我們繼續。

第一個也是最受歡迎的記錄器被稱為Log4j. 這是一個很好的、高度可定制的解決方案。由於各種情況,這個決定沒有進入 JDK,這極大地擾亂了整個社區。

這個記錄器不僅能夠記錄,它是由程序員為程序員創建的,並允許他們解決與日誌記錄相關的不斷出現的問題。

正如您已經知道的那樣,日誌是最後寫入的,以便有人閱讀它們並試圖了解程序運行期間發生的事情 - 如預期的那樣出錯的原因和時間。

log4j為此有三件事:

  • 子包日誌記錄;
  • 一組附加程序(結果);
  • 熱重載設置。

首先,log4j可以以這樣一種方式編寫設置,即在一個包中啟用日誌記錄並在另一個包中禁用它。例如,可以在 中啟用日誌記錄com.codegym.server,但在 中禁用它com.codegym.server.payment。這使得從日誌中快速刪除不必要的信息成為可能。

其次,log4j它允許一次將日誌記錄結果寫入多個日誌文件。每個輸出都可以單獨配置。例如,在一個文件中可以只寫入有關嚴重錯誤的信息,在另一個文件中 - 來自特定模塊的日誌,在第三個文件中 - 特定時間的日誌。

因此,每個日誌文件都針對特定類型的預期問題進行了調整。這極大地簡化了不喜歡手動查看千兆字節日誌文件的程序員的生活。

最後,第三,log4j它允許在程序運行時直接更改日誌設置,而無需重新啟動。當需要更正日誌的工作以查找有關特定錯誤的附加信息時,這非常方便。

重要的!日誌有兩個版本log4j1.2.x2.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,所以單一的標準從未出現過。但是出現了一個完整的伐木者動物園,每個伐木者都想變得一樣。

JCL公司

然而,普通的 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的包裝器。這個記錄器解決了一個真正的問題——管理一個記錄器動物園,所以每個人都立即開始使用它。log4jJULcommon-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