1. Spring Boot项目中SQL日志打印的必要性与场景
在开发基于Spring Boot的企业级应用时,数据库操作是核心功能之一。作为开发者,我们经常需要查看MyBatis或MyBatis-Plus生成的SQL语句及其执行结果,这主要出于以下几个目的:
- 调试验证:确认ORM框架生成的SQL是否符合预期,特别是复杂查询时
- 性能优化:分析SQL执行效率,发现潜在的N+1查询等问题
- 审计追踪:在特定业务场景下需要记录数据变更历史
- 问题排查:当出现数据不一致时,通过日志还原操作过程
我在实际项目中发现,大约80%的数据库相关问题都可以通过分析SQL日志快速定位。特别是在使用MyBatis动态SQL时,条件分支可能导致生成的SQL与预期不符,这时候详细的日志输出就显得尤为重要。
2. Logback配置详解与最佳实践
2.1 Logback配置文件的选择与加载顺序
Spring Boot支持两种Logback配置文件:
-
logback-spring.xml(推荐)
- 专为Spring Boot设计
- 支持Spring Profile特定配置
- 加载优先级高于logback.xml
-
logback.xml
- 标准Logback配置文件
- 不支持Spring Profile
- 会被logback-spring.xml覆盖
建议始终使用logback-spring.xml,因为它提供了更好的Spring集成。文件位置应放在src/main/resources目录下。
2.2 核心配置解析
以下是一个完整的logback-spring.xml配置示例,包含了SQL日志打印的最佳实践:
xml复制<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="30 seconds">
<!-- 控制台输出 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %highlight(%-5level) %cyan(%logger{36}) - %msg%n</pattern>
</encoder>
</appender>
<!-- SQL日志专用appender -->
<appender name="SQL_APPENDER" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} | %highlight(SQL) | %msg%n</pattern>
</encoder>
</appender>
<!-- MyBatis Mapper接口日志 -->
<logger name="com.example.mapper" level="DEBUG" additivity="false">
<appender-ref ref="SQL_APPENDER" />
</logger>
<!-- MyBatis-Plus日志 -->
<logger name="com.baomidou.mybatisplus" level="WARN" />
<!-- JDBC连接池日志 -->
<logger name="com.zaxxer.hikari" level="INFO" />
<!-- Spring事务日志 -->
<logger name="org.springframework.jdbc" level="INFO" />
<logger name="org.springframework.transaction" level="INFO" />
<!-- 根日志配置 -->
<root level="INFO">
<appender-ref ref="CONSOLE" />
</root>
</configuration>
关键配置说明:
-
Mapper接口日志:
- 包路径应设置为你的Mapper接口所在包
- additivity="false"避免日志重复输出
- 使用专用appender使SQL日志更醒目
-
日志级别选择:
- DEBUG:显示SQL语句和参数
- TRACE:显示结果集(谨慎使用,可能产生大量日志)
-
彩色输出:
- 使用%highlight和%cyan等转换词使不同级别日志显示不同颜色
2.3 多环境差异化配置
利用Spring Profile可以实现不同环境下的差异化日志配置:
xml复制<springProfile name="dev">
<logger name="com.example.mapper" level="DEBUG" />
</springProfile>
<springProfile name="prod">
<logger name="com.example.mapper" level="WARN" />
</springProfile>
3. MyBatis与MyBatis-Plus的额外配置
3.1 MyBatis原生配置
在application.properties中可添加:
properties复制# 显示SQL语句
logging.level.com.example.mapper=DEBUG
# MyBatis配置(可选)
mybatis.configuration.log-impl=org.apache.ibatis.logging.slf4j.Slf4jImpl
或者在mybatis-config.xml中:
xml复制<configuration>
<settings>
<setting name="logImpl" value="SLF4J"/>
</settings>
</configuration>
3.2 MyBatis-Plus增强配置
对于MyBatis-Plus,推荐以下配置:
properties复制# MyBatis-Plus SQL日志打印
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
# 显示banner(开发环境)
mybatis-plus.global-config.banner=false
4. 高级技巧与性能优化
4.1 敏感信息过滤
对于包含敏感信息的SQL(如密码、手机号),可以自定义Logback过滤器:
xml复制<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<filter class="com.example.logging.SensitiveDataFilter" />
<encoder>...</encoder>
</appender>
实现示例:
java复制public class SensitiveDataFilter extends Filter<ILoggingEvent> {
@Override
public FilterReply decide(ILoggingEvent event) {
String message = event.getFormattedMessage();
if (message.contains("password")) {
return FilterReply.DENY;
}
return FilterReply.NEUTRAL;
}
}
4.2 SQL格式化输出
使用MyBatis插件实现SQL美化:
java复制@Intercepts(@Signature(type = StatementHandler.class,
method = "query",
args = {Statement.class, ResultHandler.class}))
public class SqlFormatInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
Object result = invocation.proceed();
// 格式化SQL日志
return result;
}
}
4.3 日志文件分离
将SQL日志单独输出到文件:
xml复制<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{36} - %msg%n</pattern>
</encoder>
</appender>
5. 常见问题排查与解决方案
5.1 日志不输出问题排查
-
检查包路径是否正确
- 确认logger配置的包名与Mapper接口包名一致
- 使用通配符检查:
<logger name="com.example.*" level="DEBUG" />
-
检查日志级别冲突
- 确保没有更高级别的logger覆盖了你的配置
- 检查root logger的级别是否过高
-
检查配置文件加载
- 确认使用的是logback-spring.xml而非logback.xml
- 检查配置文件是否在resources目录下
5.2 性能问题优化
- 生产环境日志级别调整
- 将Mapper接口日志级别调整为WARN或ERROR
- 使用异步appender减少I/O阻塞:
xml复制<appender name="ASYNC_SQL" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="SQL_APPENDER" />
<queueSize>512</queueSize>
<discardingThreshold>0</discardingThreshold>
</appender>
- 限制日志输出量
- 使用EvaluatorFilter过滤高频日志:
xml复制<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.core.filter.EvaluatorFilter">
<evaluator>
<expression>return message.contains("SELECT") &&
logger.startsWith("com.example.mapper");</expression>
</evaluator>
<OnMismatch>DENY</OnMismatch>
</filter>
<encoder>...</encoder>
</appender>
5.3 多数据源配置
对于多数据源项目,需要为每个Mapper接口包配置独立的logger:
xml复制<logger name="com.example.order.mapper" level="DEBUG" additivity="false">
<appender-ref ref="ORDER_SQL_APPENDER" />
</logger>
<logger name="com.example.user.mapper" level="DEBUG" additivity="false">
<appender-ref ref="USER_SQL_APPENDER" />
</logger>
6. 实际案例:电商项目中的SQL日志实践
在我最近参与的电商平台项目中,我们通过以下配置实现了高效的SQL日志管理:
-
开发环境配置:
- 完整SQL日志输出
- 慢SQL警告(超过100ms)
- 结果集抽样打印(每10条记录打印1条)
-
生产环境配置:
- 仅记录慢SQL(超过500ms)
- 错误SQL详细记录
- 使用ELK集中管理日志
关键配置片段:
xml复制<springProfile name="prod">
<logger name="com.example.mapper" level="WARN">
<appender-ref ref="SLOW_SQL_APPENDER" />
</logger>
<appender name="SLOW_SQL_APPENDER" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>WARN</level>
</filter>
<file>logs/slow-sql.log</file>
<rollingPolicy>...</rollingPolicy>
</appender>
</springProfile>
这个配置帮助我们发现了多个性能问题,包括:
- 一个商品查询接口的N+1问题
- 未使用索引的全表扫描
- 事务范围过大的批量操作
通过分析SQL日志,我们将系统响应时间平均降低了40%。