---
# System prepended metadata

title: Apache Log4j 2 Framework
tags: [Log]

---

## 前言
日誌框架(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] 
文章內容皆為個人整理的觀點，以及整理後的個人想法，如內容有錯誤或疑慮的部分，歡迎提出討論，收到後會盡快修正！
