3、日志框架配置基础:Logback的 logback.xml 配置详解

好了,我们从最核心的配置说起。你做日志开发,第一件事通常是跟 logback.xml 打交道。这文件不长,但门道不少。我个人习惯先把它拆成三个东西:Appender、Layout、Filter。搞清楚这三者的关系,你就能像搭积木一样搭出自己的日志体系了。

一句话总结:Appender 决定日志去哪,Layout 决定日志长什么样,Filter 决定哪些日志能走。三者协作,缺一不可。

3.1 先看懂 logback.xml 的骨架

我们先看一个最小可用的配置。它虽然短,但五脏俱全。你拿到手,第一步不是往上堆东西,而是把它跑通。

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <root level="INFO">
        <appender-ref ref="STDOUT"/>
    </root>
</configuration>

这段代码定义了唯一一个 Appender,名字叫 STDOUT,输出到控制台。它配了一个 encoder,里面写了一段 pattern。这段 pattern 就是 Layout 的一种形式。你看,是不是很简单?

我的一个小习惯:刚开始配置,先用控制台输出吧。把东西打印到文件再去翻,会很麻烦。先把格式看对,再把内容看对,最后再决定落盘到哪。

3.2 Appender:日志的交通枢纽

Appender 是 Logback 的“嘴”。它负责把日志发出去。你想想看,日志种类很多——有的写文件,有的发邮件,有的走网络。Appender 就是干这个的。

常见的 Appender 有这么几类:

Appender 类型 用途 典型场景
ConsoleAppender 输出到控制台 开发调试、容器 stdout
FileAppender 输出到单个文件 简单日志记录
RollingFileAppender 按条件滚动输出到多个文件 生产环境,防止日志撑爆磁盘
SocketAppender 输出到远程 Socket 日志集中收集
DBAppender 输出到数据库 审计日志,需要持久化查询

为什么我特别强调 RollingFileAppender?因为它解决了「日志文件无限增长」的问题。我在项目中遇到过一哥们,日志不滚动,也没人清理,最后磁盘满了,服务全挂。嗯,这个坑,不值得再踩一次。

看一个滚动配置例子:

<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>/var/log/myapp/app.log</file>
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
        <fileNamePattern>/var/log/myapp/app.%d{yyyy-MM-dd}.log</fileNamePattern>
        <maxHistory>30</maxHistory>
    </rollingPolicy>
    <encoder>
        <pattern>%d %-5level %logger{36} - %msg%n</pattern>
    </encoder>
</appender>

这里用 TimeBasedRollingPolicy 按天滚动。maxHistory 设为 30,意思是保留最近 30 天的日志。这参数很重要,不要随便设成 365,除非你磁盘真的很大。

注意:滚动策略还有一个 SizeBasedTriggeringPolicy,它可以结合时间使用。比如每天一个文件,但超过 100MB 就自动滚动。这个我建议你加进去,尤其是日志量大的服务。

3.3 Layout:日志的“面子工程”

说白了,Layout 就是日志输出的模板。你希望日志长成什么样子,全在 pattern 里定义。

常用的转换符有这些:

转换符 含义 示例输出
%d 日期时间 2025-03-04 14:23:10.120
%thread 线程名 http-nio-8080-exec-3
%-5level 日志级别(左对齐5位) INFO / ERROR
%logger{len} Logger 名(限制长度) com.example.MyService
%msg 日志消息体 用户登录成功
%n 换行 (换行符)

我记得刚入行时,看到同事的日志带了一堆 %X{traceId} 和 %X{userId},觉得好高级。后来才知道,那是 MDC(Mapped Diagnostic Context) 的用法。你可以通过 %X{key} 把上下文信息打印出来。尤其在微服务里跟踪请求,太有用了。

避坑指南:我曾经习惯把 pattern 写得特别长,日期、线程、类名、行号什么的全部加上。结果日志文件大得吓人。后来我学乖了——生产环境只保留日期、级别和消息。行号只在调试时打开,因为它有性能成本。

3.4 Filter:日志的守门员

Appender 是能发出去,Layout 是能格式化。但谁来控制「哪些日志可以发」?

Filter 就是干这个的。它可以在日志到达 Appender 之前,决定放行、拒绝、或者交给下一个 Filter 决定。

Logback 内置了几种 Filter:

  • LevelFilter:匹配指定级别,精确控制。
  • ThresholdFilter:过滤掉低于指定级别的日志。
  • EvaluatorFilter:基于条件表达式,更灵活。

举个例子。我们有一个文件 Appender,只记录 ERROR 级别的日志:

<appender name="ERROR_FILE" class="ch.qos.logback.core.FileAppender">
    <file>/var/log/myapp/error.log</file>
    <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
        <level>ERROR</level>
    </filter>
    <encoder>
        <pattern>%d %-5level %msg%n</pattern>
    </encoder>
</appender>

这里用 ThresholdFilter 设了 ERROR 级别的门槛。INFO 和 WARN 的日志根本到不了这个 Appender。你想想看,这样是不是很爽?

Filter 还可以组合使用。我见过一个比较骚的操作:先用 LevelFilter 拒绝掉 WARN 级别的日志,再放行(NEUTRAL)其他级别,最后接一个 EvaluatorFilter 只允许包含特定关键字的日志。嗯,这个配置比较复杂,但灵活性一下子拉满。

再次提醒:Filter 是有执行顺序的。你写在 Appender 里的第一个 Filter 会最先执行。如果第一个 Filter 就 DENY 了,后面的 Filter 根本不会触发。理解这条,对排查「为什么日志少了」的问题很有帮助。

3.5 三者的关系:一张图看懂

为了让你更直观地理解 Appender、Layout、Filter 是怎么配合的,我画了一张图。你仔细看看,就知道日志从发出到落盘的完整路径了。

Logback 日志处理流程:Appender · Layout · Filter 协作 日志事件 Filter 链 Appender Layout 拒绝落地 思考点 Filter 决定日志去留 → Appender 决定输出到哪里 → Layout 决定显示格式。 你可以为同一个 Logger 配置多个 Appender,每个 Appender 可以有独立的 Filter 和 Layout。

你看,日志事件经过 Filter 链,被过滤、放行或者拒绝。如果被拒绝,直接丢弃;如果放行,交给 Appender,再到 Layout 格式化,最后输出。这个链路走下来,整个过程清清楚楚。

3.6 配置实战:一个完整的例子

我们把这些知识串起来,写一个真正能用的配置。

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <!-- 控制台输出:仅 INFO 及以上级别 -->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>INFO</level>
        </filter>
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <!-- 滚动文件输出:保留 7 天,每天一个文件 -->
    <appender name="ROLLING_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>/var/log/myapp/app.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>/var/log/myapp/app.%d{yyyy-MM-dd}.log</fileNamePattern>
            <maxHistory>7</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
        </encoder>
    </appender>

    <!-- 错误日志单独输出到一个文件 -->
    <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>/var/log/myapp/error.log</file>
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>ERROR</level>
        </filter>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>/var/log/myapp/error.%d{yyyy-MM-dd}.log</fileNamePattern>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>%d{ISO8601} [%thread] %-5level %logger - %msg%n</pattern>
        </encoder>
    </appender>

    <root level="DEBUG">
        <appender-ref ref="CONSOLE"/>
        <appender-ref ref="ROLLING_FILE"/>
        <appender-ref ref="ERROR_FILE"/>
    </root>
</configuration>

这里我定义了三个 Appender。Root Logger 用的是 DEBUG 级别。但控制台只显示 INFO 以上,普通文件记录所有 DEBUG 及以上的日志,而错误文件只记录 ERROR。用 Filter 实现了「不同目的地,不同级别」的效果。

避坑指南:我曾经在生产环境把 Root Logger 设成了 DEBUG 级别,结果一小时生成了几十 GB 的日志文件。幸好我配置了滚动策略和 maxHistory,不然磁盘直接爆炸。所以,上线前务必确认 Root 级别,开发环境可以 DEBUG,生产环境至少设为 INFO。

3.7 你应该记住的点

  • Appender 是日志的输出目的地,常见的包括 Console、File、RollingFile。
  • Layout 通过 pattern 控制日志格式,合理使用 MDC 能减少日志量。
  • Filter 控制日志的准入和拒绝,多个 Filter 可以串联,执行顺序很重要。
  • 三者可以自由组合:一个 Logger 可以有多个 Appender,每个 Appender 可以有独立的 Filter 和 Layout。
  • 滚动日志策略(时间和大小)是生产环境的必备项,不要省略。

好了,这一章的内容比较硬核。你只要把 Appender、Layout、Filter 这三板斧练熟,Logback 的配置基本上就没什么能难住你的了。