---
# System prepended metadata

title: JAVA Log4j
tags: [JAVA]

---

# JAVA Log4j

###### tags: `JAVA`

參考：[Log4j 簡介](https://www.funtl.com/zh/log4j/)

Youtube 影片教學：[千锋Java教程：36 框架入门 Log4j Log4j 简介](https://www.youtube.com/watch?v=7slS8Lqze-o&list=PLwDQt7s1o9J6Q20ixLbEcCosXx54h5p4P&index=36)

## Log4j 簡介

一個完整的軟件，日誌是必不可少的。程序從開發、測試、維護、運行等環節，都需要向控制台或文件等位置輸出大量信息。這些信息的輸出， 在很多時候是使用 System.out.println() 無法完成的。

日誌信息根據用途與記錄內容的不同，分為 調試日誌、運行日誌、異常日誌 等。

Log4j 的全稱為 Log for java，即專門用於 Java 語言的日誌記錄工具。

## Log4j 日誌級别

參考：[Log4j 日誌級别](https://www.funtl.com/zh/log4j/Log4j-%E6%97%A5%E5%BF%97%E7%BA%A7%E5%88%AB.html)

為了方便對於日誌信息的輸出顯示，對日誌內容進行了分級管理。日誌級別由高到低，共分 6 個級別：

* fatal(致命的)
* error
* warn
* info
* debug
* trace(堆棧)

### 為什麼要對日誌進行分級

無論是將日誌輸出到控制台，還是文件，其輸出都會降低程序的運行效率。但由於調試、運行維護的需要，客戶的要求等原因，需要進行必要的日誌輸出。這時就必須要在代碼中加入日誌輸出語句。

這些輸出語句若在程序運行時全部執行， 則勢必會降低運行效率。例如， 使用 System.out.println() 將信息輸出到控制台，則所有的該輸出語句均將執行。會大大降低程序的執行效率。而要使其不輸出，唯一的辦法就是將這些輸出語句逐個全部刪除。這是個費時費力的過程。

將日誌信息進行分級管理，便可方便的控制信息輸出內容及輸出位置：哪些信息需要輸出，哪些信息不需要輸出，只需在一個日誌輸出控製文件中稍加修改即可。而代碼中的輸出語句不用做任何修改。

從這個角度來說，代碼中的日誌編寫，其實就是寫大量的輸出語句。只不過，這些輸出語句比較特殊，它們具有級別，在程序運行期間不一定被執行。它們的執行是由另一個控製文件控制。

## Log4j 日誌輸出控制文件

參考：[Log4j 日志输出控制文件](https://www.funtl.com/zh/log4j/Log4j-%E6%97%A5%E5%BF%97%E8%BE%93%E5%87%BA%E6%8E%A7%E5%88%B6%E6%96%87%E4%BB%B6.html)

### 日誌輸出簡介

Log4j 的日誌輸出控制文件，主要由三個部分構成：

* 日誌信息的輸出位置：控制日誌信息將要輸出的位置，是控制台還是文件等。
* 日誌信息的輸出格式：控制日誌信息的顯示格式，即以怎樣的字符串形式顯示。
* 日誌信息的輸出級別：控制日誌信息的顯示內容，即顯示哪些級別的日誌信息。


有了日誌輸出控制文件，代碼中只要設置好日誌信息內容及其級別即可，通過控制文件便可控制這些日誌信息的輸出了。

### 日誌屬性配置文件

日誌屬性文件 `log4j.properties` 是專門用於控制日誌輸出的。其主要進行三方面控制：

* 輸出位置：控制日誌將要輸出的位置，是控制台還是文件等。
* 輸出佈局：控制日誌信息的顯示形式。
* 輸出級別：控制要輸出的日誌級別。


日誌屬性文件由兩個對象組成：日誌附加器與根日誌。

根日誌，即為 Java 代碼中的日誌記錄器，其主要由兩個屬性構成：日誌輸出級別與日誌附加器。

日誌附加器，則由日誌輸出位置定義，由其它很多屬性進行修飾，如輸出佈局、文件位置、文件大小等。

### 什麼是日誌附加器？

所謂日誌附加器，就是為日誌記錄器附加上很多其它設置信息。附加器的本質是一個接口，其定義語法為：`log4j.appender.appenderName` = `輸出位置`

### 常用的附加器實現類別

* `org.apache.log4j.ConsoleAppender`：日誌輸出到控制台
* `org.apache.log4j.FileAppender`：日誌輸出到文件
* `org.apache.log4j.RollingFileAppender`：當日誌文件大小到達指定尺寸的時候將產生一個新的日誌文件
* `org.apache.log4j.DailyRollingFileAppender`：每天產生一個日誌文件

### 常用佈局類型

* `org.apache.log4j.HTMLLayout`：網頁佈局，以 HTML 表格形式佈局
* `org.apache.log4j.SimpleLayout`：簡單佈局，包含日誌信息的級別和信息字符串
* `org.apache.log4j.PatternLayout`：匹配器佈局，可以靈活地指定佈局模式。其主要是通過設置 PatternLayout 的 ConversionPattern 屬性值來控制具體輸出格式的 。

打印參數: Log4J 採用類似 C 語言中的 printf 函數的打印格式格式化日誌信息

* `%m`：輸出代碼中指定的消息
* `%p`：輸出優先級，即 `DEBUG`，`INFO`，`WARN`，`ERROR`，`FATAL`
* `%r`：輸出自應用啟動到輸出該 log 信息耗費的毫秒數
* `%c`：輸出所屬的類目，通常就是所在類的全名
* `%t`：輸出產生該日誌事件的線程名
* `%n`：輸出一個回車換行符，Windows 平台為 `/r/n`，Unix 平台為 `/n`
* `%d`：輸出日誌時間點的日期或時間，默認格式為ISO8601，也可以在其後指定格式，比如：`%d{yyy MMM dd HH:mm:ss , SSS}`，輸出類似：`2002年10月18日22:10:28,921`
* %l：輸出日誌事件的發生位置，包括類目名、發生的線程，以及在代碼中的行數。舉例：`Testlog4.main(TestLog4.java: 10 )`

## 第一個 Log4j 日誌文件

參考：[第一个 Log4j 日志文件](https://www.funtl.com/zh/log4j/%E7%AC%AC%E4%B8%80%E4%B8%AA-Log4j-%E6%97%A5%E5%BF%97%E6%96%87%E4%BB%B6.html)

### Slf4j 簡介

slf4j 的全稱是 `Simple Loging Facade For Java`，即它僅僅是一個為 Java 程序提供日誌輸出的統一接口，並不是一個具體的日誌實現方案，就比如 JDBC 一樣，只是一種規則而已。所以單獨的 slf4j 是不能工作的，必須搭配其他具體的日誌實現方案，比如 apache 的 `org.apache.log4j.Logger`，JDK 自帶的 `java.util.logging.Logger` 以及 `log4j` 等。

### 我的 pom.xml 配置

參考原本的文章做修改後如下：

```xml=
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>idv.ete</groupId>
  <artifactId>log4jTest</artifactId>
  <packaging>jar</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>log4jTest</name>
  <url>http://maven.apache.org</url>
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-log4j12</artifactId>
      <version>1.7.25</version>
    </dependency>
  </dependencies>
  <build> 
    <plugins>   
      <plugin>    <!--plugins要写在build标签下，不然无法识别 -->
        <artifactId>maven-compiler-plugin</artifactId>   
        <version>3.1</version>    <!--maven-compiler-plugin版本写一个中央仓库有的版本，保存后它会自动下载 -->
        <configuration>   
          <source>10</source>    <!--我的jdk是10.0.1的，写成10.0.1不好用，所以就写成10了 -->
          <target>10</target>   
        </configuration>   
      </plugin> 
      <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-shade-plugin</artifactId>
          <version>3.2.4</version>
          <executions>
            <execution>
              <phase>package</phase>
              <goals>
                <goal>shade</goal>
              </goals>
            </execution>
          </executions>
      </plugin>
    </plugins> 
  </build>
</project>
```

### 創建 `log4j.properties` 配置文件

注意：檔案名字要是 log4j.properties 要一模一樣。

在 `src/main/resources` 目錄下創建名為 `log4j.properties` 的屬性配置文件

```java=
log4j.rootLogger=INFO, console, file

log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%d %p [%c] - %m%n

log4j.appender.file=org.apache.log4j.DailyRollingFileAppender
log4j.appender.file.File=logs/log.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.A3.MaxFileSize=1024KB
log4j.appender.A3.MaxBackupIndex=10
log4j.appender.file.layout.ConversionPattern=%d %p [%c] - %m%n
```

日誌配置相關說明：

* `log4j.rootLogger`：根日誌，配置了日誌級別為 `INFO`，預定義了名稱為 `console`、`file` 兩種附加器
* `log4j.appender.console`：console 附加器，日誌輸出位置在控制台
* `log4j.appender.console.layout`：console 附加器，採用匹配器佈局模式
* `log4j.appender.console.layout.ConversionPattern`：console 附加器，日誌輸出格式為：`日期 日誌級別 [類名] - 消息換行符`
* `log4j.appender.file`：file 附加器，每天產生一個日誌文件
* `log4j.appender.file.File`：file 附加器，日誌文件輸出位置 `logs/log.log`
* `log4j.appender.file.layout`：file 附加器，採用匹配器佈局模式
* `log4j.appender.A3.MaxFileSize`：日誌文件最大值
* `log4j.appender.A3.MaxBackupIndex`：最多紀錄文件數
* `log4j.appender.file.layout.ConversionPattern`：file 附加器，日誌輸出格式為：`日期 日誌級別 [類名] - 消息換行符`

### 測試日誌輸出

創建一個測試類別，並測試日誌輸出效果，參考原本文章的程式碼修改後如下：


```java=
package idv.ete;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class App 
{

    public static final Logger logger = LoggerFactory.getLogger(App.class);

    public static void main( String[] args )
    {
        System.out.println( "Hello World!" );
        logger.info("slf4j for info");
        logger.debug("slf4j for debug");
        logger.error("slf4j for error");
        logger.warn("slf4j for warn");

        String message = "Hello SLF4J";
        logger.info("slf4j message is : {}", message);
    }
}
```

執行後 Console 顯示如下：

![](https://i.imgur.com/wO8rBkz.png)

項目根目錄下也會多出 `logs/log.log` 目錄及文件

![](https://i.imgur.com/uYsUDxG.png)

![](https://i.imgur.com/HqtdKZw.png)

