日志系统($\text {Logging System}$) vs 日志门面($\text {Logging Facade}$)

  • 日志系统是具体的日志实现,直接负责日志的记录、输出和管理。
  • 日志门面是抽象层,提供统一的接口来使用不同的日志系统。

1 日志系统

1.1 JUL (java.util.logging)

从 JDK1.4(2002 年)开始内置的官方日志库。

1.2 Log4j

  1. Logger(记录器):负责捕获记录信息。
  2. Appender(输出源):负责发布日志信息,以不同的首选目的地。
  3. Layout(布局):负责格式化不同风格的日志信息。

1.3 Logback

  1. logback-core
  2. logback-classic
  3. logback-access

1.4 Log4j2

2 日志门面

2.1 SLF4J (Simple Logging Facade for Java)

http://www.slf4j.org/

2.2 Commons Logging

https://commons.apache.org/proper/commons-logging/

3 日志库实践

推荐使用 $\text {SLF4J + Logback}$。

日志级别:TRACE < DEBUG < INFO < WARN < ERROR

3.1 Maven 依赖配置

1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.16</version>
</dependency>

<!-- https://mvnrepository.com/artifact/ch.qos.logback/logback-classic -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.5.16</version>
</dependency>

3.2 Logback 配置文件

src/main/resources 目录下创建 logback.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<property name="LOG_PATTERN"
value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"/>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${LOG_PATTERN}</pattern>
</encoder>
</appender>

<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/application.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>logs/application-%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${LOG_PATTERN}</pattern>
</encoder>
</appender>

<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE"/>
</root>

<logger name="com.solisamicus" level="DEBUG"/>
</configuration>
  1. 日志格式定义
1
2
<property name="LOG_PATTERN" 
value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"/>
  • %d{yyyy-MM-dd HH:mm:ss.SSS} - 时间戳
  • [%thread] - 线程名
  • %-5level - 日志级别,左对齐5个字符
  • %logger{36} - logger名称,最大36个字符
  • %msg - 日志消息
  • %n - 换行符
  1. 控制台输出配置:使用之前定义的 LOG_PATTERN 格式,定义了名为"CONSOLE"的追加器,使用 ConsoleAppender 输出到控制台。
1
2
3
4
5
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${LOG_PATTERN}</pattern>
</encoder>
</appender>
  1. 文件输出配置:
1
2
3
4
5
6
7
8
9
10
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/application.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>logs/application-%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${LOG_PATTERN}</pattern>
</encoder>
</appender>
  • <file> - 当前活动日志文件位置
  • <rollingPolicy> - 按时间滚动策略
    • <fileNamePattern> - 归档日志文件名格式,按天滚动
    • <maxHistory> - 保留30天的历史日志
  1. 根日志级别配置:设置全局日志级别为 INFO,日志同时输出到控制台和文件。
1
2
3
4
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE"/>
</root>
  1. 特定包的日志级别:专门为 com.solisamicus 包设置 DEBUG 级别。
1
<logger name="com.solisamicus" level="DEBUG"/>

3.3 示例代码

com/solisamicus/Main.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.solisamicus;

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

public class Main {
private static final Logger logger = LoggerFactory.getLogger(Main.class);

public void performOperation(String data) {
logger.trace("Trace level: Processing data: {}", data);
logger.debug("Debug level: Processing data: {}", data);
logger.info("Info level: Processing data: {}", data);
logger.warn("Warn level: Be careful with data: {}", data);
try {
if (data == null) {
throw new IllegalArgumentException("Data cannot be null");
}
logger.info("Successfully processed data: {}", data);
} catch (Exception e) {
logger.error("Failed to process data: {}", data, e);
}
}

public static void main(String[] args) {
Main example = new Main();
example.performOperation("test data");
example.performOperation(null);
}
}

运行结果显示:

1
2
3
4
5
6
7
8
9
10
11
2025-01-20 21:29:35.623 [main] DEBUG com.solisamicus.Main - Debug level: Processing data: test data
2025-01-20 21:29:35.625 [main] INFO com.solisamicus.Main - Info level: Processing data: test data
2025-01-20 21:29:35.625 [main] WARN com.solisamicus.Main - Warn level: Be careful with data: test data
2025-01-20 21:29:35.625 [main] INFO com.solisamicus.Main - Successfully processed data: test data
2025-01-20 21:29:35.626 [main] DEBUG com.solisamicus.Main - Debug level: Processing data: null
2025-01-20 21:29:35.626 [main] INFO com.solisamicus.Main - Info level: Processing data: null
2025-01-20 21:29:35.626 [main] WARN com.solisamicus.Main - Warn level: Be careful with data: null
2025-01-20 21:29:35.628 [main] ERROR com.solisamicus.Main - Failed to process data: null
java.lang.IllegalArgumentException: Data cannot be null
at com.solisamicus.Main.performOperation(Main.java:16)
at com.solisamicus.Main.main(Main.java:27)

logs/application.log 同样记载了上述内容。