CodeGym /Java Blog /Toto sisi /日誌記錄:什麼、如何、在哪里以及用什麼?
John Squirrels
等級 41
San Francisco

日誌記錄:什麼、如何、在哪里以及用什麼?

在 Toto sisi 群組發布
CodeGym 社區的大家好! 日誌記錄:什麼、如何、在哪里以及用什麼? - 1 今天我們來談談日誌記錄:
  1. 它是什麼,為什麼存在,什麼時候應該使用它,什麼時候應該避免它。
  2. Java 中有哪些日誌記錄實現可用,以及您應該如何使用所有這些日誌記錄選項。
  3. 和日誌級別。我們將討論什麼是 appender 以及如何正確配置它。
  4. 記錄節點以及如何正確配置它們,以便一切都按我們想要的方式工作。
本材料適用於廣泛的受眾。任何剛開始了解 Java 的人,以及已經在工作但只探索過的人都會清楚logger.info("log something"); Let's go!

為什麼需要日誌記錄?

讓我們看一些日誌可以解決問題的真實案例。這是我工作中的一個例子。在某些點上,應用程序可以與其他服務集成。我在這些點上使用日誌記錄來建立一種“不在場證明”:如果集成不起作用,那麼很容易找出哪一方有問題。還希望記錄存儲在數據庫中的重要信息。例如,創建管理員用戶。這正是那種適合記錄的事情。

Java 中的日誌記錄工具

在 Java 中著名的日誌記錄解決方案中,我們可以強調以下內容:
  • 日誌4j
  • JUL — java.util.logging
  • JCL——雅加達公共日誌記錄
  • 登錄
  • SLF4J — Java 的簡單日誌外觀
我們將對它們中的每一個進行概述。然後我們將以slf4j - log4j綁定作為實際討論的基礎。這現在可能看起來很奇怪,但不要擔心:到本文結尾時,一切都會一目了然。

系統.err.println

一開始,有System.err.println(在控制台上顯示日誌條目)。即使在今天,這種技術也被用來在調試時快速記錄日誌。當然,這裡沒有設置可以討論,所以只要記住這個方法,我們就可以繼續了。

日誌4j

這是開發人員出於需要創建的完整解決方案。結果是您可以使用一個非常有趣的工具。由於各種情況,這個解決方案最終沒有出現在 JDK 中,這讓整個社區都非常不滿。Log4j 具有配置選項,可讓您在包中啟用日誌記錄com.example.type並在子包中將其關閉com.example.type.generic。這使得快速排除不需要記錄的代碼成為可能。這裡需要注意的是,Log4j有兩個版本:1.2.x和2.xx,並且相互不兼容Log4j 添加了appender的概念(用於編寫日誌的工具)和佈局(日誌格式化)。這使您可以只記錄您需要的內容,並根據需要記錄它。我們稍後會詳細討論 appender。

JUL — java.util.logging

該解決方案的主要優勢之一是 JUL 包含在 JDK(Java 開發工具包)中。不幸的是,在開發它時,它的創建者並沒有將其基於流行的 Log4j 實用程序,而是基於 IBM 的解決方案。該決定產生了後果。現實是現在沒有人使用 JUL。JUL 中的日誌級別不同於 Logback、Log4j 和 Slf4j 的級別。這讓他們更難相互理解。創建一個記錄器或多或少是相似的。為此,您需要進行導入:

java.util.logging.Logger log = java.util.logging.Logger.getLogger(LoggingJul.class.getName());
傳遞了類名,因此我們知道日誌記錄的來源。從 Java 8 開始,您可以通過Supplier<String>. 這有助於我們僅在真正需要時才讀取和創建一行,而不是像以前那樣每次都讀取和創建一行。直到 Java 8 的發布,開發人員才終於解決了重要的問題,讓 JUL 真正可用。即帶Supplier<String> msgSupplier參數的方法,如下所示:

public void info(Supplier<String> msgSupplier) {
   log(Level.INFO, msgSupplier);
}

JCL——雅加達公共日誌記錄

由於長期以來沒有關於日誌記錄的行業標準,而且許多人創建了自己的自定義記錄器,因此決定發布 JCL,這是一種可以在其他包裝器之上使用的通用包裝器。為什麼?有時添加到項目的依賴項使用與項目中不同的記錄器。因此,它們作為傳遞依賴項添加到項目中,這在嘗試將它們放在一起時產生了真正的問題。不幸的是,包裝器不是很實用,也沒有添加任何東西。如果每個人都使用 JCL 可能會很方便。但事實並非如此,因此目前使用 JCL 並不是最好的主意。

登錄

開源之路充滿荊棘……編寫 Log4j 的同一位開發人員還編寫了 Logback 作為後繼日誌記錄框架。它基於與 Log4j 相同的想法。Logback 的不同之處在於:
  • 提高性能
  • 添加了對 Slf4j 的原生支持
  • 擴展過濾選項
默認情況下,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>

SLF4J — Java 的簡單日誌外觀

2006 年的某個時候,Log4j 的創始人之一離開了該項目並創建了 Slf4j(Java 簡單日誌外觀),這是 Log4j、JUL、通用日誌記錄和 Logback 的包裝器。如您所見,我們已經發展到在包裝器之上創建包裝器的地步......在這種情況下,它分為兩部分:在應用程序中使用的 API,以及添加單獨的實現每種類型的日誌記錄的依賴關係。例如,slf4j-log4j12.jarslf4j-jdk14.jar。您需要連接正確的實現,僅此而已:您的整個項目都將使用它。Slf4j 支持所有最新功能,例如用於日誌記錄的格式化字符串。以前,有這樣的問題。假設我們創建了一個這樣的日誌條目:

log.debug("User " + user + " connected from " + request.getRemoteAddr());
由於連接運算符,user對象默默地變成了一個字符串,這要歸功於user.toString(). 這需要時間並減慢系統速度。如果我們正在調試應用程序,那可能沒問題。如果此類的日誌級別為 INFO 或更高,我們就會開始遇到問題。換句話說,我們不應該寫這個日誌條目(對於 INFO 或更高),我們不應該使用字符串連接。理論上,日誌庫本身應該解決這個問題。碰巧的是,這成為 Log4j 第一個版本中的最大問題。它沒有提供一個像樣的解決方案,而是建議做這樣的事情:

if (log.isDebugEnabled()) {
    log.debug("User " + user + " connected from " + request.getRemoteAddr());
}
也就是說,他們建議寫 3!日誌記錄應盡量減少代碼更改,而這三行顯然違反了該一般方法。Slf4j 與 JDK 和 API 沒有兼容性問題,因此立即出現了一個很好的解決方案:

log.debug("User {} connected from {}", user, request.getRemoteAddr());
其中{}表示傳遞給方法的參數的佔位符。即第一個{}對應user,第二個{}對應request.getRemoteAddr()。通過這種方式,只有當日誌級別要求我們寫入日誌條目時,我們才會執行字符串連接。之後,Sjf4j 開始迅速流行起來。目前,這是最好的解決方案。因此,讓我們看一下使用綁定進行日誌記錄slf4j-log4j12

需要記錄什麼

當然,您不應該記錄所有內容。這通常是不必要的,有時甚至是危險的。例如,如果您記錄某人的個人數據,但不知何故被洩露,就會出現真正的問題,尤其是在專注於西方市場的項目中。但也有一些你絕對應該記錄的東西
  1. 應用程序的開始/結束。我們需要知道應用程序是否真的按預期開始和結束。
  2. 安全問題。在這裡最好記錄猜測某人密碼的嘗試、管理員登錄時的實例等。
  3. 某些應用程序狀態。例如,業務流程中從一種狀態到另一種狀態的轉換。
  4. 某些調試信息以及相應的日誌級別。
  5. 某些 SQL 腳本。當這是必要的時候,有現實世界的案例。但同樣,通過巧妙地調整日誌級別,您可以獲得出色的效果。
  6. 當驗證事情是否正常工作時,可以記錄正在運行的線程。

日誌記錄中的常見錯誤

這裡有很多細微差別,但我們會特別提到一些常見錯誤:
  1. 過度記錄。您不應該記錄理論上可能重要的每個步驟。這裡有一個很好的經驗法則:日誌不應超過負載的 10%。否則會出現性能問題。
  2. 將所有數據記錄到一個文件中。在某些時候,這將使讀取/寫入日誌變得非常困難,更不用說某些系統對文件大小有限制了。
  3. 使用不正確的日誌級別。每個日誌級別都有明確的界限,應該尊重它們。如果邊界不清楚,您可以就使用哪個級別達成一致。

日誌級別

x:可見
致命的 錯誤 警告 信息 調試 痕跡 全部
離開
致命的 X
錯誤 X X
警告 X X X
信息 X X X X
調試 X X X X X
痕跡 X X X X X X
全部 X X X X X X X
什麼是日誌級別?為了以某種方式創建日誌條目的層次結構,某些約定和定界是必要的。這就是引入日誌級別的原因。級別在應用程序中設置。如果條目低於指定級別,則不會記錄。例如,我們有調試應用程序時使用的日誌。在正常操作期間(當應用程序用於其預期目的時),不需要此類日誌。因此,日誌級別高於調試級別。讓我們看看使用 Log4j 的日誌級別。除了 JUL 之外,其他解決方案使用相同的日誌級別。在這裡,它們按降序排列:
  • OFF:不記錄日誌條目;一切都被忽略了。
  • FATAL:阻止應用程序繼續運行的錯誤。例如,“JVM 內存不足錯誤”。
  • ERROR:此級別的錯誤表示需要解決的問題。該錯誤不會停止整個應用程序。其他請求可能會正常工作。
  • WARN:表示警告的日誌條目。發生了意外情況,但係統能夠應對並完成了請求
  • 信息:指示應用程序中重要操作的日誌條目。這些不是錯誤或警告。它們是預期的系統事件。
  • DEBUG:日誌條目需要調試應用程序。用於確保應用程序完全符合預期,或用於描述應用程序採取的操作,即“進入方法 1”。
  • TRACE:用於調試的低優先級日誌條目。最低日誌級別。
  • ALL:寫入所有應用程序日誌條目的日誌級別。
在應用程序的某處啟用了 INFO 日誌級別,然後將記錄每個級別的條目,從 INFO 到 FATAL。如果設置了 FATAL 日誌級別,則只會寫入具有該級別的日誌條目。

記錄和發送日誌:Appender

讓我們考慮一下當我們使用 Log4j 時這一切是如何工作的,它為寫入/發送日誌提供了充足的機會:
  • 寫入文件——DailyRollingFileAppender
  • 將信息寫入控制台 —ConsoleAppender
  • 將日誌寫入數據庫——JDBCAppender
  • 管理通過 TCP/IP 發送日誌 —TelnetAppender
  • 確保日誌記錄不會對性能產生負面影響——AsyncAppender
還有一些實現:完整列表可在此處獲得。順便說一句,如果您需要的附加程序不存在,那也沒問題。您可以通過實現 Log4j 支持的Appender接口來編寫自己的 appender 。

記錄節點

出於演示目的,我們將使用 Slf4j 接口,以及來自 Log4j 的實現。創建一個記錄器非常簡單:在一個名為 的類中MainDemo,它會做一些記錄,我們需要添加以下內容:

org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(MainDemo.class);
這將為我們創建一個記錄器。要創建日誌條目,有幾種可用的方法,其名稱反映了將使用的日誌級別。例如:

logger.trace("Method 1 started with argument={}", argument);
logger.debug("Database updated with script = {}", script);
logger.info("Application has started on port = {}", port);
logger.warn("Log4j didn't find the log4j.properties file. Please fix this.");
logger.error("Connection refused to host = {}", host);
雖然我們傳遞的是類,但最終的名稱是類的全名,包括包。這樣做是為了稍後您可以將日誌記錄劃分為節點並為每個節點配置日誌記錄級別和附加程序。例如,記錄器是在com.github.romankh3.logginglecture.MainDemo類中創建的。該名稱為創建日誌節點層次結構提供了基礎。主節點是頂級RootLogger。這是接收整個應用程序的所有日誌條目的節點。其餘節點可以如下圖所示: 日誌記錄:什麼、如何、在哪里以及用什麼? - 3Appenders 是為特定的日誌節點配置的。現在我們將查看log4j.properties文件以查看如何配置它們的示例。

log4j.properties 文件的分步指南

我們將一次一步地設置所有內容,看看有什麼可能:

log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
這一行表示我們正在註冊 CONSOLE appender,它使用 org.apache.log4j.ConsoleAppender 實現。此附加程序將信息寫入控制台。接下來,我們註冊另一個附加程序。這將寫入一個文件:

log4j.appender.FILE=org.apache.log4j.RollingFileAppender
重要的是要注意附加程序本身仍然需要配置。一旦我們註冊了我們的附加程序,我們就可以確定哪些日誌級別和哪些附加程序將在節點上使用。

log4j.rootLogger=調試,控制台,文件

  • log4j.rootLogger 表示我們正在配置根節點,其中包含所有日誌條目
  • 等號後的第一個詞表示要寫入的最低日誌級別(在我們的例子中,它是 DEBUG)
  • 在逗號之後,我們指示要使用的所有附加程序。
要配置更具體的日誌記錄節點,您可以使用如下條目:

log4j.logger.com.github.romankh3.logginglecture=TRACE, OWN, CONSOLE
wherelog4j.logger.用於引用特定節點。在我們的例子中,com.github.romankh3.logginglecture. 現在讓我們談談配置 CONSOLE appender:

# CONSOLE appender customization
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.threshold=DEBUG
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=[%-5p] : %c:%L : %m%n
在這裡我們看到可以設置 appender 開始工作的特定級別。下面是一個實際發生的例子:假設一個 INFO 級別的消息被日誌節點接收並傳遞給分配給它的附加程序。如果 appender 的閾值設置為 WARN,那麼它會收到日誌條目但不對其執行任何操作。接下來,我們需要決定消息將使用哪種佈局。我在示例中使用了 PatternLayout,但還有許多其他選項。我們不會在本文中介紹它們。配置 FILE appender 的示例:

# File appender customization
log4j.appender.FILE=org.apache.log4j.RollingFileAppender
log4j.appender.FILE.File=./target/logging/logging.log
log4j.appender.FILE.MaxFileSize=1MB
log4j.appender.FILE.threshold=DEBUG
log4j.appender.FILE.MaxBackupIndex=2
log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
log4j.appender.FILE.layout.ConversionPattern=[ %-5p] - %c:%L - %m%n
您可以配置將日誌條目寫入的特定文件,從這一行可以看出:

log4j.appender.FILE.File=./target/logging/logging.log
該條目被寫入文件logging.log。為避免文件大小出現問題,您可以配置最大值,在本例中為 1MB。 MaxBackupIndex表示將有多少這樣的日誌文件。如果我們需要創建比這更多的文件,那麼第一個文件將被刪除。要查看配置日誌記錄的真實示例,您可以轉到GitHub 上的 公共存儲庫。

強化我們討論過的內容

嘗試自己做我們描述的一切:
  • 創建您自己的項目,類似於我們上面的示例。
  • 如果您知道如何使用 Maven,請使用它。如果沒有,請閱讀教程,其中介紹瞭如何連接庫。

總之

  1. 我們討論了 Java 中存在的日誌記錄解決方案。
  2. 幾乎所有著名的日誌記錄庫都是由一個人編寫的 :D
  3. 我們了解了應該記錄和不應該記錄的內容。
  4. 我們弄清楚了日誌級別。
  5. 我們被介紹給日誌節點。
  6. 我們研究了 appender 是什麼以及它的用途。
  7. 我們一步步創建了一個log4j.proterties文件。

附加材料

  1. CodeGym:記錄器課程
  2. 每週 Geekly:Java 日誌記錄。你好世界
  3. 編碼恐怖:日誌記錄的問題
  4. YouTube:了解 Java Logging Hell - 基礎知識。Java 日誌地獄以及如何遠離它
  5. Log4j:追加器
  6. Log4j:佈局
另見我的另一篇文章:
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION