资讯详情

MyBatis-Plus日志输出到控制台:配置方法与原理详解

📅 2026/9/10 18:09:25 | 华诺云谱 👁 阅读
MyBatis-Plus日志输出到控制台:配置方法与原理详解
先问你一句你在IDEA里启动Spring Boot项目页面点得飞快接口也正常返回了可控制台就是看不到SQL。数据明明查出来了但你根本不知道MyBatis底层到底拼了什么条件、传了哪些参数。改了两行代码之后结果不对只能反复试、反复猜。这种靠瞎猜来排错的日子我相信每个用MyBatis-Plus的人都经历过。这里的“mp”就是MyBatis-Plus国内Java后端几乎绕不开的增强ORM框架。它原生继承了MyBatis的日志机制但默认并不向控制台输出SQL。很多人第一次处理这个问题要么百度到一段配置粘上去没反应要么日志刷屏不知道怎么关。这篇文章就专门聊清楚一件事怎么把MP的日志稳定输出到控制台以及这套配置背后的原理是什么。不管你是刚入门、还在到处复制粘贴配置的初级开发还是被SQL查错折磨已久的老兵这篇都值得花五分钟读完。我不只给代码还会把为什么这么配、踩过哪些坑都讲明白。1. 先搞清楚MP日志的来龙去脉1.1 MP的日志到底是谁在管MyBatis-Plus确实是增强框架但在日志这一件事上它没有另起炉灶底层玩的还是MyBatis那套。MyBatis从很早开始就设计了一套独立的日志门面接口org.apache.ibatis.logging.Log下面挂着一堆实现类常见的有StdOutImpl、Slf4jImpl、Log4j2Impl、Jdk14LoggingImpl、NoLoggingImpl。这套设计最大的好处是MyBatis不绑死任何一个日志框架。它在启动时通过LogFactory扫描classpath检测到哪个日志框架存在就自动用哪个。Spring Boot项目里几乎必然有SLF4J和Logback所以MP的SQL日志默认会走SLF4J再由Logback负责输出到控制台或文件。这里有必须理解的一个配置项mybatis-plus.configuration.log-impl。从名字就能看出来它对应的是MyBatis原生的logImpl配置作用就是手动指定MyBatis的日志实现类。当你手动指定后自动检测机制就退居二线MyBatis会用你指定的实现来打印SQL日志。很多文章只说“照着配置就行”但如果你不懂这个机制后面遇到“配了没反应”或“日志重复打印”就会一脸懵。我建议你先把log-impl这个单词刻在脑子里它是整个MP日志开关的核心。1.2 为什么默认看不到SQL这个问题新手问得最多。代码能跑、数据能查控制台就是干干净净一张SQL都看不到。原因不复杂MyBatis执行SQL时打印的日志级别是DEBUG而Spring Boot的默认日志级别是INFO。DEBUG比INFO低直接就被过滤掉了。但还有一个更隐蔽的情况。很多公司的Spring Boot基础工程里logback被二次封装过根日志级别是INFO控制台Appender也只接INFO及以上DEBUG日志在框架层就丢了。这时候哪怕你在配置里写了logging.level.com.xxx.mapperdebug也可能看不到SQL因为logback的根logger级别把DEBUG挡住了。另外数据库连接池比如Druid也有自己的日志开关。有些团队之前是开了Druid的SQL日志来排查问题的如果这时候再开MP的日志两套SQL日志叠加在控制台上看起来会非常混乱。你得先分清看到的SQL到底是谁打印的。所以在查MP日志之前先确认两件事第一SQL日志是什么级别第二最终日志框架到底会不会把DEBUG级别输出到控制台。理清楚了后面的配置才谈得上准确性。2. 实操三分钟把MP日志输出到控制台2.1 方法一配置log-impl直连控制台这个方法最简单也最暴力。在application.yml里加上mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl如果你用的是application.properties等价写法是mybatis-plus.configuration.log-implorg.apache.ibatis.logging.stdout.StdOutImpl配置完成后重启应用控制台立刻能看到SQL的Preparing、Parameters这些日志。原理是MyBatis的日志实现被强制切成了StdOutImpl它直接在代码里调用System.out.println把日志打到控制台完全绕过SLF4J和Logback。这套方案的好处是“快、直接、不依赖其他日志框架”特别适合本地临时想看一眼SQL的时候。但我必须提醒你它不适合作为团队长期方案。第一它不走日志框架日志没有时间戳、线程名、类名这些上下文排起错来信息量严重不足第二System.out.println是带锁的高并发下大量打印SQL会有性能损耗第三它没法做日志归档、按级别过滤生产环境一开就刷屏。我的习惯是本地快速复现问题时用一下问题一解决马上还原配置绝不留在代码里。2.2 方法二通过logging.level指定Mapper包这应该是最推荐的做法。在application.yml里加一段logging: level: com.example.demo.mapper: debug这里的关键是把com.example.demo.mapper换成你项目里Mapper接口所在的包名。配置之后MyBatis在执行SQL时产生的DEBUG日志会通过SLF4J交给Logback只要Logback的根日志级别是INFO且控制台Appender配置正常SQL日志就会稳定输出。为什么这个方式更优雅因为日志格式完全统一了。你会看到带时间戳、线程名、Logger名称的标准格式比如2025-06-10 14:32:11.123 DEBUG 12345 --- [http-nio-8080-exec-1] c.e.demo.mapper.UserMapper.selectById : Preparing: SELECT id,name,email FROM user WHERE id?这条日志既能进控制台也能通过Logback的配置落到文件里后面的排查和监控都方便。另外这个方案完全不碰MyBatis自己的log-impl配置MyBatis自动发现SLF4J各层级各司其职。你可能会问能不能只指定到具体某一个Mapper当然可以。logging.level.com.example.demo.mapper.UserMapper: debug这种写法更精确适合只想盯某张表的SQL时使用。2.3 方法三在logback配置文件里单独加Logger如果你的项目日志配置已经集中在logback-spring.xml里团队也习惯了在XML里管理所有日志那就可以用第三种方式直接加一个Logger节点configuration logger namecom.example.demo.mapper leveldebug/ root levelinfo appender-ref refCONSOLE/ /root /configuration这个配置的效果和方法二一样都是让MyBatis SQL日志以DEBUG级别输出到控制台。区别在于管理位置。方法二适合个别人临时查问题方法三适合团队把MP日志纳入统一的日志规范里。如果只想针对某个XML Mapper的namespace控制可以把name写得更具体例如com.example.mapper.UserMapper。这样其他Mapper保持安静只有UserMapper的SQL打印出来线上排查某一类问题时特别好用。三种方法我用一个表格总结一下方便你对照选型配置方式配置位置走日志框架适合场景指定log-impl为StdOutImplapplication.yml否直接System.out本地最快看到SQLlogging.level指定mapper包application.yml是SLF4J/Logback开发环境推荐、可进日志文件logback中配置loggerlogback-spring.xml是SLF4J/Logback团队日志统一管理、指定Mapper排查3. 日志输出内容深度解读3.1 一份典型的MP日志长什么样我用方法二开启日志后执行一次selectById控制台输出的内容大概是这个样子的 Preparing: SELECT id,name,age,email FROM user WHERE id? Parameters: 1(Integer) Columns: id, name, age, email Row: 1, 张三, 25, zhangsanexample.com Total: 1这段日志信息量非常大但很多人在实际排错时根本没仔细看只扫一眼有没有报错就完事了。其实每一行都有它自己的含义看懂了能节省大量时间。3.2 每一行日志代表什么Preparing这一行展示的是预编译后的SQL语句。注意这里仍然是带问号占位符的模板不是真实参数值。比如WHERE id?问号对应的值要到下一行Parameters才能看到。这是JDBC预编译机制的正常表现不是日志没打全。Parameters这一行列出的是SQL语句中每个占位符的实际绑定值。格式是值(类型)比如1(Integer)表示第一个参数是整型1。如果SQL里有多个参数会用逗号分隔例如1(Integer), 张三(String)。Columns只出现在查询语句中展示ResultSet返回的列名。Row是查询返回的实际数据行一行数据对应一条记录。Total则是本次查询返回的行数这个数字对判断查询结果是否符合预期很重要比如分页查询里Total: 10说明总共有10条数据。3.3 通过日志反推MyBatis执行行为日志不只是看SQL好不好使还能帮你判断MyBatis内部做了什么。一个很经典的场景循环里调用同一个selectById查不同ID理论上应该执行N次SQL但你发现控制台上Preparing只出现了一次后面几次循环都没有SQL日志。这不是日志丢了而是MyBatis的一级缓存生效了。默认情况下SqlSession生命周期内相同SQL和参数会命中本地缓存直接返回缓存结果不再发SQL到数据库。还有一个场景是批量操作。MyBatis的BatchExecutor在执行insert或update时会攒批并不会每条立刻提交。你会发现日志里Preparing只有一次但Parameters连着好几行。等你调用flushStatements或事务提交时才真正把批量的SQL发给数据库。这时候看日志如果只看前半段会误以为数据没写进去。分页日志也值得注意。如果用了MyBatis-Plus的分页插件SQL末尾会出现LIMIT ?Parameters里也会多出两个参数一个偏移量一个条数。比如你查第2页、每页10条Parameters里就应该有10(Integer), 10(Integer)这种组合。看到这个基本可以确认分页插件正常拦截了SQL。4. 进阶玩法让日志更好用4.1 用P6Spy打印能直接执行的完整SQL用MP自带日志看到的是带占位符的SQL和参数分行展示。本地调试没问题但遇到要拿着SQL去数据库客户端里复现问题的时候你得手动把参数拼回去麻烦又容易错。这时候我建议引入P6Spy它能在JDBC层拦下完整的、可以直接执行的SQL。步骤很简单。先引入依赖dependency groupIdp6spy/groupId artifactIdp6spy/artifactId version3.9.1/version /dependency然后修改数据源配置URL加jdbc:p6spy:前缀驱动换成P6SpyDriverspring: datasource: driver-class-name: com.p6spy.engine.spy.P6SpyDriver url: jdbc:p6spy:mysql://localhost:3306/test_db?useUnicodetruecharacterEncodingutf8 username: root password: 123456最后在src/main/resources下新建spy.propertiesappendercom.p6spy.engine.spy.appender.Slf4JLogger logMessageFormatcom.p6spy.engine.spy.appender.CustomLineFormat customLogMessageFormat%(executionTime) ms | %(sql)重启项目后控制台输出的SQL就是真实参数拼接好的5 ms | SELECT id,name,age,email FROM user WHERE id1这条SQL直接复制到Navicat或者命令行就能执行。executionTime字段还能直观看到每条SQL的耗时排查慢SQL非常有用。4.2 自定义日志格式与敏感字段脱敏P6Spy更香的地方在于支持完全自定义输出格式。举个例子如果表里有个手机号字段你不想让SQL日志里出现完整的敏感数据可以自己写一个MessageFormat实现类public class SensitiveSqlFormat implements MessageFormat { Override public String formatMessage(int connectionId, String now, long elapsed, String category, String prepared, String sql, String url) { String safeSql sql.replaceAll((phone\\s*\\s*?)[^\\s], $1***); return elapsed ms | safeSql; } }然后在spy.properties里把logMessageFormat改成这个类logMessageFormatcom.example.config.SensitiveSqlFormat这样SQL日志里phone138****1234就会被替换成phone***既能看到完整的查询结构又不暴露个人敏感信息。这个方法在金融、医疗类项目里很实用既能日常排查又兼顾数据安全合规。4.3 开发环境打印、生产环境自动关闭日志这东西开发环境越详细越好生产环境越安静越好。我是推荐用Spring Profile把日志行为分开的。在application-dev.yml里配置logging: level: com.example.demo.mapper: debug在application-prod.yml里要么什么都不配要么直接把日志级别拉高logging: level: com.example.demo.mapper: warn也可以用logback-spring.xml里的springProfile标签实现同一份配置按环境切换springProfile name!prod logger namecom.example.demo.mapper leveldebug/ /springProfile springProfile nameprod logger namecom.example.demo.mapper levelerror/ /springProfile这样开发环境天天看SQL生产环境不会因为SQL打印白白增加IO开销也不会把敏感数据写进生产日志文件。多环境配置一定要从项目第一天就做好等生产环境日志刷屏了再来补救已经被动很多了。5. 常见问题与避坑记录5.1 配置了却不打印的五种原因我在实际给团队排查时遇到的“开了MP日志但控制台没反应”情况整理下来基本就是下面这五种对照着排查会快很多现象原因解决办法配置了log-impl但控制台空白application.yml缩进错误配置没生效检查YAML缩进确认前缀是mybatis-plus.configuration控制台只出现部分SQLdebug级别只加到了某个子包把级别提升到Mapper接口所在的最外层包日志显示?而不是具体值这是预编译SQL的正常表现看Parameters那行不要盯着Preparing开发环境有、生产环境没有环境日志级别不同检查生产profile和logback配置SQL日志和服务日志混在一起MyBatis日志走System.out未走日志框架改回logging.level方案统一格式5.2 重复打印SQL的坑这个坑我踩过一次印象特别深。当时项目的application.yml里既有mybatis-plus.configuration.log-impl: StdOutImpl又有logging.level.com.example.mapper: debug。启动后控制台一条SQL出现两遍看起来是“双份日志”我还以为是框架Bug查了半天。后来才明白这两种配置走了两条完全不同的输出通道log-impl指定了StdOutImplMyBatis直接System.out.println打一遍logging.level又让SLF4J把日志交给Logback再打一遍。两边各自独立自然就重复了。解决方式很简单二选一。开发环境我更推荐保留logging.level方案因为日志格式统一、能进文件、能查上下文log-impl只适合临时快速看SQL。团队里如果发现重复日志多半就是有人图省事把两种方式都加上了。5.3 生产环境日志刷屏怎么处理有一次项目刚上线数据库突然慢下来一查发现生产环境控制台被SQL日志刷到飞起。原因是同事把开发环境的配置原封不动带到生产logging.level.com.example.mapper: debug一开全表的SQL全出来了文件IO瞬间被打满。遇到这种情况第一件事就是把生产环境的日志级别调回去用上面提到的Profile方案。如果线上确实需要保留SQL日志用于排错建议单独开一个审计Logger把SQL写入独立文件并设置滚动策略比如每天一个文件、单个文件最大100MB、保留7天。这样既能保留现场又不至于把磁盘写爆。5.4 大字段和长SQL把日志打爆日志表里存储了一个超大字段每次查询这条记录MyBatis日志会把整条数据原样打出来。控制台输出几千个字符还算小事生产环境如果频繁查询大字段日志文件会以肉眼可见的速度膨胀磁盘告警迟早会来。短期的办法是把logging.level精确到不包含该大字段查询的Mapper上但这不是根本解法。长期方案还是上P6Spy并自定义MessageFormat只输出SQL模板和参数不输出查询结果的Row内容。你需要的只是确认SQL长什么样数据库返回的数据能不能查到完全可以通过业务代码的返回结果来判断。5.5 不同MyBatis-Plus版本的日志行为差异MyBatis-Plus在3.4.x、3.5.x这些主流版本里configuration.log-impl配置基本上是一致的没有太大变化。真正容易出问题的场景反而是项目里同时存在mybatis和mybatis-plus两个依赖时两者版本不一致导致日志实现类加载异常。轻则SQL日志不打印重则启动时直接ClassNotFoundException。如果你用的是Spring Boot 3.x MyBatis-Plus 3.5.x的组合还要注意jakarta.*包名的变化。自定义Log实现类如果还用老版本的包名会一眼看去编译都过不了更别说日志输出。遇到版本问题我的建议很直接项目里不要自己手动加mybatis依赖一切交给MyBatis-Plus的BOM统一管理避免出现两个版本的MyBatis在classpath里打架。最后再分享一个我自己的使用习惯。本地调试时我会直接开logging.level的DEBUG而且只针对当前正在写的那个Mapper包其他包保持INFO。这样控制台既能看到关键SQL又不会被全项目日志淹没。代码里涉及敏感表时我会顺手用P6Spy的脱敏格式兜底。项目从建立那天起就把开发、测试、生产三套Profile分开日志开关各管各的这样就不会等到上线之后才慌慌张张去补配置。这套组合拳用下来至今没被SQL日志问题坑过第二次。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。