## 前言 日誌框架(Logging Framework)主要用於記錄程式執行過程中的一般訊息或錯誤訊息。 有別於原生語言的`System.out.println()`語法,Logging Framework可以更有效幫助開發人員追蹤系統的執行狀況、分析錯誤和效能等,且Logging Framework普遍提供多種靈活的配置方式,如支持多個輸出方式、輸出目的地、日誌等級和格式等。 以Java來說,常見的Logging Framework,如:JUL(Java util Logging)、Logback、Log4j、Log4j2等。 而多數的Java Logging Framework 均由 Logger、Appender、Layout 三個核心Component組合而成。 ![Log4j (1)](https://hackmd.io/_uploads/HJwOsXyhC.jpg) 後續將以 Apache 所發佈的 Log4j2 Framework 為例,進行說明與示範。 </br> ## Dependency 可使用Maven或Gradle引入 Maven ``` <dependencies> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-api</artifactId> <version>2.20.0</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.20.0</version> </dependency> </dependencies> ``` Gradle ``` dependencies { implementation 'org.apache.logging.log4j:log4j-api:2.20.0' implementation 'org.apache.logging.log4j:log4j-core:2.20.0' } ``` </br> ## 1. Logger(記錄器) Logger主要負責控制與記錄Log,每個Logger可使用類別名或自定義名稱取得Logger實例。 ``` public class MainClass{ //以類別名取得Logger實例 private static final Logger logger = LogManager.getLogger(MainClass.class); //以自定義的Logger名稱取得Logger實例 private static final Logger myLogger = LogManager.getLogger("MyLogger"); } ``` ### 1.1. Level(等級) Logger可控制不同的日誌等級,主要分為以下等級,由左最低至右最高。 `trace < debug < info < warn < error < fatal` </br> 一般來說,開發人員會依據所記錄訊息的重要性,使用不同日誌等級的方法記錄。 ``` logger.debug("print test log"); logger.info("print test log"); logger.warn("print test log"); logger.error("print test log"); ``` 如遇系統錯誤,也可連同Exception一起輸出,方便後續追蹤問題 ``` try{ //... }catch(IOException e){ logger.error("System error.", e); } ``` </br> 而Logger會依據所設定的日誌等級,將該等級(含)以上的所有Log進行輸出,低於該等級的Log則忽略不記。 若Logger的等級設置為`error`可看到`error、fatal`的Log 若Logger的等級設置為`info`可看到`info、warn、error、fatal`的Log 以此類推 </br> ## 2. Appender Appender負責將Logger所產生的Log,輸出至指定的目的地,如:檔案、主控台、網路或資料庫等。 | **Appender 類型** | **描述** | |--------------------------|------------------------------------------------------------------------| | **ConsoleAppender** | 將 Log 輸出到控制台 (Console) | | **FileAppender** | 將 Log 寫入到指定的檔案 | | **RollingFileAppender** | 將 Log 寫入到檔案,並根據設定的條件(如檔案大小或日期)自動滾動生成新檔案 | | **SocketAppender** | 將 Log 發送到遠端伺服器 | | **JDBCAppender** | 將 Log 寫入到資料庫(DataBase) | | **MailAppender** | 將 Log 發送至 Mail | | **KafkaAppender** | 將 Log 發送到 Kafka 叢集 | </br> Apache Log4j2 Framework支援使用XML、YAML以及JSON等方式定義Appender [XML] ``` <Configuration status="WARN"> <Appenders> <!-- 定義 RollingFileAppender,每日自動滾動產生新檔案--> <RollingFile name="MyAppender" fileName="logs/System.log" filePattern="logs/System-%d{yyyy-MM-dd}.log"> <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} %-5level %logger{36} - %msg%n"/> <Policies> <TimeBasedTriggeringPolicy interval="1" modulate="true"/> </Policies> </RollingFile> <!-- 定義 ConsoleAppender --> <Console name="ConsoleAppender" target="SYSTEM_OUT"> <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} %-5level %logger{36} - %msg%n"/> </Console> </Appenders> <Loggers> <!-- 定義Logger Level,並指定輸出至MyAppender與ConsoleAppender --> <Logger name="MyLogger" level="info" additivity="false"> <AppenderRef ref="MyAppender"/> <AppenderRef ref="ConsoleAppender"/> </Logger> <!-- Root Logger --> <Root level="info"> <AppenderRef ref="MyAppender"/> </Root> </Loggers> </Configuration> ``` </br> [YAML] ``` Configuration: status: WARN Appenders: # 定義 RollingFileAppender,每日自動滾動產生新檔案 RollingFile: name: MyAppender fileName: logs/System.log filePattern: logs/System-%d{yyyy-MM-dd}.log PatternLayout: pattern: "%d{yyyy-MM-dd HH:mm:ss} %-5level %logger{36} - %msg%n" Policies: TimeBasedTriggeringPolicy: interval: 1 modulate: true # 定義 ConsoleAppender Console: name: ConsoleAppender target: SYSTEM_OUT PatternLayout: pattern: "%d{yyyy-MM-dd HH:mm:ss} %-5level %logger{36} - %msg%n" Loggers: # 定義 Logger Level,並指定輸出至 MyAppender 與 ConsoleAppender Logger: name: MyLogger level: info additivity: false AppenderRef: - ref: MyAppender - ref: ConsoleAppender # Root Logger Root: level: info AppenderRef: - ref: MyAppender ``` </br> ## 3. Layout Layout用於格式化Appender輸出的Log,其中以PatternLayout最為常見 ``` <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} %-5level %logger{36} - %msg%n"/> ``` | 模式字符 | 描述 | 範例 | |----------|--------------------------------|--------------------------------| | `%d{pattern}` | 日誌事件的時間戳記 | `%d{yyyy-MM-dd HH:mm:ss}` | | `%p` | 日誌級別(例如 INFO、DEBUG) | `INFO` | | `%c` | 記錄器名稱(例如類別名或自定義名稱) | `com.example.MyClass` | | `%m` | 日誌訊息內容 | `This is a log message` | | `%n` | 換行符號 | `\n`或`\r\n` | | `%t` | 執行緒名稱 | `main` | </br> ## 結語 通過 Logger、Appender、Layout 這三個核心 Component,使 Log4j2 Framework 可提供一個靈活且功能強大的日誌系統,支持多種日誌輸出方式和格式,以滿足不同的日誌需求。 </br> ## Log4j2 Framework 重大風險漏洞 2021年12月9日 Apache 公布 Log4j2 Framework 存在 Log4Shell 漏洞,其 CVE 編號為 CVE-2021-44228,屬於最嚴重等級10分的重大風險漏洞(滿分10分)。 該漏洞可讓駭客透過JNDI(java naming and directory Interface)的方式,遠端執行程式碼,進而達到控制系統或伺服器,如:癱瘓系統、竊取使用者個人資料、信用卡資料或將其感染為殭屍主機等。 其中因 Java 以 Log4j2 與 Logback 兩大 Logging Framework 最為常見,故影響全球數以百萬台設備。 受影響的 Log4j2 版本為 2.0~2.14.1,後續於Apache Log4j 2.15.0版本修復此風險漏洞。 >[!Tip] 文章內容皆為個人整理的觀點,以及整理後的個人想法,如內容有錯誤或疑慮的部分,歡迎提出討論,收到後會盡快修正!