Skip to content

Logback 核心概念解析

问题解答

一、Appender 是什么?

Appender 是 Logback 的输出目的地组件,决定日志输出到哪里

类比:就像写信时的"邮寄方式":

  • 控制台 Appender = 当面交付
  • 文件 Appender = 挂号信
  • 邮件 Appender = 电子邮件

二、常用 Appender 详解

2.1 ConsoleAppender(控制台输出)

完整类名: ch.qos.logback.core.ConsoleAppender

功能说明

将日志输出到控制台的标准输出流(System.out)或标准错误流(System.err)。

配置示例

xml
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
    <!-- 输出目标:System.out(默认)或 System.err -->
    <target>System.out</target>
    
    <!-- 编码器:定义输出格式 -->
    <encoder>
        <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        <charset>UTF-8</charset>
    </encoder>
</appender>

适用场景

场景是否适用说明
开发环境✅ 推荐实时查看日志,方便调试
Docker 容器✅ 推荐日志收集工具从标准输出采集
Kubernetes✅ 推荐通过 kubectl logs 查看
传统服务器部署❌ 不推荐控制台日志无法持久化,重启后丢失
需要日志归档❌ 不推荐无法自动滚动和清理

工作原理

应用程序

   ├─ logger.info("用户登录")


ConsoleAppender

   ├─ Encoder 格式化


System.out(控制台)

优缺点

优点:

  • ⚡ 速度快,直接输出
  • 🔍 实时可见,便于调试
  • 🐳 适合容器化部署

缺点:

  • ❌ 无法持久化
  • ❌ 控制台关闭后日志丢失
  • ❌ 无法搜索历史日志

2.2 RollingFileAppender(滚动文件输出)⭐

完整类名: ch.qos.logback.core.rolling.RollingFileAppender

功能说明

将日志写入文件,并支持自动滚动(按时间、大小等条件切换文件)和自动清理(删除过期日志)。

为什么需要滚动?

如果只用普通的 FileAppender:

application.log  ← 一直写入这个文件

                   文件越来越大(几十GB)

                   无法打开、搜索困难

使用 RollingFileAppender:

application.log               ← 当前活动文件(正在写入)
application.2025-12-08.log    ← 昨天的日志(自动归档)
application.2025-12-07.log    ← 前天的日志
application.2025-11-08.log    ← 30天前的日志 → 自动删除

完整配置示例

xml
<appender name="ROLLING_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    
    <!-- 当前活动的日志文件路径 -->
    <file>logs/application.log</file>
    
    <!-- 滚动策略:按时间+大小滚动 -->
    <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
        
        <!-- 归档文件命名模式 -->
        <!-- %d{yyyy-MM-dd}:按天滚动 -->
        <!-- %i:同一天内的序号 -->
        <!-- .gz:自动压缩 -->
        <fileNamePattern>logs/application.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
        
        <!-- 单个文件最大大小 -->
        <maxFileSize>100MB</maxFileSize>
        
        <!-- 保留的归档文件数量(天数) -->
        <maxHistory>30</maxHistory>
        
        <!-- 所有归档文件的总大小上限 -->
        <totalSizeCap>10GB</totalSizeCap>
        
        <!-- 是否在启动时清理过期文件 -->
        <cleanHistoryOnStart>true</cleanHistoryOnStart>
        
    </rollingPolicy>
    
    <!-- 编码器:定义输出格式 -->
    <encoder>
        <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        <charset>UTF-8</charset>
    </encoder>
    
</appender>

滚动策略详解

A. 按时间滚动(TimeBasedRollingPolicy)
xml
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
    <!-- 按天滚动 -->
    <fileNamePattern>logs/app.%d{yyyy-MM-dd}.log</fileNamePattern>
    
    <!-- 保留30天 -->
    <maxHistory>30</maxHistory>
</rollingPolicy>

滚动时机: 每天 00:00:00

生成的文件:

logs/
├── application.log           # 今天(正在写入)
├── application.2025-12-08.log # 昨天
├── application.2025-12-07.log # 前天
└── application.2025-12-06.log # 3天前
B. 按大小滚动(SizeBasedRollingPolicy)
xml
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
    <fileNamePattern>logs/app.%i.log</fileNamePattern>
    <minIndex>1</minIndex>
    <maxIndex>10</maxIndex>
</rollingPolicy>

<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
    <maxFileSize>10MB</maxFileSize>
</triggeringPolicy>

滚动时机: 文件达到 10MB

生成的文件:

logs/
├── application.log    # 当前(正在写入)
├── application.1.log  # 上一个文件
├── application.2.log  # 更早的文件
└── application.10.log # 最早的文件(超出后删除)
C. 按时间+大小滚动(SizeAndTimeBasedRollingPolicy)⭐ 推荐
xml
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
    <fileNamePattern>logs/app.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
    <maxFileSize>100MB</maxFileSize>
    <maxHistory>30</maxHistory>
    <totalSizeCap>10GB</totalSizeCap>
</rollingPolicy>

滚动时机:

  • 每天 00:00:00 切换到新文件
  • 或当前文件达到 100MB 时切换

生成的文件:

logs/
├── application.log                # 当前(正在写入)
├── application.2025-12-08.0.log.gz # 今天的第1个文件(已压缩)
├── application.2025-12-08.1.log.gz # 今天的第2个文件
├── application.2025-12-07.0.log.gz # 昨天的文件
└── application.2025-11-08.0.log.gz # 30天前 → 自动删除

工作流程

1. 应用启动

2. 创建 logs/application.log

3. 持续写入日志

4. 达到滚动条件(时间或大小)

5. 重命名 application.log → application.2025-12-08.0.log

6. 压缩(如果配置了 .gz)

7. 创建新的 application.log

8. 检查并删除过期文件(超过 maxHistory)

适用场景

场景是否适用说明
生产环境✅ 强烈推荐必须持久化日志
需要日志归档✅ 推荐自动归档和清理
磁盘空间有限✅ 推荐自动删除过期日志
需要搜索历史日志✅ 推荐文件可永久保存
容器环境⚠️ 看情况如果容器有持久化卷,可以使用

优缺点

优点:

  • 💾 日志持久化,重启不丢失
  • 🔄 自动滚动,文件大小可控
  • 🗑️ 自动清理过期日志
  • 🗜️ 支持自动压缩(.gz)
  • 📂 易于归档和备份

缺点:

  • 🐢 相比控制台略慢(需要写磁盘)
  • 💽 占用磁盘空间
  • 🔒 并发写入需要考虑锁的开销

三、Filter 过滤器中的 onMatch 和 onMismatch

3.1 Filter 的作用

Filter(过滤器)用于精确控制哪些日志事件应该被记录,哪些应该被丢弃。

类比:就像安检门:

  • onMatch:符合条件时怎么办
  • onMismatch:不符合条件时怎么办

3.2 Filter 的三种决策

返回值含义后续动作
ACCEPT接受日志立即记录,不再询问后续 Filter
DENY拒绝日志立即丢弃,不再询问后续 Filter
NEUTRAL中立继续询问下一个 Filter

3.3 LevelFilter 示例

需求:只记录 ERROR 级别的日志

xml
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>logs/error.log</file>
    
    <!-- 级别过滤器 -->
    <filter class="ch.qos.logback.classic.filter.LevelFilter">
        <!-- 匹配的级别 -->
        <level>ERROR</level>
        
        <!-- 匹配时的决策:接受 -->
        <onMatch>ACCEPT</onMatch>
        
        <!-- 不匹配时的决策:拒绝 -->
        <onMismatch>DENY</onMismatch>
    </filter>
    
    <encoder>
        <pattern>%d %p %c - %m%n</pattern>
    </encoder>
</appender>

工作流程

日志事件:logger.error("数据库连接失败")


LevelFilter 检查

   ├─ 级别是 ERROR?
   │     ├─ 是 → onMatch=ACCEPT → 记录到文件 ✅
   │     └─ 否 → onMismatch=DENY  → 丢弃 ❌

不同配置的效果

配置 1:只记录 ERROR

xml
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
日志级别是否记录
ERROR✅ 记录
WARN❌ 丢弃
INFO❌ 丢弃
DEBUG❌ 丢弃

配置 2:排除 ERROR(记录其他所有级别)

xml
<level>ERROR</level>
<onMatch>DENY</onMatch>      <!-- 匹配时拒绝 -->
<onMismatch>NEUTRAL</onMismatch> <!-- 不匹配时中立 -->
日志级别是否记录
ERROR❌ 丢弃
WARN✅ 记录
INFO✅ 记录
DEBUG✅ 记录

3.4 ThresholdFilter 示例

需求:只记录 WARN 及以上级别

xml
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
    <!-- 阈值:WARN -->
    <level>WARN</level>
</filter>

等价于:

xml
<filter class="ch.qos.logback.classic.filter.LevelFilter">
    <level>WARN</level>
    <onMatch>NEUTRAL</onMatch>  <!-- 高于或等于 WARN → 中立(继续) -->
    <onMismatch>DENY</onMismatch> <!-- 低于 WARN → 拒绝 -->
</filter>
日志级别是否记录
ERROR✅ 记录(高于阈值)
WARN✅ 记录(等于阈值)
INFO❌ 丢弃(低于阈值)
DEBUG❌ 丢弃(低于阈值)

3.5 实战案例:分离不同级别的日志

需求:ERROR 日志单独文件,INFO 日志另一个文件

xml
<configuration>
    
    <!-- INFO 日志文件 -->
    <appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>logs/info.log</file>
        
        <!-- 只记录 INFO 级别 -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>INFO</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>logs/info.%d{yyyy-MM-dd}.log</fileNamePattern>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        
        <encoder>
            <pattern>%d %p %c - %m%n</pattern>
        </encoder>
    </appender>
    
    <!-- ERROR 日志文件 -->
    <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>logs/error.log</file>
        
        <!-- 只记录 ERROR 级别 -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>logs/error.%d{yyyy-MM-dd}.log</fileNamePattern>
            <maxHistory>90</maxHistory> <!-- ERROR 日志保留更久 -->
        </rollingPolicy>
        
        <encoder>
            <pattern>%d %p %c - %m%n%ex{full}</pattern> <!-- 包含完整堆栈 -->
        </encoder>
    </appender>
    
    <root level="INFO">
        <appender-ref ref="INFO_FILE"/>
        <appender-ref ref="ERROR_FILE"/>
    </root>
    
</configuration>

效果:

java
logger.info("用户登录成功");  // → 只写入 logs/info.log
logger.error("数据库连接失败"); // → 只写入 logs/error.log
logger.debug("调试信息");      // → 两个文件都不写入(低于 INFO 级别)

3.6 多个 Filter 的执行顺序

xml
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
    <file>logs/app.log</file>
    
    <!-- Filter 1 -->
    <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
        <level>INFO</level>
    </filter>
    
    <!-- Filter 2 -->
    <filter class="ch.qos.logback.core.filter.EvaluatorFilter">
        <evaluator>
            <expression>message.contains("敏感信息")</expression>
        </evaluator>
        <onMatch>DENY</onMatch>
        <onMismatch>NEUTRAL</onMismatch>
    </filter>
    
    <encoder>
        <pattern>%msg%n</pattern>
    </encoder>
</appender>

执行流程:

日志事件


Filter 1: ThresholdFilter

   ├─ 低于 INFO? → DENY(丢弃)❌


Filter 2: EvaluatorFilter

   ├─ 包含"敏感信息"? → DENY(丢弃)❌
   ├─ 不包含? → NEUTRAL(继续)


记录到文件 ✅

四、完整配置示例对比

4.1 开发环境配置(简单)

xml
<configuration>
    <!-- 控制台输出即可 -->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} %highlight(%-5level) %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>
    
    <root level="DEBUG">
        <appender-ref ref="CONSOLE"/>
    </root>
</configuration>

4.2 生产环境配置(完整)

xml
<configuration>
    
    <!-- 控制台输出 -->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>
    
    <!-- INFO 日志文件(滚动) -->
    <appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>logs/info.log</file>
        
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>INFO</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>logs/info.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
            <maxFileSize>100MB</maxFileSize>
            <maxHistory>30</maxHistory>
            <totalSizeCap>10GB</totalSizeCap>
        </rollingPolicy>
        
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>
    
    <!-- ERROR 日志文件(滚动) -->
    <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>logs/error.log</file>
        
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>ERROR</level>
        </filter>
        
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>logs/error.%d{yyyy-MM-dd}.log.gz</fileNamePattern>
            <maxHistory>90</maxHistory>
        </rollingPolicy>
        
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n%ex{full}</pattern>
        </encoder>
    </appender>
    
    <root level="INFO">
        <appender-ref ref="CONSOLE"/>
        <appender-ref ref="INFO_FILE"/>
        <appender-ref ref="ERROR_FILE"/>
    </root>
    
</configuration>

五、快速参考表

Appender 类型对比

Appender类名输出位置滚动压缩适用场景
ConsoleAppenderch.qos.logback.core.ConsoleAppender控制台开发、容器
FileAppenderch.qos.logback.core.FileAppender文件简单场景
RollingFileAppenderch.qos.logback.core.rolling.RollingFileAppender文件生产环境
AsyncAppenderch.qos.logback.classic.AsyncAppender包装器--高性能

Filter 决策对照表

决策含义后续行为
ACCEPT接受立即记录,跳过后续 Filter
DENY拒绝立即丢弃,跳过后续 Filter
NEUTRAL中立继续下一个 Filter

常用 Filter 类型

Filter 类型类名用途
LevelFilterch.qos.logback.classic.filter.LevelFilter精确匹配日志级别
ThresholdFilterch.qos.logback.classic.filter.ThresholdFilter阈值过滤(≥某级别)
EvaluatorFilterch.qos.logback.core.filter.EvaluatorFilter基于表达式的条件过滤

六、常见问题

Q1: ConsoleAppender 和 RollingFileAppender 能同时使用吗?

答:可以! 这是最常见的配置。

xml
<root level="INFO">
    <appender-ref ref="CONSOLE"/>         <!-- 控制台查看 -->
    <appender-ref ref="ROLLING_FILE"/>    <!-- 文件持久化 -->
</root>

日志会同时输出到控制台和文件。

Q2: onMatch 和 onMismatch 必须都配置吗?

答:不一定。 有默认值:

  • onMatch 默认:NEUTRAL
  • onMismatch 默认:NEUTRAL

但建议显式配置,增加可读性。

Q3: 如何只在生产环境使用文件,开发环境用控制台?

使用 Spring Profile 配置:

logback-spring.xml:

xml
<configuration>
    <!-- 开发环境 -->
    <springProfile name="dev">
        <root level="DEBUG">
            <appender-ref ref="CONSOLE"/>
        </root>
    </springProfile>
    
    <!-- 生产环境 -->
    <springProfile name="prod">
        <root level="INFO">
            <appender-ref ref="ROLLING_FILE"/>
        </root>
    </springProfile>
</configuration>

Q4: 日志文件不滚动怎么办?

检查清单:

  1. fileNamePattern 是否包含 %d%i
  2. ✅ 文件路径是否有写权限
  3. ✅ 是否达到滚动条件(时间或大小)
  4. ✅ 查看 Logback 内部状态:<configuration debug="true">

七、总结

核心要点

  1. ConsoleAppender:输出到控制台,适合开发和容器环境
  2. RollingFileAppender:输出到文件并自动滚动,适合生产环境
  3. onMatch:Filter 匹配时的决策(ACCEPT/DENY/NEUTRAL)
  4. onMismatch:Filter 不匹配时的决策(ACCEPT/DENY/NEUTRAL)

推荐配置

  • 🔧 开发环境:ConsoleAppender + DEBUG 级别
  • 🚀 生产环境:RollingFileAppender + INFO 级别 + ERROR 单独文件
  • 🐳 容器环境:ConsoleAppender(日志由容器平台收集)

相关文档:

最后更新: 2025-12-08

基于 VitePress 构建