SQL 日志不打印:先定段落,再进分支
SQL 日志要经过四道关才能到你眼前:配置活进 Environment、logger 级别放行、持久层框架把 SQL 交给日志系统、appender 和采集链路送到你的观察口。任何一道关卡住,现象都是「不打印」。先确认持久层是 JPA 还是 MyBatis、跑的是 Boot 2 还是 3,再按现象对号进分支。

按首个可操作的异常分流
SQL 日志不是开了就有:配置要先活进 Environment,级别要放行,框架要把 SQL 交给日志系统,最后 appender 和采集链路要把日志送到你眼前。先确认两件事:持久层是 JPA 还是 MyBatis、跑的是 Boot 2 还是 3。再按现象对号进分支。
当前环境
先记录配置写在哪个文件、哪一层缩进,以及打包产物里到底有没有这份配置。配置写错位置是最常见的哑火原因。
- 列出项目里所有配置文件:application.yml / application.properties / 各 profile 文件
- 确认构建产物(target/classes 或 jar 内 BOOT-INF/classes)里实际打包了哪几份
报错识别
控制台与日志文件里都没有 Hibernate: select ...
也没有 DEBUG org.hibernate.SQL 行# 错:show-sql 写进了 datasource 命名空间
spring:
datasource:
show-sql: true
# 错:yml 缩进错位,show-sql 挂到了 logging 下
logging:
level:
org.hibernate.SQL: DEBUG
show-sql: true/actuator/env 里查不到 spring.jpa.show-sql→ 配置根本没绑定,查键的位置、拼写和文件是否被打包env 里值是 false→ 配置绑上了但被别的配置源盖掉,走 A3 分支键值都对、就是没输出→ 不是配置问题,去 C1 看输出是不是走到了 stdout
spring.jpa.show_sql(下划线)靠 relaxed binding 也能绑上,别把它当反例;真正坑的是命名空间和缩进。
快速解法
按序执行三条判据命令,用输出定位是「没绑上」还是「被打包漏了」。需要暴露 actuator 的 env 端点:management.endpoints.web.exposure.include=env。
判据一:查生效值与来源。property 不存在 → 配置没绑上;value=false → 被覆盖(去 A3)
curl -s localhost:8080/actuator/env/spring.jpa.show-sql | jq判据二:查打包产物。grep 无输出 → 文件没被打包,查 resources 目录与多模块结构
unzip -p target/app.jar BOOT-INF/classes/application.yml | grep -n "show-sql"修复:把键放回 spring.jpa 命名空间
spring:
jpa:
show-sql: true验证
- 重启后 /actuator/env/spring.jpa.show-sql 返回 true,origin 指向预期配置文件
- 执行一次查询,控制台出现 Hibernate: select ... 输出
解法无效时
- 没有 actuator:用命令行参数 --spring.jpa.show-sql=true 启动一次,能打印就说明开关有效、问题在配置文件
- 仍无输出:确认用的是 JPA/Hibernate 而不是 MyBatis,MyBatis 不认这个键,去 A4
相关
- A2:配置写在不激活的 profile 文件里,同样查不到生效值
- A3:值被配置中心或环境变量覆盖
- C1:show-sql 生效了但输出在 stdout,日志文件里照样没有
当前环境
记录启动时实际激活的 profile,以及每个 profile 文件里分别写了什么。本地 dev 能打印、服务器不能打印,八成是这个分支。
- 确认启动命令、脚本、Dockerfile / K8s 清单里的 SPRING_PROFILES_ACTIVE
- 列出 application-{profile}.yml 与主 application.yml 的分工
报错识别
The following 1 profile is active: "prod"
# 或
No active profile set, falling back to 1 default profile: "default"激活的 profile 与配置文件后缀对不上→ application-dev.yml 整份没加载,里面的开关全是白写spring.profiles.active 出现在多个地方→ 高优先级的定义说了算,低优先级的被忽略
检索方式:在启动日志里搜 profile is active。
快速解法
先确认激活的是谁,再决定是切 profile 还是挪配置。
判据:查激活的 profile,与配置文件后缀比对
grep -i "profile is active" logs/app.log
# 容器环境
kubectl logs deploy/<app-name> | grep -i "profile is active"修法一:激活对应 profile 启动
java -jar app.jar --spring.profiles.active=dev
# 或环境变量
SPRING_PROFILES_ACTIVE=dev修法二:把 SQL 日志开关挪进主 application.yml,用 profile 只留差异项
logging:
level:
org.hibernate.SQL: DEBUG验证
- 启动日志里的 active profile 与目标配置文件后缀一致
- /actuator/env 复核开关值已生效
解法无效时
- profile 是 CI/CD 或部署清单注入的:去 Dockerfile、K8s env、systemd unit 里找 SPRING_PROFILES_ACTIVE
- 用了 Spring Cloud:bootstrap 上下文另有 profile 来源,查 spring.cloud.config 或 Nacos 的 profile 配置
相关
- A1:profile 对了但键写错位置,同样查不到生效值
- A3:profile 文件加载了,值却被更高优先级的配置源盖掉
当前环境
记录项目接了哪些配置源:本地 yml、配置中心(Nacos / Spring Cloud Config)、K8s ConfigMap、环境变量、启动参数。源越多,越要先问「谁说了算」。
- 确认部署环境里有没有环境变量:LOGGING_LEVEL_ROOT、SPRING_JPA_SHOWSQL 这类
- 确认是否接了配置中心且有动态刷新
报错识别
GET /actuator/env/spring.jpa.show-sql
{
"property": { "value": "false",
"origin": "systemEnvironment property \"SPRING_JPA_SHOWSQL\"" }
}origin 指向 systemEnvironment / commandLineArgs→ 环境变量或启动参数盖掉了 yml,去部署清单里找origin 指向配置中心(nacos / config server)→ 远端配置优先,改本地没用origin 就是本地 application.yml 且值为 true→ 不是覆盖问题,去 C1 看输出落点
快速解法
先用 env 端点定位来源,再动对应的源。
判据:查值的来源,origin 字段就是答案
curl -s localhost:8080/actuator/env/spring.jpa.show-sql | jq '.propertySources[] | {name, value: .property.value}'环境变量来源:在部署清单里查并改掉
kubectl get deploy <app-name> -o yaml | grep -iE "SHOWSQL|LOGGING_LEVEL"确需本环境打开:在高优先级源里显式设为 true(改配置中心,或加启动参数)
java -jar app.jar --spring.jpa.show-sql=true验证
- /actuator/env 返回的值与来源都符合预期
- 改配置中心时确认客户端已刷新(或重启后再查一次)
解法无效时
- 改完配置中心又被刷回去:确认 @RefreshScope 与配置中心的优先级,客户端本地配置顶不过远端
- 找不到来源:Spring Boot 配置优先级官方文档对照排查(命令行 > 环境变量 > 配置中心 > 本地文件)
相关
- 官方外部化配置优先级:https://docs.spring.io/spring-boot/reference/features/external-config.html
- A2:profile 没激活时,连被覆盖的资格都没有
当前环境
记录持久层用的是原生 MyBatis 还是 MyBatis-Plus,两套配置前缀互不兼容,是这类哑火的高发区。
- 确认依赖是 mybatis-spring-boot-starter 还是 mybatis-plus-boot-starter
- 确认有没有自定义 SqlSessionFactory Bean
报错识别
日志里没有任何 ==> Preparing: 行
# 项目里同时存在两种写法:
mybatis:
configuration:
log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl依赖是 mybatis-plus-boot-starter→ 只有 mybatis-plus.* 前缀生效,mybatis.* 一律不读前缀对了、log-impl 也是 Slf4jImpl,仍无输出→ 不是这个分支,去 B3 查 mapper 包的 logger 级别
检索方式:依赖树里搜 mybatis,看是哪个 starter。
快速解法
先定身份(哪个 starter),再对齐前缀。
判据:确认用的是哪个 starter
mvn dependency:tree | grep -Ei "mybatis"
# Gradle: ./gradlew dependencies --configuration runtimeClasspath | grep -i mybatisMyBatis-Plus:用 mybatis-plus 前缀
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl别忘了给 mapper 包开 DEBUG(Slf4jImpl 按 mapper namespace 输出)
logging:
level:
com.example.mapper: DEBUG验证
- 执行一次查询,日志出现 ==> Preparing: 与 <== Total: 输出
解法无效时
- 项目里自定义了 SqlSessionFactory Bean:yml 里的 mybatis.* / mybatis-plus.* 整体失效,要在 Bean 里 setConfiguration / setLogImpl
- 多数据源场景:每个 SqlSessionFactory 独立配置,只配了一个就只看到一个库的 SQL
相关
- C3:log-impl 的选型(Slf4jImpl 与 StdOutImpl 的差别)
- B3:logger 名对了但级别没放行
当前环境
记录 classpath 上有哪些日志实现:默认 spring-boot-starter 带 Logback,换 Log4j2 需要显式排除。绑定错了一个,你改的那份配置文件就永远没人读。
- 确认项目想用 Logback 还是 Log4j2
- 确认有没有 starters 排除不彻底留下的传递依赖
报错识别
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/.../logback-classic-1.2.13.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/.../log4j-slf4j-impl-2.20.0.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Actual binding is of type [ch.qos.logback.classic.util.ContextSelectorStaticBinder]SLF4J(W): Class path contains multiple SLF4J providers.
SLF4J(W): Found provider [ch.qos.logback.classic.spi.LogbackServiceProvider]
SLF4J(W): Found provider [org.apache.logging.slf4j.SLF4JServiceProvider]Actual binding is of type [...Logback...]→ slf4j 绑到了 Logback,你改的 log4j2.xml 完全不会被读没有多绑定警告,但配置文件改了没反应→ 不是绑定冲突,去 B2 查配置文件有没有被加载
启动日志最前面几行,搜 SLF4J 就能看到。
快速解法
目标只有一个:classpath 上只剩一个 slf4j 实现。
判据:列出所有日志相关依赖,找出多余的实现是谁带进来的
mvn dependency:tree | grep -Ei "logback|log4j|slf4j|jul-to-slf4j"用 Log4j2:先排除默认 Logback,再引 log4j2 starter
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>保留 Logback:把传递进来的 log4j-slf4j2-impl / log4j-to-slf4j 排掉
mvn dependency:tree -Dincludes=org.apache.logging.log4j验证
- 启动日志不再出现 multiple bindings / multiple providers 警告
- 修改目标日志框架的配置文件,输出行为随之变化
解法无效时
- 排除后仍有多个实现:是某个业务依赖传递带进来的,用 dependency:tree -Dincludes 定位到具体 GAV 再排
- WebFlux 项目注意 spring-boot-starter-webflux 同样内置 logging starter,每个 starter 都要排
相关
- Spring Boot 官方 Logging 文档:https://docs.spring.io/spring-boot/reference/features/logging.html
- B2:绑定对了但配置文件没被加载
当前环境
记录配置文件的确切文件名、所在模块、是否打进了最终产物。多模块项目里配置文件躺在没被打包的模块里,是最典型的翻车方式。
- 核对文件名逐字符:logback-spring.xml 不是 logback-sprint.xml
- 确认是否用 logging.config 显式指定了别的路径
报错识别
自定义的日志格式、appender、级别全部没出现
启动输出仍是 Spring Boot 默认格式unzip -l target/app.jar | grep -Ei "logback|log4j2"
# 无输出 → 文件根本没打进 jarjar 里 BOOT-INF/classes 下找不到配置文件→ 没打包进去,查模块的 resources 目录与构建配置文件在 jar 里但行为没变化→ 文件名不符合约定,或 logging.config 指向了别处用了 Log4j2 却只提供 logback-spring.xml→ 绑定的框架跟配置文件对不上,回 B1
快速解法
两条判据:文件在不在产物里、名字对不对。
判据一:查打包产物里有没有配置文件
unzip -l target/app.jar | grep -Ei "logback|log4j2"判据二:核对命名约定,Boot 识别带 -spring 的版本优先
# Logback 识别顺序:logback-spring.xml 优先于 logback.xml
# Log4j2 识别顺序:log4j2-spring.xml 优先于 log4j2.xml
ls src/main/resources/ | grep -Ei "logback|log4j2"临时验证:显式指定配置路径,能生效就坐实是命名或位置问题
java -jar app.jar --logging.config=classpath:logback-spring.xml验证
- 启动后日志输出变成自定义 pattern / 级别 / 文件落点
- 产物里能 unzip 出配置文件
解法无效时
- Logback:加 JVM 参数 -Dlogback.statusListenerClass=ch.qos.logback.core.status.OnConsoleStatusListener,看配置加载全过程
- Log4j2:加 JVM 参数 -Dlog4j2.debug,打印内部状态日志,看实际读了哪份配置
相关
- B1:绑定错了,文件名再对也没人读
- log4j2-spring.xml 里才能用 Spring Boot 的 springProperty / springProfile 扩展
当前环境
记录 root 级别、相关类目的显式级别,以及 xml 里有没有写死级别或 additivity。logger 级别是第一道闸,闸不开后面全白搭。
- 确认 logging.level.root 的值
- 确认 xml 里有没有 <logger name="org.hibernate" level="INFO" additivity="false"> 这类硬编码
报错识别
GET /actuator/loggers/org.hibernate.SQL
{ "configuredLevel": null, "effectiveLevel": "WARN" }<logger name="org.hibernate" level="INFO" additivity="false">
<appender-ref ref="CONSOLE"/>
</logger>configuredLevel 为 null、effectiveLevel 是 WARN/INFO→ 类目没显式配,继承了 root 的低级别,显式开类目即可configuredLevel=DEBUG 但输出仍没有→ logger 级别已放行,问题在下一道闸,去 D2 查 appender 过滤xml 里 additivity="false"→ 日志被截在自己的 appender,root 挂的文件 appender 收不到
快速解法
先查生效级别,再决定临时改还是永久改。查和改都用 actuator 的 loggers 端点(需暴露:management.endpoints.web.exposure.include=loggers)。
判据:查生效级别,effectiveLevel 高于 DEBUG 就是没放行
curl -s localhost:8080/actuator/loggers/org.hibernate.SQL | jq临时开(不重启,诊断完记得关)
curl -X POST localhost:8080/actuator/loggers/org.hibernate.SQL \
-H 'Content-Type: application/json' \
-d '{"configuredLevel": "DEBUG"}'永久开:yml 显式声明,并删掉 xml 里写死的级别与 additivity="false"
logging:
level:
org.hibernate.SQL: DEBUG验证
- /actuator/loggers 复核 effectiveLevel 已变成 DEBUG
- 执行一次查询,目标输出位置出现对应日志行
解法无效时
- 没有 actuator:Logback 加 -Dlogback.statusListenerClass 看加载时的级别;或用 Arthas 的 logger 命令在线改
- 改了 yml 没生效:确认 yml 本身被加载(回 A1/A2 的判据),且没有 xml 硬编码级别压过它
相关
- D2:logger 级别放行后,appender 的过滤器是第二道闸
- A3:logging.level.* 同样会被环境变量(如 LOGGING_LEVEL_ROOT=WARN)覆盖
当前环境
记录你平时在哪看日志:IDE 控制台、SSH 终端、日志文件还是日志平台。show-sql 只写 System.out,哪个观察口收集 stdout,哪个就能看到,日志文件永远看不到。
- 确认 hibernate.show_sql 的来源(spring.jpa.show-sql 或 spring.jpa.properties.hibernate.show_sql)
- 确认应用日志文件的采集方式
报错识别
Hibernate: select u1_0.id,u1_0.email from user u1_0 where u1_0.id=?2026-09-27 10:15:32.118 DEBUG 23456 --- [nio-8080-exec-1] org.hibernate.SQL : select u1_0.id,u1_0.email from user u1_0 where u1_0.id=?行首是裸 Hibernate:,没时间戳、级别、logger 名→ 这是 show_sql 直接写 System.out,不经 Logback,日志文件自然没有行带 DEBUG org.hibernate.SQL→ 走的是日志框架,文件里没有就去 D2 查 appender
Hibernate 官方对 show_sql 的定义就是 Write all SQL statements to console,是控制台,不是日志。
快速解法
根治方案:弃用 show-sql,改用 logger 输出,让 SQL 进日志体系。
关掉 show-sql
spring:
jpa:
show-sql: false改用 logger 输出 SQL(参数绑定日志见 C2)
spring:
jpa:
properties:
hibernate.format_sql: true
logging:
level:
org.hibernate.SQL: DEBUG验证
- 日志文件里出现带级别与 logger 名的 SQL 行
- stdout 里的裸 Hibernate: 行消失
解法无效时
- 历史包袱必须保留 show-sql:把 stdout 纳入采集:容器场景 kubectl logs 本来就有;物理机用 nohup 重定向或 systemd journal 兜底
- 改用 logger 后级别不生效:去 B3 查级别
相关
- C3:MyBatis 的 StdOutImpl 是同一个病
- D1:stdout 与日志文件是两个观察口,采集链路要对上
当前环境
记录 Hibernate 版本。Boot 2 带 Hibernate 5,Boot 3 带 Hibernate 6,参数绑定的 logger 名在 6 里换了,老配置原样带过来就是白配。
- 确认 hibernate-core 的版本号
- 确认现有配置里写的是哪个 logger 名
报错识别
logging:
level:
org.hibernate.SQL: DEBUG
org.hibernate.type.descriptor.sql.BasicBinder: TRACE # Hibernate 5 的类目,6 里已改名
# 输出只有:
# select ... where u1_0.id=?依赖里 hibernate-core 是 6.x→ BasicBinder 类目在 Hibernate 6 已废弃,配置打在空处hibernate-core 是 5.x 且 BasicBinder 已开→ 级别没放行或 appender 挡了,去 B3 / D2只想看 SQL 不想看参数→ 不是这个分支,org.hibernate.SQL=DEBUG 就够了
检索方式:依赖树里搜 hibernate-core 看版本。
快速解法
先定 Hibernate 版本,再选对应的 logger 名。
判据:确认 Hibernate 主版本,5 还是 6
mvn dependency:tree | grep hibernate-coreHibernate 6(Boot 3):参数绑定走新类目,结果集提取另有一个
logging:
level:
org.hibernate.SQL: DEBUG
org.hibernate.orm.jdbc.bind: TRACE
# 需要看结果集提取时再加:
# org.hibernate.orm.jdbc.extract: TRACEHibernate 5(Boot 2):保留老类目
logging:
level:
org.hibernate.SQL: DEBUG
org.hibernate.type.descriptor.sql.BasicBinder: TRACE验证
- 日志出现 binding parameter 字样且参数值可见,不再是满屏问号
解法无效时
- 类目对了仍无参数输出:查 effectiveLevel(B3 的判据),TRACE 比 DEBUG 更容易被压
- 升级后 SQL 本身也没了:去 B1,Boot 3 升级常常伴随日志依赖变动
相关
- Hibernate 官方文档的日志类目表:https://docs.hibernate.org/orm/6.2/introduction/html_single/Hibernate_Introduction.html
- C1:顺手把 show-sql 换成 org.hibernate.SQL
当前环境
记录 log-impl 的当前取值。没配时 MyBatis 按 slf4j → commons-logging → log4j2 → log4j → jdk 的顺序自动探测,有 slf4j 就走 slf4j,这时 SQL 打不打全看 mapper 包的级别。
- 确认 mybatis.configuration.log-impl(或 mybatis-plus 前缀)当前值
- 确认 mapper 接口所在包名
报错识别
DEBUG c.e.mapper.UserMapper.selectById - ==> Preparing: SELECT id,name FROM user WHERE id=?
DEBUG c.e.mapper.UserMapper.selectById - ==> Parameters: 1(Long)
DEBUG c.e.mapper.UserMapper.selectById - <== Total: 1==> Preparing: SELECT id,name FROM user WHERE id=?
==> Parameters: 1(Long)
# 裸输出在控制台,日志文件里没有log-impl 配成 StdOutImpl→ 直接写 stdout,与 C1 的 show-sql 同病,日志文件永远看不到log-impl 是 Slf4jImpl 但 mapper 包是 INFO→ slf4j 收到了但被级别挡住,给 mapper 包开 DEBUG什么 log-impl 都没配且没有 slf4j 绑定→ 探测落到了 jdk logging 等实现,回 B1 先把日志后端理顺
快速解法
推荐组合:Slf4jImpl 加 mapper 包 DEBUG,SQL 就进了统一日志体系。
判据:确认 log-impl 现状,注意 MyBatis-Plus 是另一个前缀
grep -rn "log-impl" src/main/resources/推荐:走 slf4j,按 mapper namespace 输出
mybatis:
configuration:
log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl
logging:
level:
com.example.mapper: DEBUG只想在控制台看:可以用 StdOutImpl,但先想清楚它不进文件、不进采集
mybatis:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl验证
- 执行一次查询,看到 Preparing / Parameters / Total 三段输出
- Slf4jImpl 方案下,日志文件里同样能搜到这三段
解法无效时
- mapper 包开了 DEBUG 还是没有:确认包名写对(是接口所在包,不是 xml 所在目录),再去 B3 查 effectiveLevel
- 只想给单个 mapper 开:logging.level.com.example.mapper.UserMapper=DEBUG,精确到类
相关
- A4:前缀写错时 log-impl 根本不生效
- MyBatis 官方日志文档:http://mybatis.org/mybatis-3/zh/logging.html
当前环境
记录持久层写法和连接池类型。JDBC 规范本身没有 SQL 日志这一说,JdbcTemplate 的 DEBUG 日志也不展开完整带参 SQL;想在 JDBC 层抓全量 SQL,得给 DataSource 包一层代理。
- 确认连接池:Druid、HikariCP 还是默认池
- 确认有没有自定义 DataSource Bean
报错识别
logging.level.org.springframework.jdbc=DEBUG 只见到模板动作日志
看不到完整的、带参数的 SQL 文本项目用 Druid 连接池→ 开 Druid 自带的 slf4j filter 即可,别再引入第二套代理HikariCP 或默认池→ 池子没有日志 filter,需要 p6spy / datasource-proxy 这类 JDBC 代理项目混用 JPA 和 JdbcTemplate→ JPA 的 SQL 走 Hibernate(C1/C2),JdbcTemplate 的走这里,两边都要配
快速解法
按连接池选型走对应路径。Druid 方案改配置即可,代理方案要动依赖。
Druid:启用 slf4j filter 并开对应 logger
spring:
datasource:
druid:
filters: stat,slf4j
filter:
slf4j:
enabled: true
statement-executable-sql-log-enable: true
logging:
level:
druid.sql.Statement: DEBUG非 Druid:引 p6spy,依赖加 spy.properties 里指定 slf4j 输出,p6spy 这个 logger 开 INFO 即可
<dependency>
<groupId>p6spy</groupId>
<artifactId>p6spy</artifactId>
<version>3.9.1</version>
</dependency>
# spy.properties
appender=com.p6spy.engine.spy.appender.Slf4JLogger非 Druid 备选:datasource-proxy 的 SLF4JQueryLoggingListener,对应 logger 开 DEBUG
logging:
level:
net.ttddyy.dsproxy.listener.logging.SLF4JQueryLoggingListener: DEBUG验证
- 执行一次查询,日志出现带真实参数值的 SQL(代理输出通常直接展开参数)
- 确认原有 JPA/MyBatis 的 SQL 输出没有被代理方案重复打印或打断
解法无效时
- 代理装了仍无输出:确认 DataSource 是不是自定义 Bean 直接 new 出来的,代理根本没包上
- Druid filter 开了没输出:确认 filters 里确实包含 slf4j,且 druid.sql.Statement 的 effectiveLevel 已放行(B3 判据)
相关
- C1/C3 是框架层方案,代理是 JDBC 层兜底,能抓到所有走 DataSource 的 SQL
- 慢 SQL 监控是另一条线:Druid 的 stat filter 或 p6spy 的 outage 检测
当前环境
记录应用的运行方式和日志采集链路:谁采集 stdout、谁采集文件。到这个阶段,配置和级别都已确认没问题,查的是「日志去了哪、你在哪看」对不对得上。
- 确认运行方式:IDE、nohup、systemd、Docker、K8s
- 确认日志平台采集的是容器 stdout 还是应用日志文件
报错识别
kubectl logs deploy/<app-name> | grep -i "select"
# 或物理机:
journalctl -u myapp -n 500 | grep -i hibernate
# 能搜到,但应用日志文件里搜不到同一条stdout 有、文件没有→ 输出落点问题,别再回头改 yml,要么把 SQL 挪进日志框架,要么把 stdout 纳入采集stdout 也没有→ 不是落点问题,回 01–03 阶段按顺序查
快速解法
两个方向:根治(SQL 进日志框架)或补短板(采集 stdout)。
判据:确认落点差异,stdout 与文件各查一遍
kubectl logs deploy/<app-name> | grep -i "select"
grep -i "select" logs/app.log根治:SQL 改走日志框架,让文件 appender 接住(JPA 见 C1,MyBatis 见 C3)
logging:
level:
org.hibernate.SQL: DEBUG补短板:把 stdout 纳入采集,容器 stdout 接 filebeat / fluent-bit;物理机 nohup 重定向到文件
nohup java -jar app.jar >> logs/stdout.log 2>&1 &验证
- 同一条 SQL 能在你日常使用的观察口(文件或日志平台)搜到
- 临时验证用 actuator 动态开级别(B3),确认整条链路通了再固化配置
解法无效时
- 日志平台有延迟:先确认不是采集延迟造成的误判,等一个采集周期再搜
- 多副本部署:确认你查的副本就是处理请求的那个,负载均衡会把请求分到别的实例
相关
- C1:show-sql 是 stdout 派的头号来源
- 容器十二要素主张日志走 stdout,约定本身没错,关键是采集链路要知道去哪捞
当前环境
记录 logback-spring.xml / log4j2 配置里每个 appender 的过滤器。logger 级别放行后,appender 的过滤器是第二道闸:控制台放行了,文件没放行,就会出现一半有一半没有。
- 确认控制台与文件 appender 各自的 filter 配置
- 确认有没有用 AsyncAppender 包文件 appender
报错识别
同一个 logger 的 DEBUG SQL 行:控制台有,文件 appender 没有<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
...
</appender>文件 appender 带 ThresholdFilter=INFO→ DEBUG/TRACE 在 appender 处被过滤,logger 开得再低也没用低峰期文件里有、高峰期丢→ AsyncAppender 队列满后按 discardingThreshold 丢弃 DEBUG,不是过滤是丢弃控制台和文件都没有→ 不是 appender 问题,回 B3 查 logger 级别
快速解法
先找到过滤器,再决定放行还是给 SQL 开专用 appender。
判据:定位 appender 上的过滤器配置
grep -n -B2 -A4 "filter" src/main/resources/logback-spring.xml修法一:文件 appender 放行 DEBUG
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>DEBUG</level>
</filter>修法二:给 SQL 日志开专用 appender,与业务日志分开滚
<logger name="org.hibernate.SQL" level="DEBUG" additivity="false">
<appender-ref ref="SQL_FILE"/>
</logger>验证
- 执行一次查询,文件 appender 里出现与控制台相同的 DEBUG SQL 行
- 高峰时段复核一次,确认不是异步丢弃
解法无效时
- Log4j2 同理:查 appender 上的 ThresholdFilter 与 RegexFilter
- 放行后文件仍没有:确认 logger 的 appender-ref 指向的是不是你盯着的那个文件
相关
- B3:logger 级别是第一道闸,先过那关再来这
- logback Filter 官方文档:https://logback.qos.ch/manual/filters.html
