大家好,JavaRush 社群!今天我們來談談Java日誌記錄:
- 這是什麼,這是為什麼。什麼情況下最好用,什麼情況下不宜用?
- Java 中的日誌記錄有哪些不同的實作?我們該如何利用這種多樣性?
記錄級別。讓我們討論一下什麼是appender以及如何正確配置它。
- 記錄節點以及如何正確配置它們,以便一切都按照我們想要的方式運作。
本資料面向廣大受眾。對於那些剛剛熟悉 Java 的人以及那些已經在工作但只是透過
logger.info(“log something”);
Let's Go!才弄清楚的人來說,這一點都會很清楚。
為什麼需要日誌記錄?
讓我們來看看日誌記錄可以解決問題的真實案例。這是我工作中的一個例子。有一些與其他服務整合的應用程式點。我使用記錄這些點
作為「不在場證據」:如果整合不起作用,很容易找出問題源自哪一方。也建議記錄保存到資料庫的重要資訊。例如,建立管理員使用者。這正是記錄的好地方。
Java 日誌工具
眾所周知的 Java 日誌記錄解決方案包括:
- 日誌4j
- 七月 - java.util.logging
- JCL - 雅加達公共日誌記錄
- 回溯
- SLF4J - java 的簡單日誌記錄門面
讓我們快速瀏覽一下它們中的每一個,在材料的實際部分中,我們將以Slf4j - log4j 的連接作為基礎。現在這可能看起來很奇怪,但別擔心:在文章結束時一切都會清楚。 |
系統錯誤.println
當然,最初有
System.err.println(將輸出記錄到控制台)。仍然是在調試時用來快速取得日誌。當然,這裡不需要談論任何設置,所以我們記住它並繼續。
Log4j
這已經是一個成熟的解決方案,是根據開發人員的需求創建的。事實證明這是一個非常有趣的工具。由於各種情況,這個解決方案從未進入 JDK,這讓整個社區感到非常沮喪。log4j 具有配置選項,以便可以在套件中開啟日誌記錄
com.example.type
並在子包中關閉日誌記錄
com.example.type.generic
。這使得快速區分需要記錄的內容和不需要的內容成為可能。這裡要注意的是,
log4j有兩個版本:1.2.x和2.x.x,它們彼此不相容。
log4j增加了appender這樣一個概念,即記錄日誌和佈局日誌格式化的工具。這使您可以僅記錄您需要的內容以及您需要的方式。稍後我們將詳細討論appender。
七月 - 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 並不是一個好主意。
回溯
開源之路是多麼的荊棘啊……Logback 是由與 log4j 相同的開發者編寫的,旨在創建它的後繼者。這個想法與 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 - Simple Logging Facade for Java - log4j、JUL、common-loggins 和 logback 的包裝器。正如你所看到的,進展已經達到了他們在包裝器之上創建了一個包裝器的地步......而且,它分為兩部分:API(在應用程序中使用)和實現(添加為)每種類型的日誌記錄都有單獨的依賴關係。例如,
slf4j-log4j12.jar
,
slf4j-jdk14.jar
。連接正確的實作就足夠了:整個專案都將與它一起工作。Slf4j 支援所有新功能,例如記錄的字串格式。之前也有這樣的問題。假設有一個日誌條目:
log.debug("User " + user + " connected from " + request.getRemoteAddr());
由於字串連接, 物件中
user
存在隱式轉換,這需要時間,從而減慢系統速度。
user.toString()
如果我們調試應用程序,一切都會正常。如果此類的日誌記錄等級為 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迅速流行起來,是目前最好的解決方案。因此,我們將考慮使用bundle 的範例進行日誌記錄
slf4j-log4j12
。
需要記錄什麼
當然,您不應該記錄所有內容。有時這是不必要的,甚至是危險的。例如,如果你承諾某人的個人數據,但不知何故被曝光,就會出現真正的問題,特別是在面向西方的專案上。但還有一些事情是
必須記錄的:
- 應用程式的開始/結束。我們需要知道應用程式實際上按我們的預期啟動並按預期結束。
- 安全問題。在這裡最好記錄密碼猜測嘗試、記錄重要使用者的登入等。
- 一些應用程式聲明。例如,業務流程中從一種狀態轉換到另一種狀態。
- 一些用於調試的信息,以及適當級別的日誌記錄。
- 一些 SQL 腳本。確實有需要這樣做的案例。再次,透過巧妙地調整級別,可以取得優異的效果。
- 在檢查正確操作的情況下,可以記錄執行的執行緒(Thread) 。
常見的日誌記錄錯誤
有很多細微差別,但以下是一些常見錯誤:
- 過多的日誌記錄。您不應該記錄理論上可能重要的每一個步驟。有一個規則:日誌對效能的負載不能超過10%。否則會出現效能問題。
- 將所有數據記錄到一個文件中。這將使讀取/寫入在某個時刻變得非常困難,更不用說某些系統上有檔案大小限制。
- 使用不正確的日誌記錄等級。每個日誌記錄等級都有明確的界限,應該受到尊重。如果邊界模糊,您可以商定使用哪個層級。
記錄層級
|
|
|
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:表示包含警告的日誌。儘管系統拒絕並完成了請求,但發生了意外的操作;
- INFO:記錄應用程式中重要操作的日誌。這些不是錯誤,這些不是警告,這些是系統的預期操作;
- DEBUG:調試應用程式所需的日誌。確保系統完全按照預期執行,或描述系統的操作:「method1開始工作」;
- TRACE:用於偵錯的較低優先權日誌,具有最低的日誌記錄等級;
- ALL:記錄系統所有日誌的等級。
事實證明,如果在應用程式中的某個位置啟用了 INFO 日誌記錄級別,則所有級別都會被記錄,從 INFO 開始一直到 FATAL。如果日誌等級為FATAL,則僅記錄該等級的日誌。
記錄與傳送日誌:Appender
我們將使用 log4j 作為範例來考慮此過程:它提供了充足的記錄/發送日誌的機會:
- 用於寫入檔案 - 解決方案DailyRollingFileAppender;
- 將資料接收到應用程式控制台 - ConsoleAppender;
- 將日誌寫入資料庫 - JDBCAppender;
- 透過 TCP/IP 控制傳輸 - TelnetAppender;
- 確保日誌記錄不會影響效能 - AsyncAppender。
還有其他幾種實現:可以
在此處找到完整列表。順便說一句,如果所需的附加程式不可用,這不是問題。
您可以透過實作Appender介面來編寫自己的appender ,該介面僅接受log4j。
記錄節點
為了進行演示,我們將使用 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 log4j.properties. Please, provide them");
logger.error("Connection refused to host = {}", host);
儘管我們傳遞的是類,但最終寫下的是類別和包的全名。這樣做是為了讓您可以將日誌記錄劃分為節點,並為每個節點配置日誌記錄等級和附加程式。例如,類別的名稱:
com.github.romankh3.logginglecture.MainDemo
- 在其中建立了一個記錄器。這就是它可以被劃分為日誌節點的方式。主節點是 null
RootLogger。這是接收整個應用程式所有日誌的節點。其餘部分可以如下所示:
Appender 專門在日誌節點上配置其工作。
現在,以 log4j.properties為例,我們將了解如何配置它們。
逐步配置Log4j.properties
現在我們將逐步設定一切,看看可以做什麼:
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
這一行表示我們正在註冊一個使用 org.apache.log4j.ConsoleAppender 實作的 CONSOLE 附加程式。該附加程式將資料寫入控制台。接下來,讓我們註冊另一個將寫入檔案的附加程式:
log4j.appender.FILE=org.apache.log4j.RollingFileAppender
值得注意的是,附加程式仍然需要配置。一旦我們已經註冊了附加程序,我們就可以確定節點中的日誌記錄等級以及將使用哪些附加程序。
log4j.rootLogger=偵錯、控制台、文件
- log4j.rootLogger 表示我們將配置主節點,其中包含所有日誌;
- 等號後面的第一個單字表示日誌將記錄在哪個等級及更高等級(在我們的例子中,這是 DEBUG);
- 然後在逗號之後指示將使用的所有附加程式。
若要設定特定的日誌記錄節點,您需要使用下列條目:
log4j.logger.com.github.romankh3.logginglecture=TRACE, OWN, CONSOLE
它用於
log4j.logger.
配置特定節點,在我們的例子中它是
com.github.romankh3.logginglecture.
現在讓我們討論設定 CONSOLE 附加程式:
# CONSOLE appender customisation
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
在這裡我們看到我們可以設定附加程式處理的層級。真實情況:日誌節點收到 info 等級的訊息,並將其傳遞給指派給它的appender,但是 warn 等級及更高等級的appender 接受了該日誌,但沒有對其執行任何操作。接下來,您需要決定在訊息中使用什麼模板。我在範例中使用 PatternLayout,但有很多解決方案。它們不會在本文中披露。設定 FILE 附加程式的範例:
# File appender customisation
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,我們將使用它;如果沒有,那麼這裡有一篇描述如何連接該庫的文章的連結。
讓我們總結一下
- 我們討論了Java有哪些解決方案。
- 幾乎所有已知的日誌庫都是在一個人的控制下編寫的:D
- 我們了解了哪些內容需要記錄,哪些內容不需要記錄。
- 我們計算出了日誌記錄等級。
- 我們熟悉了日誌節點。
- 我們了解了附加程式是什麼以及它的用途。
- 我們一步步配置了log4j.proterties檔。
附加材料
- JavaRush:日誌記錄。展開 strace 球
- JavaRush:記錄器講座
- Habr:Java 日誌記錄。你好世界
- Habr:Java 日誌記錄:惡夢的故事
- Youtube:戈洛瓦赫課程。記錄。第 1 部分、第 2 部分、第 3 部分、第 4 部分
- Log4j:附加程序
- Log4j:佈局
另請參閱我的其他文章:
GO TO FULL VERSION