整合 Logback 日志框架
概述
日志是排查问题的眼睛。接口报错了、数据不对了、性能慢了,第一反应就是去看日志。Spring Boot 默认集成了 Logback,你要做的不是"引入",而是"配好"。
需要引入依赖吗?
不需要。spring-boot-starter-web 里面已经自带了 spring-boot-starter-logging,而 spring-boot-starter-logging 底层就是 Logback。只要你用了 Spring Boot Web,Logback 就已经在 classpath 里了。
所以直接进入正题:写配置文件。
配置文件放哪?
Spring Boot 会自动在 classpath 下查找这几个文件:
| 文件名 | 优先级 |
|---|---|
logback-spring.xml |
✅ 推荐,可以用 Spring 的 <springProfile> 标签按环境切换 |
logback.xml |
能用,但不支持 <springProfile>,比 Spring 更早加载 |
命名加 -spring 后缀的原因是:logback.xml 先于 Spring 容器加载,那时候还不知道激活的是哪个 profile,所以 <springProfile> 标签不生效。logback-spring.xml 则交给 Spring 来控制加载时机,profile 功能就能用了。
所以我们统一放在 src/main/resources/logback-spring.xml。
从零开始搭
第一步:定义变量
先定义几个变量,后面反复用到,改一处全生效:
xml
<configuration>
<!-- 引用 Spring Boot 自带的默认变量(CONSOLE_LOG_PATTERN 等) -->
<include resource="org/springframework/boot/logging/logback/defaults.xml" />
<!-- 应用名称,区分不同服务的日志 -->
<property scope="context" name="appName" value="auth"/>
<!-- 日志文件路径和名称 -->
<property name="LOG_FILE" value="./logs/${appName}.%d{yyyy-MM-dd}"/>
<!-- 每行日志的输出格式 -->
<property name="LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n"/>
</configuration>
LOG_PATTERN 里每个占位符的含义:
| 占位符 | 含义 | 输出示例 |
|---|---|---|
%d{yyyy-MM-dd HH:mm:ss.SSS} |
时间 | 2026-07-06 14:30:00.123 |
[%thread] |
线程名 | [http-nio-8080-exec-1] |
%-5level |
日志级别,左对齐占 5 位 | INFO |
%logger{50} |
输出日志的类名,最多 50 个字符 | c.l.i.a.controller.TestController |
%msg%n |
日志正文 + 换行 | --- |
第二步:配置文件输出(RollingFileAppender)
日志必须落地到文件,不然服务一重启控制台的就没了。
xml
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- 日志文件命名:auth.2026-07-06-0.log、auth.2026-07-06-1.log ... -->
<fileNamePattern>${LOG_FILE}-%i.log</fileNamePattern>
<!-- 保留 30 天 -->
<maxHistory>30</maxHistory>
<!-- 单个文件超过 10MB 就滚动到下一个 -->
<maxFileSize>10MB</maxFileSize>
<!-- 总大小不限制 -->
<totalSizeCap>0</totalSizeCap>
<!-- 重启不清历史日志 -->
<cleanHistoryOnStart>false</cleanHistoryOnStart>
</rollingPolicy>
<encoder>
<pattern>${LOG_PATTERN}</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
SizeAndTimeBasedRollingPolicy 是一个复合滚动策略,同时按"时间"和"大小"来切分文件。普通策略只能二选一,这个可以兼顾:
- 每天生成新文件:
auth.2026-07-06-0.log - 单文件超过 10MB 也会切:
auth.2026-07-06-1.log、auth.2026-07-06-2.log...... - 超过 30 天的旧文件自动删除
| 属性 | 作用 |
|---|---|
fileNamePattern |
日志文件的命名模板 |
maxFileSize |
单个文件超过多大就滚动切分 |
maxHistory |
最多保留多少天 |
totalSizeCap |
总大小上限,0 表示不限制 |
cleanHistoryOnStart |
启动时是否清空历史,生产环境务必设为 false |
第三步:异步写入(AsyncAppender)
上一步的 FILE appender 是同步写 的:每次 log.info() 都直接写磁盘,I/O 阻塞当前线程。高并发下,日志写入会成为性能瓶颈。
所以我们需要异步:日志先扔到一个内存队列里,由另一个线程负责批量刷盘。
xml
<appender name="ASYNC_FILE" class="ch.qos.logback.classic.AsyncAppender">
<!-- 队列满时是否丢弃日志。0 表示永不丢弃 -->
<discardingThreshold>0</discardingThreshold>
<!-- 队列容量,默认 256 -->
<queueSize>256</queueSize>
<!-- 套在哪个真实 appender 外面 -->
<appender-ref ref="FILE"/>
</appender>
| 配置项 | 含义 |
|---|---|
queueSize |
队列大小。日志量大的服务可以调到 512 或 1024 |
discardingThreshold |
队列满到多少比例开始丢弃。默认 20,即队列满 80% 时丢弃 TRACE/DEBUG/INFO 级别的日志。设为 0 表示永不丢弃,宁可堵着也不丢 |
discardingThreshold = 0 为什么重要?
默认值是 20。意味着:队列满了 80%,新来的 INFO 日志直接扔掉。排查线上问题的时候,你翻半天日志发现关键那条"刚好"被丢了,抓瞎。设成 0 之后,如果队列满了会阻塞一小会儿等队列腾出空间,但不会丢日志。
如果你对性能有极致要求、允许偶尔丢日志,可以把
discardingThreshold调成默认值,并且打开neverBlock。
第四步:控制台输出(CONSOLE appender)
不会有人把所有的日志都写在文件里,控制台也要一份。毕竟 DevOps 看的是控制台。
不过 Spring Boot 已经自带了 console-appender.xml,不需要手写,直接引用就行:
xml
<include resource="org/springframework/boot/logging/logback/console-appender.xml" />
第五步:按环境组装
开发环境我希望控制台 + 文件 都有,生产环境只保留文件,别在线上打控制台日志,浪费 I/O。
用 <springProfile> 按环境分别配置:
xml
<!-- 开发环境:控制台 + 文件 -->
<springProfile name="dev">
<include resource="org/springframework/boot/logging/logback/console-appender.xml" />
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="ASYNC_FILE"/>
</root>
</springProfile>
<!-- 生产环境:只看文件,不打控制台 -->
<springProfile name="prod">
<root level="INFO">
<appender-ref ref="ASYNC_FILE"/>
</root>
</springProfile>
<root level="INFO"> 的含义:默认日志级别为 INFO,DEBUG 和 TRACE 的日志不打印(除非单独给某个包开了 DEBUG)。
完整配置
把上面的步骤拼起来:
xml
<configuration>
<include resource="org/springframework/boot/logging/logback/defaults.xml" />
<!-- 变量 -->
<property scope="context" name="appName" value="auth"/>
<property name="LOG_FILE" value="./logs/${appName}.%d{yyyy-MM-dd}"/>
<property name="LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n"/>
<!-- 文件输出(同步) -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_FILE}-%i.log</fileNamePattern>
<maxHistory>30</maxHistory>
<maxFileSize>10MB</maxFileSize>
<totalSizeCap>0</totalSizeCap>
<cleanHistoryOnStart>false</cleanHistoryOnStart>
</rollingPolicy>
<encoder>
<pattern>${LOG_PATTERN}</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!-- 异步包装 -->
<appender name="ASYNC_FILE" class="ch.qos.logback.classic.AsyncAppender">
<discardingThreshold>0</discardingThreshold>
<queueSize>256</queueSize>
<appender-ref ref="FILE"/>
</appender>
<!-- 开发环境 -->
<springProfile name="dev">
<include resource="org/springframework/boot/logging/logback/console-appender.xml" />
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="ASYNC_FILE"/>
</root>
</springProfile>
<!-- 生产环境 -->
<springProfile name="prod">
<root level="INFO">
<appender-ref ref="ASYNC_FILE"/>
</root>
</springProfile>
</configuration>
常见踩坑:No appenders present
如果你启动后看到这个警告:
WARN - No appenders present in context [default] for logger [xxx]
意思是 Logback 找不到任何 <root> 配置------没有一个 appender 生效,日志不知道该往哪儿输出。
回头检查你的 <springProfile>:你的 application.properties 里是否配置了 spring.profiles.active?如果没有激活任何 profile(比如没设 dev),那么所有的 <springProfile> 都不会生效,<root> 也就没了。
修法 :在 application.properties 里加上:
properties
spring.profiles.active=dev
或者写一个 default 配置 ,在所有 <springProfile> 之外补一段 root 作为兜底:
xml
<!-- 兜底:当没有任何 profile 激活时,默认输出到控制台 -->
<root level="INFO">
<appender-ref ref="CONSOLE"/>
</root>