Spring Boot 的日志体系是怎样的?默认用什么、怎么配置分组和文件输出?
简化版
Spring Boot 默认的日志体系是「SLF4J(门面)+ Logback(实现)」——你的代码统一用 SLF4J 的 Logger 打日志(LoggerFactory.getLogger),底层默认由 Logback 实际输出。Spring Boot 帮你做了什么:① 提供了开箱即用的默认配置(控制台彩色输出、合理的格式、INFO 级别),不用写一行配置就有像样的日志;② 用 spring-boot-starter-logging(被大多数 starter 传递依赖)自动引入 Logback + SLF4J,并把其他日志框架(log4j、JUL、commons-logging)桥接到 SLF4J,统一日志出口。常用配置(在 application.yml 里,不用写 logback.xml):logging.level.<包名>=DEBUG 设某包日志级别;logging.file.name/logging.file.path 输出到文件;logging.group 定义日志分组(把一批包归为一组,一次性调级别,如 web、sql);logging.pattern.console 自定义格式。换成 Log4j2:排除 spring-boot-starter-logging,引入 spring-boot-starter-log4j2 即可(异步日志性能更好)。
详细版
日志体系分层:
你的代码:SLF4J API(门面)—— log.info("...")
↓ 绑定
默认实现:Logback(真正输出)
↑ 桥接
其他框架:log4j-to-slf4j、jul-to-slf4j、jcl-over-slf4j
(把用 log4j/JUL/commons-logging 的第三方库的日志,也导流到 SLF4J)
→ 全应用只有一个日志出口,统一格式、统一控制
常用配置速查(application.yml):
| 配置 | 作用 |
|---|---|
logging.level.root=INFO | 全局根级别 |
logging.level.com.example=DEBUG | 某个包的级别 |
logging.group.sql=org.hibernate.SQL,... | 定义日志分组 |
logging.level.sql=DEBUG | 给分组统一设级别 |
logging.file.name=app.log | 输出到文件 |
logging.logback.rollingpolicy.max-file-size=10MB | 滚动策略 |
logging.pattern.console=... | 控制台格式 |
logging:
level:
root: INFO
com.example.service: DEBUG # 某包调成 DEBUG
group:
web: org.springframework.web, org.springframework.http # 自定义分组
file:
name: logs/app.log # 输出到文件(同时保留控制台)
logback:
rollingpolicy:
max-file-size: 10MB # 单文件最大
max-history: 7 # 保留天数
⚠️ 理解「门面 + 实现 + 桥接」是理解 Java 日志生态混乱的钥匙。Java 历史上日志框架众多(log4j、JUL、commons-logging、logback、log4j2),如果各库用各的,日志会「五花八门、无法统一控制」。SLF4J 是「门面」(统一 API,代码只依赖它);Logback/Log4j2 是「实现」(真正干活,运行时选一个);桥接库把「用了其他日志框架的第三方库」的日志重定向到 SLF4J。Spring Boot 把这套整合好开箱即用,所以你「只用 SLF4J 打日志、只在 yml 里配级别」就够了。大坑:如果同时引入了 Logback 和 Log4j2 两个实现,或桥接冲突(如同时有
log4j-to-slf4j和slf4j-log4j12形成环),会报错或日志失效——换实现时务必先排除默认的spring-boot-starter-logging。
完整版教学
一、门面 + 实现:为什么日志要分两层
理解 Spring Boot 日志,先理解「门面模式」这个基础:
问题:Java 日志框架太多(log4j、JUL、logback、log4j2...)
如果代码直接依赖某个具体框架(如 import org.apache.log4j.Logger)
→ 想换框架就要改所有代码,且第三方库各用各的没法统一
解决:门面模式(Facade)
SLF4J = 统一的日志"门面"(API 接口)
代码只写 org.slf4j.Logger(不绑定具体实现)
运行时再"绑定"一个具体实现(Logback / Log4j2)
→ 换实现不改代码,只换依赖
类比:JDBC(门面)+ MySQL/Oracle 驱动(实现)
代码面向 JDBC 写,换数据库换驱动即可
日志分两层是「门面模式」——SLF4J 是统一门面(代码只依赖它的 API),Logback/Log4j2 是实现(运行时绑定一个)。好处是「换实现不改代码,只换依赖」,且能统一第三方库的日志。这和 JDBC(门面)+ 数据库驱动(实现)、Spring Cache(抽象)+ Redis(实现)是同一思路。理解「日志门面模式:SLF4J 门面(代码依赖)+Logback/Log4j2 实现(运行时绑定)、换实现不改代码、类比 JDBC」,就抓住了 Java 日志体系的核心。
二、Spring Boot 的默认:Logback + 开箱即用
Spring Boot 默认用 Logback,且开箱即用:
默认组合:SLF4J + Logback
spring-boot-starter(几乎所有 starter 都传递依赖它)
→ spring-boot-starter-logging
→ logback-classic(Logback 实现)
→ slf4j-api(SLF4J 门面)
→ 各种桥接库
开箱即用(不写任何配置就有):
- 控制台彩色输出(不同级别不同颜色)
- 合理的默认格式(时间 级别 PID 线程 logger 消息)
- 默认 INFO 级别
- 异常堆栈完整打印
所以:新建一个 Spring Boot 项目,直接 log.info("x") 就有漂亮日志
不用像传统 Java 项目那样先配 log4j.properties
Spring Boot 默认用 Logback,通过 spring-boot-starter-logging(被 spring-boot-starter 传递依赖)自动引入,且开箱即用(控制台彩色输出、合理格式、INFO 级别)——不用写任何配置就有像样的日志。这省去了传统 Java 项目「先配 log4j.properties」的麻烦。理解「Spring Boot 默认 SLF4J+Logback、starter-logging 自动引入、开箱即用彩色控制台/合理格式/INFO 级别」,就理解了默认体系。
三、桥接:统一所有第三方库的日志
Spring Boot 日志体系的一个关键作用是「桥接」——把用其他日志框架的库统一到 SLF4J:
问题:你的应用引入了很多第三方库,它们用的日志框架不一:
- 库 A 用 log4j
- 库 B 用 JUL(java.util.logging)
- 库 C 用 commons-logging(JCL)
如果不管,它们的日志各走各的、格式不一、没法统一控制级别
解决:桥接库(bridge)
log4j-to-slf4j:把 log4j 的调用"骗"到 SLF4J
jul-to-slf4j:把 JUL 的调用导到 SLF4J
jcl-over-slf4j:把 commons-logging 导到 SLF4J
→ 所有日志最终都汇入 SLF4J → 由 Logback 统一输出
效果:
全应用(含所有第三方库)的日志,格式统一、级别统一可控
在 yml 里 logging.level.某三方库包=WARN 就能压制它的日志
桥接是「把用其他日志框架的第三方库统一到 SLF4J」——log4j-to-slf4j、jul-to-slf4j、jcl-over-slf4j 等桥接库把 log4j/JUL/commons-logging 的日志调用「导流」到 SLF4J,最终由 Logback 统一输出。效果是全应用(含所有第三方库)日志格式统一、级别统一可控(能在 yml 里压制某个吵闹的三方库)。理解「桥接库把 log4j/JUL/commons-logging 的日志导到 SLF4J、全应用日志统一格式和级别可控」,就理解了 Spring Boot 日志整合的关键作用。
四、配置级别:从 root 到包到分组
日志最常用的配置是「控制级别」,Spring Boot 让你在 yml 里就能配:
① 全局根级别:
logging.level.root=INFO (默认就是 INFO)
② 某个包/类的级别(精细控制):
logging.level.com.example.service=DEBUG (只把这个包调 DEBUG)
logging.level.org.springframework.web=DEBUG
③ 日志分组(把一批包归为一组,统一调级别):
logging.group.sql=org.hibernate.SQL, org.mybatis ← 定义分组
logging.level.sql=DEBUG ← 给分组设级别
→ 一次调 sql 组,就同时调了组里所有包
Spring Boot 内置了两个分组:
web:Spring Web 相关的一批包
sql:SQL 相关的一批包
→ logging.level.web=DEBUG 直接开启一批 web 日志
级别顺序(低→高):TRACE < DEBUG < INFO < WARN < ERROR
设 INFO 则 INFO 及以上(WARN/ERROR)都输出,DEBUG/TRACE 不输出
级别配置从粗到细:root(全局)→ 包/类级别(精细)→ 分组(一批包统一调)。日志分组(logging.group) 是 Spring Boot 的便利特性——把一批相关包归为一组(如 sql、web),一次调整整组级别(内置了 web 和 sql 两个分组)。级别顺序 TRACE<DEBUG<INFO<WARN<ERROR,设某级别则该级别及以上输出。理解「级别配置 root→包→分组、logging.group 分组统一调级别(内置 web/sql)、级别顺序 TRACE<DEBUG<INFO<WARN<ERROR」,就掌握了日志级别控制。
五、输出到文件与滚动
生产环境要把日志输出到文件并滚动,Spring Boot 也用 yml 简化了:
输出到文件(默认只输出控制台):
logging.file.name=logs/app.log 指定文件名(含路径)
或 logging.file.path=logs 只指定目录(文件名默认 spring.log)
→ 配了之后,同时输出到控制台和文件
滚动策略(防止单个日志文件无限增长):
logging.logback.rollingpolicy.max-file-size=10MB 单文件最大 10MB
logging.logback.rollingpolicy.max-history=7 保留 7 天
logging.logback.rollingpolicy.total-size-cap=1GB 总大小上限
→ 到达大小/天数就切分新文件、删除过期的
什么时候需要 logback-spring.xml:
简单需求 yml 就够;复杂需求(多 appender、按环境不同配置、
自定义格式、发到 ELK/Kafka)→ 用 logback-spring.xml
(用 logback-spring.xml 而非 logback.xml,因为前者支持
Spring 的 <springProfile> 等扩展)
文件输出用 logging.file.name/logging.file.path(配了就同时输出控制台和文件),滚动策略用 logging.logback.rollingpolicy.*(单文件大小、保留天数、总大小上限,防止日志无限增长)。简单需求 yml 够用;复杂需求(多 appender、按环境配置、发到 ELK)用 logback-spring.xml(比 logback.xml 多支持 <springProfile> 等 Spring 扩展)。理解「logging.file.name 输出文件、rollingpolicy 滚动防无限增长、复杂需求用 logback-spring.xml(支持 springProfile)」,就掌握了文件输出配置。
六、换成 Log4j2 与结构化日志
进阶:换日志实现、以及现代的结构化日志:
换成 Log4j2(性能更好,尤其异步日志):
1. 排除默认的 spring-boot-starter-logging:
<exclusion> spring-boot-starter-logging </exclusion>
2. 引入 spring-boot-starter-log4j2
→ SLF4J 门面不变、代码不变,只换了实现
Log4j2 的优势:异步日志(AsyncLogger,基于 Disruptor)吞吐更高
★ 换实现必须先排除默认的,否则两个实现冲突(报错或日志失效)
结构化日志(现代趋势):
传统日志是"人读的文本",难以被日志系统(ELK)解析
结构化日志输出 JSON(每条日志是结构化字段)
→ 便于采集、检索、告警
Spring Boot 3.4+ 内置支持结构化日志(logging.structured.format)
配合 MDC(放 traceId 等上下文)做链路追踪
选择:追求极致日志吞吐 → Log4j2 异步;一般场景 → 默认 Logback 够用
换实现很简单——排除 spring-boot-starter-logging、引入 spring-boot-starter-log4j2(门面和代码不变,只换实现;Log4j2 的异步日志吞吐更高);换实现必须先排除默认的,否则两个实现冲突。现代趋势是结构化日志(输出 JSON 便于 ELK 采集检索,Spring Boot 3.4+ 内置支持),配合 MDC 做链路追踪。理解「换 Log4j2 要先排除 starter-logging(否则冲突)、Log4j2 异步吞吐高、结构化日志输出 JSON 便于采集(3.4+内置)配 MDC 链路」,就掌握了日志的进阶话题。
记忆钩子:「Spring Boot 日志 = SLF4J 门面 + Logback 实现(默认),门面模式换实现不改代码(类比 JDBC);starter-logging 开箱即用(彩色控制台/INFO/合理格式);桥接库(log4j-to-slf4j/jul-to-slf4j/jcl-over-slf4j)把第三方库日志统一到 SLF4J→格式级别统一可控;yml 配置:logging.level.包=DEBUG、logging.group 分组统一调级别(内置 web/sql)、logging.file.name 输出文件、rollingpolicy 滚动;换 Log4j2 要先排除 starter-logging(否则冲突,异步吞吐高);复杂用 logback-spring.xml」。
七、常见误区与追问
- 误区:Spring Boot 用 log4j 做日志。 默认用 Logback(不是 log4j 或 log4j2);SLF4J 是门面、Logback 是默认实现;想用 Log4j2 要排除 spring-boot-starter-logging 再引入 spring-boot-starter-log4j2。
- 误区:SLF4J 是一个日志实现。 SLF4J 只是「门面/API」(统一的日志接口),本身不输出日志;真正输出的是绑定的实现(Logback/Log4j2);代码依赖 SLF4J,运行时绑定实现。
- 误区:要用日志必须写 logback.xml。 Spring Boot 开箱即用,简单需求在 application.yml 里配 logging.level/file 就够;只有复杂需求(多 appender、按环境配置、发到 ELK)才需要 logback-spring.xml。
- 误区:换 Log4j2 直接加依赖就行。 必须先排除默认的 spring-boot-starter-logging(含 Logback),否则 Logback 和 Log4j2 两个实现共存会冲突(报错或日志失效);换实现的铁律是「先排除旧的」。
- 追问:日志的桥接库是干什么的? 把用其他日志框架(log4j、JUL、commons-logging)的第三方库的日志「导流」到 SLF4J——log4j-to-slf4j/jul-to-slf4j/jcl-over-slf4j;这样全应用(含所有依赖库)的日志统一由 Logback 输出、格式和级别可统一控制。
- 追问:logging.group 有什么用?内置了哪些? 把一批相关包归为一个组,一次性调整整组的日志级别(如把 sql 组调 DEBUG 就同时开启所有 SQL 相关包的日志);Spring Boot 内置了 web 和 sql 两个分组,也可自定义 logging.group.xxx。
- 追问:为什么用 logback-spring.xml 而不是 logback.xml? logback-spring.xml 由 Spring Boot 加载,支持 Spring 扩展标签如
<springProfile>(按 profile 差异化配置)、<springProperty>(读取 Spring 环境属性);logback.xml 是 Logback 原生加载的,早于 Spring 初始化,用不了这些扩展。
八、加强记忆
Spring Boot 默认日志体系是「SLF4J(门面)+ Logback(实现)」——门面模式:代码只依赖 SLF4J API(org.slf4j.Logger),运行时绑定实现,换实现不改代码(类比 JDBC + 驱动)。spring-boot-starter-logging(被 spring-boot-starter 传递依赖)自动引入 Logback + SLF4J + 桥接库,开箱即用(控制台彩色、合理格式、INFO 级别)。桥接库(log4j-to-slf4j/jul-to-slf4j/jcl-over-slf4j)把用其他日志框架的第三方库日志统一导流到 SLF4J,使全应用日志格式和级别统一可控。yml 配置:logging.level.root(全局)/logging.level.<包>(精细)/logging.group(分组统一调级别,内置 web/sql)/logging.file.name(输出文件)/logging.logback.rollingpolicy.*(滚动防无限增长);级别顺序 TRACE<DEBUG<INFO<WARN<ERROR。复杂需求用 logback-spring.xml(支持 <springProfile> 等 Spring 扩展)。换 Log4j2:先排除 spring-boot-starter-logging(否则两实现冲突)、引入 spring-boot-starter-log4j2(异步日志吞吐高)。现代趋势是结构化日志(JSON,3.4+ 内置)配 MDC 做链路追踪。一句话「Spring Boot 日志 = SLF4J 门面 + Logback 实现(默认,开箱即用),桥接库把三方库日志统一到 SLF4J;yml 配 level/group(内置 web·sql)/file/rollingpolicy;换 Log4j2 先排除 starter-logging;复杂用 logback-spring.xml」。