☰
Spring Boot SQL日志打印全攻略:MyBatis/JPA配置与Logback实战
2026/9/28 5:26:04 网站建设 项目流程

Spring Boot 项目里打印 SQL 日志这件事,说大不大,说小不小。我见过不少同事在 IDEA 控制台里翻来翻去找不到一条 SQL 输出,最后发现是 MyBatis 的 log-impl 没配,也有人把日志框架从 Logback 切到 Log4j2 之后,SQL 日志莫名消失,排查半天才意识到是 logger name 的包路径写错了。这篇文章就把 Spring Boot 项目里打印 SQL 日志和结果的完整方案讲清楚,覆盖 MyBatis、MyBatis-Plus、JPA/Hibernate 三种主流持久层框架,同时讲透基于配置文件(application.yml)和基于 Logback(logback-spring.xml)两种配置方式的差异与选择,最后附上我实际踩过的坑和排查思路。

适合谁看:正在做 Spring Boot 项目、发现 SQL 日志打不出来或者不知道日志该怎么独立归档的开发者,尤其是刚接触 MyBatis 和 JPA 不久、对日志体系还没完全理清的同学。读完你不仅能照着配,还能明白为什么这样配。

1. 先搞清楚 Spring Boot 的日志体系,再谈 SQL 日志

这一步很多人会跳过,直接去搜"Spring Boot 打印 SQL 日志"然后抄一段配置,结果换了个框架就失效了。根本原因在于,不同持久层框架对日志的处理机制完全不一样。

1.1 SLF4J 门面与 Spring Boot 默认的 Logback

Spring Boot 的默认日志实现是 Logback,但项目代码里通常直接用的是 SLF4J 的 API。你可以把 SLF4J 理解成一个插座规范,Logback、Log4j2、java.util.logging 都是不同的插头,只要插头符合规范,插座就能用。Spring Boot 的 spring-boot-starter-logging 已经帮你把 Logback 和 SLF4J 绑定好了,所以你在 pom.xml 里看不到 Logback 依赖,代码里照样能打日志,因为 starter 帮你在后台装配了。

理解这个机制对配置 SQL 日志非常重要。因为你配置 SQL 日志时,本质上有两条路:

  • 走应用程序的日志框架(Logback/Log4j2),用 logging.level 来控制 SQL 日志的级别。
  • 不走日志框架,直接通过持久层框架自身的 stdout 输出到控制台。

这两条路的配置入口完全不同,而且很多教程把它们混在一起讲,导致新手抄配置时很容易张冠李戴。

举个例子,MyBatis 的 StdOutImpl 是直接 System.out 输出,根本不经过 Logback,所以你在 logging.level 里怎么调级别都没用。而 Slf4jImpl 是把日志交给了 SLF4J,这时才受 logging.level 管控。这就是为什么同样的"打印 SQL 日志"需求,配置方式却千差万别。

1.2 日志级别是 SQL 日志输出的总开关

日志级别从低到高是 trace、debug、info、warn、error。如果不理解级别,你可能会遇到一个非常常见的困惑:明明代码里什么都正常,为什么 SQL 日志就是不出来?

原因很简单。Spring Boot 默认的 root 日志级别是 info,而很多持久层框架的 SQL 日志默认是 debug 级别(比如 Hibernate 的 org.hibernate.SQL 就是 debug)。debug 低于 info,不会输出。所以你必须在 application.yml 里把对应 logger 的级别降到 debug 或更低,SQL 日志才会浮现。

这是 SQL 日志问题的核心开关。你后面所有配置,本质上都是在做一件事:把特定 logger 的级别调到能输出 SQL 的程度,并指定输出的目的地。

另外注意一点,项目里如果有比较多的 debug 日志,直接调 root 的级别到 debug 会造成日志暴涨,所以实践上永远只针对 mapper 包或者框架的 SQL logger 单独调级别,不要全局放开。

1.3 持久层框架不同,日志行为天差地别

MyBatis、MyBatis-Plus、JPA/Hibernate 三者的日志机制差异非常大,我用一张表总结一下,后面每个框架再细讲:

持久层框架SQL 日志走日志框架?关键 logger/配置项默认级别结果集日志
MyBatis可选,通过 log-impl 指定mapper 接口包路径debug支持,输出行数
MyBatis-Plus同 MyBatismapper 接口包路径debug支持
JPA/Hibernate走日志框架org.hibernate.SQLdebug通过 trace 级输出参数
JPA/Hibernate不走日志框架spring.jpa.show-sqlinfo(stdout)不支持

这张表记住一个核心结论:MyBatis 系要看 log-impl 和包路径;JPA/Hibernate 系要看 spring.jpa.show-sql 和 org.hibernate.SQL 的级别。理解了这一点,后面就是照着配置的事了。

2. MyBatis 系 SQL 日志配置实操

MyBatis 和 MyBatis-Plus 是 Java 后端用得最多的持久层框架,SQL 日志配置也几乎是同一个套路。我这里直接给出最实用的配置组合。

2.1 方式一:走 Logback,用 logging.level 控制 mapper 包

这种方法是我最推荐的,因为日志会统一进入 Logback,后续不管是想输出到文件、按天归档、还是接日志采集系统,全都顺理成章。

首先确保 application.yml 中有这样一段配置:

logging: level: com.example.project.mapper: debug

这里 com.example.project.mapper 是你自己的 Mapper 接口所在的包路径,不是 XML 文件所在路径。很多刚接触的同学把 resource 下的 mapper XML 目录路径填进来,结果怎么配都不生效,就是这个原因。

同时,MyBatis 需要指定日志实现为 Slf4jImpl,这样它才会走日志框架而不是直接 stdout:

mybatis: configuration: log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl

如果你是 MyBatis-Plus,配置几乎一样,只是前缀改成 mybatis-plus:

mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl

然后重启项目,执行一条 SQL,你就会在控制台看到类似这样的输出:

==> Preparing: SELECT id, name, age FROM user WHERE id = ? ==> Parameters: 1(Long) <== Columns: id, name, age <== Row: 1, 张三, 25 <== Total: 1

能看到 Preparing(预编译 SQL)和 Parameters(绑定参数)就说明配置成功了。注意,这时即使你的 root 日志级别是 warn,mapper 包下的 SQL 日志也照样输出,因为 logging.level 下的包路径优先级高于 root。

2.2 方式二:用 StdOutImpl 快速验证

如果不关心日志归档,只是想在本地调试时快速看到 SQL,可以直接用 MyBatis 的 StdOutImpl:

mybatis: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

这个实现会把 SQL 日志直接打印到标准输出,IDEA 控制台里显示效果完全一样。好处是零学习成本、配置一行搞定;坏处是日志不经过 Logback,你无法用 logging.level 控制、无法归档、也无法接入日志采集系统。

我在开发环境用过一段时间的 StdOutImpl,后来切到生产环境做日志采集时被迫全部改回 Slf4jImpl。坦白说,如果从一开始就用 Slf4jImpl,后面不用返工。

注意:StdOutImpl 与 logging.level 互不干扰。即使你设了 logging.level.com.example.project.mapper=debug,也没办法让 StdOutImpl 的输出受控,因为它根本不走 SLF4J。

2.3 方式三:把 SQL 日志单独输出到文件

这个需求比较常见:排查线上问题时不希望 SQL 日志跟其他业务日志混在一起,而是单独放一个 sql.log,方便 grep。这时必须配合 Logback 配置。

先看 application.yml 部分的写法:

logging: level: com.example.project.mapper: debug

然后在 resources 下创建 logback-spring.xml,核心配置如下:

<?xml version="1.0" encoding="UTF-8"?> <configuration> <!-- 单独的文件 appender --> <appender name="SQL_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>logs/sql.log</file> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>logs/sql.%d{yyyy-MM-dd}.log</fileNamePattern> <maxHistory>30</maxHistory> </rollingPolicy> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> </encoder> </appender> <!-- 只针对 mapper 包 --> <logger name="com.example.project.mapper" level="DEBUG" additivity="false"> <appender-ref ref="SQL_FILE"/> </logger> </configuration>

这里有个关键细节:additivity="false"。如果你不写这一句,SQL 日志不仅会写入 sql.log,还会继续向上传播到 root logger,然后在控制台打一份、在其他业务日志文件里再打一份。很多同学配置后日志重复,就是栽在这个属性上。

这个方案在生产环境非常实用。我把 mapper 包下的日志单独落盘后,运维同学查问题只需要看 sql.log 一个文件,效率提升明显。daily 滚动保留 30 天,磁盘压力可控。

3. JPA/Hibernate 的 SQL 日志配置套路

用 Spring Data JPA 的同学经常会遇到另一种困惑:spring.jpa.show-sql=true 配置了,SQL 确实能看到,但日志里没有参数值,全是问号占位符,排查起来很难受。这其实是 Hibernate 的日志机制决定的。

3.1 show-sql 只是权宜之计,真正可控的是日志级别

先看最简单的配置方式:

spring: jpa: show-sql: true properties: hibernate.format_sql: true

show-sql=true 会把 SQL 输出到控制台,format_sql=true 会把 SQL 格式化换行,看起来更美观。但问题有两个:

  • 输出走的是 System.out,不是 Logback,不受日志级别控制,也无法归档。
  • SQL 里的参数值不会显示,你只能看到类似 select u.* from user u where u.id=? 的占位符。

所以 show-sql 更适合本地开发快速验证,生产环境不推荐。

真正可控的方式是走日志框架:

logging: level: org.hibernate.SQL: debug

这时 SQL 会通过 Logback 输出,并且受日志框架管控,可以归档、可以采集。但注意,参数值仍然不会显示在 SQL 那一行里,Hibernate 把参数绑定和结果集处理放在了更细的级别。

3.2 在日志里看到参数值:org.hibernate.type.descriptor.sql

如果你不光要看 SQL,还要看每个参数实际传进来的值,需要额外开启 trace 级别:

logging: level: org.hibernate.SQL: debug org.hibernate.type.descriptor.sql: trace

这样配置后,每次 SQL 执行,会在日志里输出一行绑定参数的信息,类似:

bind => [1, 张三]

我实际排查一个分页查询的分页参数传错问题时,就是因为 trace 级别下可以看到每个参数的实际类型和值,很快就定位到是 Pageable 的页码参数传了 0 导致查出的数据不符合预期。这在 debug 级别下根本看不出来。

不过这里有个性能层面的考虑:trace 级别的日志输出对高并发接口有一定开销,生产环境不建议长期开启。我的做法是:本地开发开 trace,生产只开 debug,遇到疑难问题时临时把某个接口的 logger 级别动态调成 trace,排查完立刻降回来。

3.3 JPA 配置对照表

把 JPA 的几种配置方式和效果整理成一张表,方便你按需选择:

配置方式输出位置是否显示参数是否可归档适合场景
spring.jpa.show-sql=true标准输出否否本地开发快速看 SQL
logging.level.org.hibernate.SQL=debug日志框架否是生产环境常规监控
再加 org.hibernate.type.descriptor.sql=trace日志框架是是排查参数类问题
配合 format_sql=true日志框架视上表是需要阅读复杂 SQL

4. Logback 配置文件实战:从基础到进阶

讲完了持久层框架侧,接下来重点说 Logback 本身。很多项目在运行一段时间后,日志管理需求会从"能打印"升级到"能控制、能归档、能分流",这时 logback-spring.xml 就是绕不开的环节。

4.1 logback-spring.xml 与 logback.xml 的区别

Spring Boot 官方推荐使用 logback-spring.xml,而不是 logback.xml。原因在于 logback-spring.xml 支持 Spring Boot 的 profile 特性,比如:

<springProfile name="dev"> <!-- 开发环境专用配置 --> </springProfile> <springProfile name="prod"> <!-- 生产环境专用配置 --> </springProfile>

这样同一份配置文件可以针对不同环境输出不同格式、不同文件目录。例如开发环境 SQL 日志直接输出到控制台,生产环境 SQL 日志输出到独立文件并做滚动归档。如果你用 logback.xml,就没有这个能力,需要维护多份配置文件。

另外还需要注意,配置文件的命名位置不能错。Spring Boot 默认会从 classpath 下加载 logback-spring.xml,所以要放在 resources 目录下。如果你用的是 yml 配置项 logging.config,可以指定自定义路径,但一般没必要改。

4.2 通过 logger 精确控制持久层日志

Logback 的 logger 节点是控制日志行为的核心。可以把它理解成一个筛选规则:name 属性指定包名或类名,level 属性指定日志级别,appender-ref 指定输出到哪里。

可以这样配置:

<logger name="com.example.project.mapper" level="DEBUG"/>

这个配置意味着 com.example.project.mapper 包下的所有 debug 级别及以上日志都会被收集,然后向上传递给 root logger 进行输出(控制台、文件等)。

如果你不想让 MyBatis 的内部日志轰炸你的文件,还可以单独把 MyBatis 框架自身的日志级别调高:

<logger name="org.apache.ibatis" level="INFO"/>

这在排错时很实用。MyBatis 在 debug 级别下会输出大量内部状态日志,信息量很大但也很吵,调成 INFO 后清爽很多,而我们真正关注的业务 mapper 包日志不受影响。

4.3 多环境配置推荐模板

我做项目时常用的模板是:dev 环境 SQL 日志走控制台,prod 环境 SQL 日志走独立文件,同时保留必要的文件滚动策略。贴合 Spring Boot profile 的写法推荐参考这段:

<configuration> <!-- 控制台 appender --> <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%d{HH:mm:ss.SSS} %-5level %logger{36} - %msg%n</pattern> <charset>UTF-8</charset> </encoder> </appender> <!-- SQL 专用文件 appender --> <appender name="SQL_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>logs/sql.log</file> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>logs/sql.%d{yyyy-MM-dd}.log</fileNamePattern> <maxHistory>30</maxHistory> <totalSizeCap>2GB</totalSizeCap> </rollingPolicy> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> </encoder> </appender> <logger name="com.example.project.mapper" level="DEBUG" additivity="false"> <springProfile name="prod"> <appender-ref ref="SQL_FILE"/> </springProfile> <springProfile name="dev"> <appender-ref ref="CONSOLE"/> </springProfile> </logger> <root level="INFO"> <appender-ref ref="CONSOLE"/> </root> </configuration>

这里有几个细节需要说明:

  • totalSizeCap 控制所有归档日志的总大小上限,避免日志无限膨胀撑爆磁盘。2GB 是我按业务量拍的一个合理值,你可以根据实际情况调整。
  • maxHistory 是保留天数,30 天对于线上排障来说够用。
  • 如果 additivity 保持默认 true,SQL 日志会在 SQL_FILE 输出之外,再往 root 的 CONSOLE 打一份,这不一定是你想要的,注意按需求决定。

4.4 异步输出 SQL 日志的取舍

高并发系统里,同步写文件日志会占用接口响应时间,所以 Logback 提供了 AsyncAppender 来实现异步日志。原理是先把日志事件放进一个阻塞队列,后台线程负责真正写文件,主线程无需等待磁盘 IO 完成。

如果你的系统并发较高且希望 SQL 日志不影响主流程性能,可以这样配置:

<appender name="ASYNC_SQL" class="ch.qos.logback.classic.AsyncAppender"> <queueSize>2048</queueSize> <discardingThreshold>0</discardingThreshold> <appender-ref ref="SQL_FILE"/> </appender>

注意 discardingThreshold 这个参数,官方默认值是队列容量的 20%,意思是当队列剩余容量低于这个比例时,会直接丢弃 INFO 及以下级别的日志事件来保护系统。但 SQL 日志本身是我们排查问题的依据,直接丢弃会让问题无处可查。所以建议把 discardingThreshold 设为 0,也就是队列满时阻塞等待而不是丢弃。

同时 queueSize 要根据 QPS 估算。一个简单的估算方法:假设每秒执行 500 条 SQL,每条日志平均 200 字节,那么每秒产生 100KB 日志,2048 的队列容量足够缓冲几秒的积压,其实已经够用了。如果 QPS 高一个量级,可以调到 8192,但要注意内存占用会相应增加。

我实际项目中用 AsyncAppender 配合独立 SQL 文件,在日均百万级 SQL 执行的系统上跑过,性能影响可以忽略,日志一条不少。不过这里也提醒一句:文件 appender 的磁盘 IO 能力是系统整体瓶颈,异步只是把压力后移,磁盘本身还是要监控的。

5. 慢 SQL 日志:从打印到监控的一步之遥

SQL 日志打出来之后,很多同学的下一步诉求是:能不能自动把执行时间超过阈值的 SQL 标记出来?这就是慢 SQL 日志。借助 Logback 或者 MyBatis 的拦截器机制,这个需求并不复杂。

5.1 基于 Logback 的耗时输出

Logback 的 pattern 里有一个 %r 或 %relative 参数,可以输出从应用启动到当前日志事件的时间差,单位毫秒。不过这个时间差跟单条 SQL 执行耗时没有直接关系,所以用它监控慢 SQL 并不靠谱。

更实用的方案是结合 MyBatis 拦截器,拦截 Executor 层的 query 和 update 方法,计算执行耗时。核心代码可以参考这段:

@Intercepts({ @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}), @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}) }) public class SlowSqlInterceptor implements Interceptor { private static final Logger log = LoggerFactory.getLogger(SlowSqlInterceptor.class); private long slowSqlMillis = 3000; @Override public Object intercept(Invocation invocation) throws Throwable { long start = System.currentTimeMillis(); try { return invocation.proceed(); } finally { long cost = System.currentTimeMillis() - start; if (cost > slowSqlMillis) { MappedStatement ms = (MappedStatement) invocation.getArgs()[0]; BoundSql boundSql = ms.getBoundSql(); log.warn("慢 SQL 执行耗时: {} ms, SQL: {}", cost, formatSql(boundSql.getSql())); } } } private String formatSql(String sql) { return sql.replaceAll("\\s+", " "); } }

然后在 MyBatis 配置里注册这个拦截器。Spring Boot 场景下,如果用的是 MyBatis-Plus,直接在配置类里声明一个 bean 即可;用的是原生 MyBatis,可以通过 org.apache.ibatis.session.Configuration 的 addInterceptor 注册。

这里有几个执行上的细节需要注意:

  • Optional 判断要放在 try 之外,否则慢 SQL 判断本身也会被算进耗时。
  • 反射获取 BoundSql 时还需要处理参数,实际项目中可以借助 ms.getConfiguration().newMetaObject 等方式获取完整的可执行 SQL,这里给的是简化版。
  • slowSqlMillis 阈值可以通过配置类注入,不要在代码里写死。生产环境一般建议先设 1 秒,观察一个周期后再调整。

5.2 慢 SQL 日志与独立文件结合

拦截器打出 warn 级别日志后,再利用 Logback 的 logger 配置把它引导到独立文件,这样慢 SQL 和普通 SQL 分开,定位问题的效率更高。

<logger name="com.example.project.interceptor.SlowSqlInterceptor" level="WARN" additivity="false"> <appender-ref ref="SLOW_SQL_FILE"/> </logger>

这本质上是 Logback 的精确路由能力。把拦截器类当成普通 logger 来定向输出,跟前面把 mapper 包输出到独立文件是同一个套路,只是粒度细到了类级别。

我实际做过一次比较极端的应用:把慢 SQL 日志接到一个单独的告警文件,配合脚本每分钟检测文件是否有新增内容,一旦发现就触发告警。虽然不如专业 APM 工具强大,但在小团队、预算有限的情况下,确实能用最低成本解决慢 SQL 的监控问题。

6. 常见问题排查与避坑指南

配置方式都讲完了,最后总结我这些年工作中遇到的高频问题。这些问题在官方文档里很难直接搜到答案,属于典型的"踩过才知道"系列。

6.1 问题速查表

现象根本原因解决方案
配置 logging.level 后 SQL 日志不出现MyBatis 还在用 StdOutImpl 或其他非 Slf4j 实现将 log-impl 改为 Slf4jImpl
改了 mapper 包路径配置没反应包路径写错,或写成了 XML 文件路径确认包路径是 Mapper 接口包,不是 resource 下的 XML 路径
SQL 日志在控制台打印两份Logback 里没有设置 additivity="false"为对应 logger 设置 additivity="false"
SQL 日志出现在业务日志文件,但控制台没有appender 配置指向了文件,没有指向 CONSOLE检查 logger 的 appender-ref 配置,按需增加 CONSOLE
日志里只有 Preparing,没有 Parameters级别开到了 debug 但缺少参数绑定输出检查 MyBatis log-impl 是否生效,或考虑换框架级别的日志输出
JPA 的 SQL 有占位符没有参数值只开了 org.hibernate.SQL debug额外开启 org.hibernate.type.descriptor.sql trace
配置了 logback-spring.xml 但完全不生效文件名或路径不对确认文件在 classpath 下且名称为 logback-spring.xml
profile 相关配置不生效配置文件是 logback.xml 而不是 logback-spring.xml重命名为 logback-spring.xml

6.2 配置不生效的三个常见原因

第一个原因是依赖冲突。如果你的项目里手动引入了 Log4j2 的依赖,同时又不小心排除了 spring-boot-starter-logging,那么 Logback 的配置很可能完全不生效。检查方法很简单:在 IDE 的依赖树里搜 ch.qos.logback,如果没有这个依赖,说明 Logback 已经不在运行时环境里了。

第二个原因是包路径大小写问题。MyBatis 的 mapper 接口包路径必须和实际代码完全一致,不能凭印象写一个看起来差不多的路径。我见过有人把 com.example.mapper 写成了 com.example.Mapper,排查了很久。

第三个原因是配置被 profile 覆盖。Spring Boot 支持多份配置文件,比如 application-dev.yml 和 application-prod.yml。如果你在 application.yml 里配了 logging.level.mapper=debug,但 application-dev.yml 里又把 root 级别设为 error,实际运行时的效果取决于 profile 激活情况。要养成习惯:所有日志级别配置集中在同一份主配置文件中,或者明确知道 profile 之间的覆盖规则。

6.3 日志打印重复问题的分析与解决

日志重复是最高频的坑,而且成因不止一种。除了前面说的 additivity 问题,还有一种隐蔽的场景:项目里不光有 logback-spring.xml,还保留了 logback.xml,两份配置文件同时存在时 Spring Boot 会优先加载 logback.xml,导致你的 logback-spring.xml 完全失效。

排查日志重复或日志配置不生效时,第一步不是看代码,而是看启动日志里有没有这一行:

Logging system: Logback

如果启动日志里显示的日志实现不是 Logback,说明你的依赖体系出了问题。如果是 Logback,再看加载的配置文件路径,Spring Boot 会在启动时打印类似Logging configuration: classpath:logback-spring.xml的信息。这两行信息能定位 80% 的问题。

还有一个容易忽略的点:某些中间件会自带日志配置。比如 Dubbo、Spring Cloud Gateway 等框架可能在依赖里带了自己的 logback 配置文件,通过 Maven 的 dependency 管理把项目里的配置文件覆盖掉。遇到这种情况,可以通过排除依赖或者指定 logging.config 来强制指定配置路径。

6.4 参数结果日志的隐私与安全提醒

SQL 日志里会打印参数值和查询结果,包括用户手机号、身份证、姓名等敏感信息。开发环境打出来没问题,但生产环境日志文件如果被未授权人员拿到,就是严重的数据泄露事件。

建议至少做到两点:

  • 生产环境只输出 SQL 语句,不输出参数值和结果集。MyBatis 系可以使用日志级别控制,不开启 trace 或调整 log-impl;JPA/Hibernate 系则不开 trace。
  • 如果业务确实需要完整 SQL 参数日志(比如金融支付类系统的对账需求),必须对日志文件做权限管控,日志采集链路要做脱敏处理。

这里有一个安全细节:MyBatis 的 StdOutImpl 输出结果集时,日志里会显示完整的结果行。相比 Slf4jImpl 只输出 Preparing 和 Parameters,StdOutImpl 的敏感信息暴露面更大。从这个角度讲,我也更推荐生产环境使用 Slf4jImpl 而不是 StdOutImpl。

6.5 动态调日志级别的实用技巧

最后分享一个小技巧:配置文件的日志级别是静态的,但排查线上问题时往往需要临时调高某个包的日志级别,又不想重启应用。Spring Boot 提供了 actuator 的 loggers 端点,可以动态修改。

依赖里加上 spring-boot-starter-actuator,然后通过 POST 请求即可:

curl -X POST http://localhost:8080/actuator/loggers/com.example.project.mapper \ -H "Content-Type: application/json" \ -d '{"configuredLevel":"DEBUG"}'

这个操作在生产环境非常实用。比如某个 SQL 接口突然变慢,你临时把 mapper 包级别调到 debug,查看日志后马上调回 info,全程不用重启,对在线业务零影响。

需要注意的是,这个端点在生产环境暴露时有安全风险,必须配合 Spring Security 做权限控制,或者至少限制内网访问。

写在最后

Spring Boot 打印 SQL 日志这件事,配置本身不难,难点在于搞清楚不同持久层框架的日志机制差异,以及怎么让日志输出体系跟业务监控需求匹配。我个人的经验是,不管用什么框架,生产环境永远优先走 Logback 体系,而不是依赖框架自身的 stdout 输出。统一的日志链路,后续做文件归档、日志采集、慢 SQL 监控,才能游刃有余。

最后再分享一个自己的习惯:每次新项目启动,先花两分钟确认日志配置文件被正确加载、mapper 包路径正确、SQL 日志能正常输出,再开始写业务代码。这个习惯帮我避免了无数次"项目写完了才发现日志体系有问题"的返工。你的项目如果现在还在为 SQL 日志发愁,照着上面的配置走一遍,应该五分钟之内就能看到自己想要的输出。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询