1. MyBatis Plus打印SQL日志的核心价值
在数据库操作开发过程中,SQL日志是排查问题、优化性能的重要依据。MyBatis Plus作为MyBatis的增强工具,默认的日志输出往往无法满足深度调试需求。当我们需要检查执行的SQL语句是否正确、参数是否合理时,完整的SQL日志输出就显得尤为关键。
实际开发中遇到过这样的场景:一个复杂的多表联查在测试环境运行正常,上了生产却返回空结果。没有完整的SQL日志,我们花了3个小时才定位到是生产环境数据库字段命名规范不同导致的。如果当时开启了详细SQL日志,可能5分钟就能发现问题所在。
2. MyBatis Plus日志配置方案对比
2.1 内置日志实现分析
MyBatis Plus支持多种日志实现,每种实现方式在输出内容和格式上有所区别:
| 日志实现类 | 输出特点 | 适用场景 | 性能影响 |
|---|---|---|---|
| StdOutImpl | 控制台输出完整SQL及参数 | 本地开发调试 | 低 |
| Slf4jImpl | 通过SLF4J输出到日志文件 | 生产环境调试 | 中 |
| NoLoggingImpl | 不输出任何SQL日志 | 生产环境运行 | 无 |
提示:生产环境建议使用Slf4jImpl配合日志级别控制,避免日志量过大影响性能
2.2 推荐配置方案
在application.yml中配置最完整的日志输出:
mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl log-sql: true这种配置会输出类似如下的日志格式:
==> Preparing: SELECT id,name FROM user WHERE age > ? ==> Parameters: 18(Integer) <== Total: 33. 高级日志配置技巧
3.1 使用P6Spy实现SQL拦截
对于需要更详细SQL监控的场景,可以集成P6Spy:
- 添加依赖:
<dependency> <groupId>p6spy</groupId> <artifactId>p6spy</artifactId> <version>3.9.1</version> </dependency>- 配置spy.properties:
module.log=com.p6spy.engine.logging.P6LogFactory appender=com.p6spy.engine.spy.appender.StdoutLogger logMessageFormat=com.p6spy.engine.spy.appender.MultiLineFormat- 修改数据源配置:
spring.datasource.url=jdbc:p6spy:mysql://localhost:3306/testP6Spy会输出包含执行时间的完整SQL:
1592200395102|0|statement|connection 5|SELECT id,name FROM user WHERE age > 18|SELECT id,name FROM user WHERE age > 183.2 动态日志级别控制
通过代码动态调整日志级别:
@RestController @RequestMapping("/log") public class LogController { @GetMapping("/sql/{level}") public String setSqlLogLevel(@PathVariable String level) { Logger logger = (Logger) LoggerFactory.getLogger("com.baomidou.mybatisplus"); logger.setLevel(Level.valueOf(level)); return "SQL日志级别设置为:" + level; } }4. 生产环境日志优化
4.1 慢SQL监控配置
在application.yml中添加慢SQL阈值:
mybatis-plus: global-config: db-config: slow-sql-millis: 1000 log-slow-sql: true当日志中出现如下警告时,就需要关注SQL性能:
Slow SQL: 1256ms - SELECT * FROM large_table WHERE ...4.2 日志文件分离配置
在logback-spring.xml中单独配置SQL日志文件:
<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> </rollingPolicy> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern> </encoder> </appender> <logger name="com.baomidou.mybatisplus" level="DEBUG" additivity="false"> <appender-ref ref="SQL_FILE"/> </logger>5. 常见问题排查指南
5.1 日志不输出问题
检查清单:
- 确认配置文件中没有设置
log-impl: org.apache.ibatis.logging.nologging.NoLoggingImpl - 检查日志级别是否为DEBUG或TRACE
- 确保没有其他日志框架覆盖了MyBatis Plus的日志配置
5.2 参数显示为问号
如果看到SQL日志中的参数显示为?,通常是因为:
- 没有使用推荐的StdOutImpl
- 参数类型转换器配置有问题
- 使用了某些安全插件对敏感信息进行了脱敏
解决方案:
@Bean public ConfigurationCustomizer configurationCustomizer() { return configuration -> { configuration.setLogPrefix("MYBATIS-LOG -> "); configuration.setLogImpl(org.apache.ibatis.logging.stdout.StdOutImpl.class); }; }5.3 多数据源日志配置
对于多数据源场景,需要为每个数据源单独配置:
@Bean @ConfigurationProperties("spring.datasource.db1") public DataSource dataSourceDb1() { return DataSourceBuilder.create() .type(HikariDataSource.class) .build(); } @Bean public SqlSessionFactory sqlSessionFactoryDb1() throws Exception { MybatisSqlSessionFactoryBean factoryBean = new MybatisSqlSessionFactoryBean(); factoryBean.setDataSource(dataSourceDb1()); factoryBean.setConfiguration(new Configuration() {{ setLogImpl(StdOutImpl.class); }}); return factoryBean.getObject(); }6. 日志分析与性能优化
6.1 日志分析技巧
通过正则表达式提取关键信息:
# 提取执行时间超过100ms的SQL grep -E 'Time: \d{3,}ms' sql.log # 统计各SQL执行频率 awk -F'Preparing: ' '{print $2}' sql.log | sort | uniq -c | sort -nr6.2 基于日志的优化建议
当发现以下日志模式时需要特别注意:
- 频繁出现的相同SQL - 考虑使用缓存
- 大量
SELECT *查询 - 优化为只查询必要字段 - 多次单条INSERT - 合并为批量INSERT
- 没有使用索引的查询 - 添加适当索引
我在实际项目中遇到过这样一个案例:日志显示某个分页查询每次都要执行count语句,通过添加缓存后性能提升了20倍:
// 优化前 Page<User> page = new Page<>(1, 10); userMapper.selectPage(page, null); // 优化后 Page<User> page = new Page<>(1, 10, false); // 不查询总记录数7. 安全与隐私注意事项
- 生产环境避免输出完整SQL参数,特别是包含敏感信息的字段
- 使用日志脱敏插件处理密码等敏感信息
- 定期清理历史日志文件
- 设置适当的日志文件权限
推荐的安全配置示例:
public class SensitiveDataSqlLogger extends StdOutImpl { @Override public void debug(String text) { text = text.replaceAll("(password|token)=([^&]*)", "$1=***"); super.debug(text); } }在配置中指定自定义logger:
mybatis-plus: configuration: log-impl: com.example.config.SensitiveDataSqlLogger8. 扩展应用场景
8.1 结合Arthas进行动态调试
当生产环境不能直接修改日志级别时,可以使用Arthas动态修改:
# 查看当前logger logger --name com.baomidou.mybatisplus # 修改日志级别 logger --name com.baomidou.mybatisplus --level debug8.2 与Prometheus监控集成
通过日志解析实现SQL监控指标:
@Aspect @Component public class SqlMonitorAspect { private final Counter sqlCounter = Counter.build() .name("sql_exec_total") .help("Total SQL executions") .register(); @AfterReturning("execution(* org.apache.ibatis.session.SqlSession.*(..))") public void afterSqlExecution() { sqlCounter.inc(); } }8.3 多租户场景下的日志区分
在多租户系统中,可以在日志中增加租户标识:
public class TenantAwareLogger extends StdOutImpl { @Override public void debug(String text) { String tenantId = TenantContext.getCurrentTenant(); super.debug("[" + tenantId + "] " + text); } }9. 日志格式自定义
9.1 实现自定义日志格式
继承BaseJdbcLogger实现个性化日志:
public class CustomSqlLogger extends BaseJdbcLogger { @Override public void debug(String text) { String formatted = LocalDateTime.now() + " | " + Thread.currentThread().getName() + " | " + text; System.out.println(formatted); } }9.2 彩色日志输出
在支持ANSI颜色的终端中输出彩色日志:
public class ColorSqlLogger extends StdOutImpl { private static final String GREEN = "\033[32m"; private static final String RESET = "\033[0m"; @Override public void debug(String text) { super.debug(GREEN + text + RESET); } }10. 性能考量与最佳实践
- 开发环境可以使用最详细的日志级别,但生产环境应该根据实际情况调整
- 高频执行的SQL可以考虑抽样记录,避免日志爆炸
- 将SQL日志输出到单独文件,便于后续分析
- 定期检查日志文件大小,设置合理的滚动策略
实测数据表明,开启DEBUG级别日志会使系统吞吐量降低约15-20%,因此建议:
- 开发环境:DEBUG级别
- 测试环境:INFO级别+慢SQL日志
- 生产环境:WARN级别+告警日志
在Spring Boot中可以通过Profile区分配置:
--- spring: profiles: dev mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl --- spring: profiles: prod mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl