埃裏克·迪特里希算法
2018 年 9 月 5 日apache
軟件開發中最煩人的方面之一絕對是日誌記錄。若是一個非平凡的應用程序缺乏日誌記錄,那麼維護它的人都會遇到困難,過後調試將主要是一個猜謎遊戲。今天,咱們經過提供有關 log4j2 配置的教程,爲解決這種狀況作出了另外一貢獻。json
有不少方法能夠爲Java記錄日誌。您可使用更手動的方法,但推薦的方法是採用專用的日誌記錄框架。這就是爲何咱們要介紹 Apache log4j2,它是 log4j 的改進版本,是行業標準的日誌框架之一。api
咱們將首先快速回顧一下咱們以前關於 Java 日誌的文章,並介紹一些關於 log4j2 的事實。而後咱們將繼續介紹咱們在上一個教程中開始編寫的示例應用程序的狀態。數組
以後,咱們進入文章的重點。您將學習如何配置 log4j2,從基礎開始,並逐步學習更高級的主題,例如日誌格式、附加程序、日誌級別和日誌層次結構。網絡
讓咱們開始吧。app
本教程中,咱們使用了 log4j 版本 2,這是來自 Apache 項目的日誌框架。框架
讓咱們進一步瞭解 Java 應用程序日誌並查看 log4j2 配置。今天咱們將介紹 log4j2 配置的基本方面,以幫助您入門。運維
Log4j 的功能使其成爲 Java 最流行的日誌框架之一。它能夠配置爲多個日誌記錄目的地和各類日誌文件格式。函數
能夠在單個類級別過濾和定向日誌消息,從而使開發人員和運維人員可以對應用程序消息進行精細控制。
讓咱們經過使用命令行 Java 應用程序配置 log4j 來檢查這些機制。
讓咱們使用 log4j 進行日誌記錄的應用程序。
package com.company; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; public class Main { private static final Logger logger = LogManager.getLogger(Main.class); public static void main(String[] args) { String message = "Hello there!"; logger.trace(message); logger.debug(message); logger.info(message); logger.warn(message); logger.error(message); logger.fatal(message); } }
咱們在每一個 log4j 預約義的日誌級別記錄相同的消息:跟蹤、調試、信息、警告、錯誤和致命。
咱們將使用 log4j 的 YAML 文件格式,所以您須要向pom.xml(或build.gradle)添加一些額外的依賴項。
<dependencies> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-api</artifactId> <version>2.12.1</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.12.1</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.dataformat</groupId> <artifactId>jackson-dataformat-yaml</artifactId> <version>2.10.0</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.10.0</version> </dependency> </dependencies>
設置此代碼,以便您可使用您喜歡的 Java 工具構建和運行它。
讓咱們在沒有 log4j 配置文件的狀況下運行咱們的應用程序。若是您已經有一個,請將其刪除或將其移動到另外一個文件名,以便 log4j 將忽略它。
當咱們運行應用程序時,咱們會在控制檯上看到:
09:38:14.114 [main] ERROR com.company.Main - Hello there! 09:38:14.119 [main] FATAL com.company.Main - Hello there!
六個日誌消息中的兩個,指定爲「錯誤」和「致命」的消息被髮送到控制檯。
Log4j 有一個默認配置。它將記錄到控制檯,顯示歸類爲「錯誤」或更高級別的消息。
瞭解 log4j 在沒有配置文件的狀況下如何運行頗有用,但讓咱們看看如何根據咱們的須要設置它。
咱們能夠經過log4j.configurationFile系統屬性在特定位置爲 log4j 提供配置文件。這是它將查找配置文件的第一個位置。
若是 log4j 找不到系統屬性,它會在類路徑中查找文件。因爲 log4j 版本 2 支持四種不一樣的文件格式和兩種不一樣的文件命名約定,所以定位文件的規則很複雜。在咱們介紹了不一樣的選項後,咱們將討論它們。
Log4j 將加載 Java 屬性和 YAML、JSON 和 XML 配置文件。它經過檢查文件擴展名來識別文件格式。
log4j.configurationFile
系統屬性指定的文件必須具備這些文件擴展名之一,但能夠具備任何基本名稱。Log4j 將根據擴展名指示的格式對其進行解析。
當 log4j 掃描文件的類路徑時,它會按照上面列出的順序掃描每種格式,並在找到匹配項時中止。
例如,若是它找到一個 YAML 配置,它將中止搜索並加載它。若是沒有 YAML 文件但它找到了 JSON,它將中止搜索並使用它。
當 log4j 掃描類路徑時,它會查找兩個文件名之一:log4j2-test.[extension] 或 log4j2.[extension]。
它首先加載測試文件,爲開發人員提供了一種方便的機制,能夠在不改變標準配置的狀況下強制應用程序在調試或跟蹤級別進行記錄。
當咱們將文件格式和名稱的規則放在一塊兒時,咱們能夠看到log4j的自我配置算法。
若是如下任何步驟成功,log4j 將中止並加載生成的配置文件。
log4j 有 12 個潛在的配置文件名。若是應用程序在生產環境中記錄了沒必要要的消息,則加載錯誤的信息可能會致使日誌信息丟失或性能降低。
在部署代碼以前,請確保您的應用程序只有一個配置文件,而且您知道它在哪裏。若是您堅持從類路徑加載配置,請在發佈代碼以前掃描虛假文件。
如今咱們知道如何爲 log4j 提供配置,讓咱們建立一個並使用它來自定義咱們的應用程序。
讓咱們從默認配置開始,而後從那裏修改咱們應用程序的行爲。咱們將從 log4j 的配置規則中獲取提示並使用 YAML。
默認配置以下所示:
Configuration: status: warn Appenders: Console: name: Console target: SYSTEM_OUT PatternLayout: Pattern: "%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" Loggers: Root: level: error AppenderRef: ref: Console
使用這些內容建立一個文件名log4j2.yaml並將log4j.configurationFile設置 爲指向其位置。
接下來,運行應用程序。您將看到與之前相同的輸出。
09:38:14.114 [main] ERROR com.company.Main - Hello there! 09:38:14.119 [main] FATAL com.company.Main - Hello there!
咱們已經控制了應用程序的日誌記錄配置。如今讓咱們改進它。
第一步是將咱們的日誌從控制檯中取出並放入一個文件中。爲此,咱們須要瞭解appender。
Appenders 將日誌消息放在它們所屬的地方。默認配置提供一個控制檯附加程序。顧名思義,它將消息附加到控制檯。
Appenders: Console: name: Console target: SYSTEM_OUT PatternLayout: Pattern: "%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"
咱們想要一個文件附加程序。讓咱們替換咱們的控制檯 appender。
Appenders: File: name: File_Appender fileName: logfile.log PatternLayout: Pattern: "%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"
文件附加器有一個name,就像控制檯附加器同樣。可是他們有一個fileName而不是target。
與控制檯 appender 相似,它們也有一個PatternLayout,咱們將在下面介紹。
這個名字不只僅是爲了展現。若是咱們想用文件附加器替換控制檯附加器,咱們須要讓咱們的記錄器 知道在哪裏放置咱們的日誌消息。
所以,將記錄器中的ref值更改成文件附加程序的名稱。
Loggers: Root: level: error AppenderRef: ref: File_Appender
如今,從新運行應用程序。它沒有記錄到控制檯,而是將消息放在工做目錄中名爲logfile.log 的文件中。咱們已將日誌移至文件中!
在咱們的日誌文件中,咱們仍然只看到了六個日誌消息中的兩個。讓咱們談談記錄器以及它們如何管理日誌消息。
咱們的基本配置定義了一個記錄器。
Loggers: Root: level: error AppenderRef: ref: File_Appender
它有一個「錯誤」級別,因此它只打印錯誤或致命的消息。
當記錄器收到日誌消息時,它會根據其配置的級別傳遞或過濾它。此表顯示了記錄器配置和日誌消息級別之間的關係。
所以,若是咱們更改記錄器的級別,咱們將看到更多消息。將其設置爲「調試」。
Loggers: Root: level: debug AppenderRef: ref: File_Appender
接下來,從新運行程序。應用程序記錄全部調試級別或更高級別的消息。
記錄器層次結構
Log4j 按層次結構排列記錄器。這使得爲各個類指定不一樣的配置成爲可能。
讓咱們更改咱們的應用程序,看看它的實際效果。
public class Main { private static final Logger logger = LogManager.getLogger(Main.class); public static void main(String[] args) { String message = "Hello there!"; System.out.println(message); logger.debug(message); logger.info(message); logger.error(message); LoggerChild.log(); } private static class LoggerChild { private static final Logger childLogger = LogManager.getLogger(LoggerChild.class); static void log() { childLogger.debug("Hi Mom!"); } } }
咱們添加了一個內部類來建立一個記錄器並用它記錄一條消息。
以後主要作了記錄,它調用LoggerChild 。
若是咱們使用當前配置運行它,咱們會看到新消息,而且它是從不一樣的類記錄的。
12:29:23.325 [main] DEBUG com.company.Main - Hello there! 12:29:23.331 [main] INFO com.company.Main - Hello there! 12:29:23.332 [main] ERROR com.company.Main - Hello there! 12:29:23.333 [main] DEBUG com.company.Main.LoggerChild - Hi Mom!
記錄器具備相似於 Java 的類層次結構。全部記錄器都是迄今爲止咱們一直在使用的根記錄器的後代。
缺乏任何特定配置的記錄器繼承根配置。
因此當 Main 和 LoggerChild 使用它們的類名建立記錄器時,這些記錄器繼承了 Root 的配置,即向 File_Appender發送調試級別和更高級別的消息 。
咱們能夠覆蓋這兩個記錄器的指定配置。
Loggers: logger: - name: com.company.Main level: error additivity: false AppenderRef: ref: File_Appender - name: com.company.Main.LoggerChild level: debug additivity: false AppenderRef: ref: File_Appender Root: level: debug AppenderRef: ref: File_Appender
記錄器在記錄器部分命名 。因爲咱們列出了兩個,所以咱們使用 YAML 數組語法。
咱們設置 com.company.Main的 記錄爲「錯誤」,並 com.company.Main.LoggerChild的 爲「調試」。
該加設置控制的log4j是否會從記錄儀的祖先將消息發送給後代。
若是設置爲 true,兩個記錄器將處理相同的消息。某些系統但願將相同的消息添加到兩個不一樣的日誌中。咱們不但願出現這種行爲,所以咱們覆蓋了默認值並指定了 false。
如今再次運行程序:
12:33:11.062 [main] ERROR com.company.Main - Hello there! 12:33:11.073 [main] DEBUG com.company.Main.LoggerChild - Hi Mom!
咱們只看到來自Main的錯誤消息, 但仍然看到來自LoggerChild的調試消息!
就像咱們能夠擁有多個 logger 同樣,咱們也能夠擁有多個 appender。
讓咱們對咱們的配置進行一些更改。
添加第二個文件附加程序。爲此,請使用原始 appender 建立一個列表,並使用不一樣的名稱和文件建立第二個列表。您的Appenders部分應以下所示:
Appenders: File: - name: File_Appender fileName: logfile.log PatternLayout: Pattern: "%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" - name: Child_Appender fileName: childlogfile.log PatternLayout: Pattern: "%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"
接下來,將 LoggerChild 記錄器指向新的 appender。您的記錄器部分將以下所示。
Loggers: logger: - name: com.company.Main level: error additivity: false AppenderRef: ref: File_Appender - name: com.company.Main.LoggerChild level: debug additivity: false AppenderRef: ref: Child_Appender Root: level: debug AppenderRef: ref: File_Appender
如今運行該應用程序,您將看到兩個不一樣的日誌文件,每一個文件都包含來自其關聯類的消息。
日誌消息格式
咱們的每一個 appender 都有一個 PatternLayout。
PatternLayout:
Pattern: "%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"
PatternLayout 是 Log4j 佈局類的一個實例。Log4j 具備用於以 CSV、JSON、Syslog 和各類不一樣格式記錄消息的內置佈局。
PatternLayout 有一組用於格式化消息的運算符,其操做相似於 C 的sprintf函數。經過指定模式,咱們能夠控制由 appender 寫入的日誌消息的格式。
咱們的佈局字符串以下所示:
"%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"
每一個 % 對應於日誌消息中的一個字段。
PatternLayout有許多額外的操做符。
隨着 appender 和 loggers 的增長,配置文件可能會變得重複。Log4j 支持變量替換以幫助減小重複並使其更易於維護。讓咱們使用Properties 來優化咱們的配置。
Configuration: status: warn Properties: property: - name: "LogDir" value: "logs" - name: "DefaultPattern" value: "%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" Appenders: File: - name: File_Appender fileName: ${LogDir}/logfile.log PatternLayout: Pattern: ${DefaultPattern} - name: Child_Appender fileName: ${LogDir}/childlogfile.log PatternLayout: Pattern: ${DefaultPattern} Loggers: logger: - name: com.company.Main level: error additivity: false AppenderRef: ref: File_Appender - name: com.company.Main.LoggerChild level: debug additivity: false AppenderRef: ref: Child_Appender Root: level: debug AppenderRef: ref: File_Appender
在文件的頂部,咱們聲明瞭兩個屬性,一個名爲LogDir,另外一個名爲DefaultPattern。
聲明屬性後,可使用大括號和美圓符號在配置中使用它: ${LogDir} 或${DefaultPattern}
LogDir 是咱們添加到兩個日誌文件名稱中的子目錄名稱。當咱們運行應用程序時,log4j 將建立這個目錄並將日誌文件放在那裏。
咱們指定DefaultPattern做爲咱們兩個日誌文件的模式佈局,將定義移到一個地方。若是咱們想修改咱們的日誌文件格式,咱們如今只需擔憂更改一次。
Log4j 還能夠從環境中導入屬性。您能夠在此處找到詳細信息 。
例如,若是咱們想從 Java 系統屬性導入日誌文件目錄,咱們在 log4j 配置中將其指定爲${sys : LogDir}並將 LogDir 系統屬性設置爲所需的目錄。
Log4j 能夠按期從新加載其配置,使咱們可以在不從新啓動應用程序的狀況下更改應用程序的日誌記錄配置。
將monitorInterval設置添加到文件的Configuration部分,log4j 將按指定的時間間隔掃描文件。
Configuration: monitorInterval: 30
間隔以秒爲單位指定。
Log4j 是一個強大的日誌框架,它容許咱們將應用程序配置爲以各類不一樣的方式登陸,並對不一樣組件如何使用日誌文件進行精細控制。
本教程涵蓋了配置 log4j 的基本方面,但還有不少東西須要學習。您能夠在項目網站上了解 log4j 配置 。
這篇文章是由 Eric Goebelbecker 撰寫的。Eric在紐約市的金融市場工做了 25 年,爲市場數據和金融信息交換 (FIX) 協議網絡開發基礎設施。他喜歡談論什麼使團隊有效(或不那麼有效!)