整合Logback日志框架
整合 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。
从零开始搭
第一步:定义变量
先定义几个变量,后面反复用到,改一处全生效:
<configuration><!-- 引用 Spring Boot 自带的默认变量(CONSOLE_LOG_PATTERN 等) --><includeresource="org/springframework/boot/logging/logback/defaults.xml"/><!-- 应用名称,区分不同服务的日志 --><propertyscope="context"name="appName"value="auth"/><!-- 日志文件路径和名称 --><propertyname="LOG_FILE"value="./logs/${appName}.%d{yyyy-MM-dd}"/><!-- 每行日志的输出格式 --><propertyname="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)
日志必须落地到文件,不然服务一重启控制台的就没了。
<appendername="FILE"class="ch.qos.logback.core.rolling.RollingFileAppender"><rollingPolicyclass="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)
上一步的FILEappender 是同步写的:每次log.info()都直接写磁盘,I/O 阻塞当前线程。高并发下,日志写入会成为性能瓶颈。
所以我们需要异步:日志先扔到一个内存队列里,由另一个线程负责批量刷盘。
<appendername="ASYNC_FILE"class="ch.qos.logback.classic.AsyncAppender"><!-- 队列满时是否丢弃日志。0 表示永不丢弃 --><discardingThreshold>0</discardingThreshold><!-- 队列容量,默认 256 --><queueSize>256</queueSize><!-- 套在哪个真实 appender 外面 --><appender-refref="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,不需要手写,直接引用就行:
<includeresource="org/springframework/boot/logging/logback/console-appender.xml"/>第五步:按环境组装
开发环境我希望控制台 + 文件都有,生产环境只保留文件,别在线上打控制台日志,浪费 I/O。
用<springProfile>按环境分别配置:
<!-- 开发环境:控制台 + 文件 --><springProfilename="dev"><includeresource="org/springframework/boot/logging/logback/console-appender.xml"/><rootlevel="INFO"><appender-refref="CONSOLE"/><appender-refref="ASYNC_FILE"/></root></springProfile><!-- 生产环境:只看文件,不打控制台 --><springProfilename="prod"><rootlevel="INFO"><appender-refref="ASYNC_FILE"/></root></springProfile><root level="INFO">的含义:默认日志级别为INFO,DEBUG和TRACE的日志不打印(除非单独给某个包开了 DEBUG)。
完整配置
把上面的步骤拼起来:
<configuration><includeresource="org/springframework/boot/logging/logback/defaults.xml"/><!-- 变量 --><propertyscope="context"name="appName"value="auth"/><propertyname="LOG_FILE"value="./logs/${appName}.%d{yyyy-MM-dd}"/><propertyname="LOG_PATTERN"value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n"/><!-- 文件输出(同步) --><appendername="FILE"class="ch.qos.logback.core.rolling.RollingFileAppender"><rollingPolicyclass="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><!-- 异步包装 --><appendername="ASYNC_FILE"class="ch.qos.logback.classic.AsyncAppender"><discardingThreshold>0</discardingThreshold><queueSize>256</queueSize><appender-refref="FILE"/></appender><!-- 开发环境 --><springProfilename="dev"><includeresource="org/springframework/boot/logging/logback/console-appender.xml"/><rootlevel="INFO"><appender-refref="CONSOLE"/><appender-refref="ASYNC_FILE"/></root></springProfile><!-- 生产环境 --><springProfilename="prod"><rootlevel="INFO"><appender-refref="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里加上:
spring.profiles.active=dev或者写一个default 配置,在所有<springProfile>之外补一段root作为兜底:
<!-- 兜底:当没有任何 profile 激活时,默认输出到控制台 --><rootlevel="INFO"><appender-refref="CONSOLE"/></root>