1. 项目背景与核心需求
在Spring Boot应用开发过程中,SQL日志的打印是调试和优化数据库操作的重要依据。很多开发者都遇到过这样的困境:明明程序运行正常,但就是查不到执行的SQL语句;或者能看到SQL却看不到参数值;更麻烦的是当出现数据异常时,无法快速定位是哪条SQL导致了问题。
我经历过一个典型场景:在电商促销期间,某个商品库存扣减出现了异常。当时控制台只显示了"Hibernate: update product set stock=?"这样的日志,完全无法判断具体扣减了多少库存。如果当时有完整的SQL日志记录,问题排查时间至少能缩短70%。
2. 技术方案选型分析
2.1 日志框架对比
Spring Boot支持多种日志框架,但最主流的有两种配置方式:
Logback配置方案(推荐):
- Spring Boot默认集成的日志框架
- 配置灵活,支持多种输出格式
- 性能优异,适合生产环境
- 可以通过XML或Groovy配置
application.properties配置方案:
- 配置简单,适合快速验证
- 功能相对有限
- 适合开发环境临时使用
提示:生产环境建议使用Logback方案,它提供了更精细的日志控制能力。
2.2 ORM框架注意事项
不同ORM框架的日志配置有所差异:
| 框架类型 | 配置项示例 | 特点说明 |
|---|---|---|
| Hibernate | logging.level.org.hibernate | 控制Hibernate内部日志级别 |
| JPA | logging.level.org.springframework.orm | Spring Data JPA日志 |
| MyBatis | logging.level.org.mybatis | MyBatis框架日志 |
| JDBC Template | logging.level.org.springframework.jdbc | Spring JDBC日志 |
3. Logback完整配置方案
3.1 基础配置文件
在resources目录下创建logback-spring.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] %-5level %logger{36} - %msg%n</pattern> </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> </rollingPolicy> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> </encoder> </appender> <!-- SQL日志记录器 --> <logger name="org.hibernate.SQL" level="DEBUG" additivity="false"> <appender-ref ref="SQL_FILE"/> <appender-ref ref="CONSOLE"/> </logger> <logger name="org.hibernate.type.descriptor.sql.BasicBinder" level="TRACE" additivity="false"> <appender-ref ref="SQL_FILE"/> <appender-ref ref="CONSOLE"/> </logger> <root level="INFO"> <appender-ref ref="CONSOLE"/> </root> </configuration>3.2 关键配置解析
Hibernate SQL日志:
org.hibernate.SQL=DEBUG:输出执行的SQL语句org.hibernate.type.descriptor.sql.BasicBinder=TRACE:输出SQL参数值
MyBatis日志:
<logger name="org.mybatis" level="DEBUG"/> <logger name="java.sql.Connection" level="DEBUG"/> <logger name="java.sql.Statement" level="DEBUG"/> <logger name="java.sql.PreparedStatement" level="DEBUG"/>日志文件滚动策略:
TimeBasedRollingPolicy:按天分割日志文件maxHistory=30:保留最近30天的日志
4. application.properties配置方案
4.1 基础配置
对于快速验证场景,可以在application.properties中添加:
# Hibernate日志配置 logging.level.org.hibernate.SQL=DEBUG logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE # MyBatis日志配置 logging.level.org.mybatis=DEBUG logging.level.java.sql.Connection=DEBUG logging.level.java.sql.Statement=DEBUG logging.level.java.sql.PreparedStatement=DEBUG # 日志格式配置 logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n4.2 配置说明
日志级别选择:
DEBUG:显示SQL语句TRACE:显示参数值(需要更详细的日志时使用)
性能考虑:
- 生产环境不建议开启
TRACE级别 - 可以针对特定包路径开启DEBUG
- 生产环境不建议开启
5. 高级配置技巧
5.1 敏感数据脱敏
在Logback配置中添加转换规则:
<conversionRule conversionWord="msg" converterClass="com.example.SensitiveDataConverter"/>示例转换器实现:
public class SensitiveDataConverter extends ClassicConverter { @Override public String convert(ILoggingEvent event) { String message = event.getFormattedMessage(); // 对手机号、身份证等敏感信息进行脱敏处理 return message.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2"); } }5.2 慢SQL监控
在Logback配置中添加:
<logger name="org.hibernate.stat.Statistics" level="DEBUG"> <appender-ref ref="SLOW_SQL_FILE"/> </logger>对应的appender配置:
<appender name="SLOW_SQL_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <filter class="ch.qos.logback.core.filter.EvaluatorFilter"> <evaluator class="ch.qos.logback.classic.boolex.JaninoEventEvaluator"> <expression> return event.getFormattedMessage().contains("executed slowly"); </expression> </evaluator> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> <!-- 其他配置同上 --> </appender>6. 生产环境最佳实践
6.1 日志分级策略
建议采用以下分级策略:
| 环境 | SQL日志级别 | 参数日志级别 | 输出目标 |
|---|---|---|---|
| 开发环境 | DEBUG | TRACE | 控制台+文件 |
| 测试环境 | DEBUG | DEBUG | 文件 |
| 生产环境 | WARN | OFF | 仅慢SQL日志 |
6.2 性能优化建议
异步日志记录:
<appender name="ASYNC_SQL" class="ch.qos.logback.classic.AsyncAppender"> <queueSize>512</queueSize> <discardingThreshold>0</discardingThreshold> <appender-ref ref="SQL_FILE"/> </appender>日志采样:
<appender name="SAMPLING_SQL" class="ch.qos.logback.classic.sift.SiftingAppender"> <discriminator class="com.example.SQLSamplingDiscriminator"/> <!-- 其他配置 --> </appender>
7. 常见问题排查
7.1 日志不输出问题
检查清单:
- 确认配置文件名正确(logback-spring.xml)
- 检查包路径是否正确(org.hibernate.SQL等)
- 确认没有其他配置覆盖(如application.properties中的配置)
- 检查日志级别是否足够(DEBUG/TRACE)
7.2 日志格式混乱
解决方案:
- 检查pattern配置中的占位符
- 确保没有多个appender冲突
- 检查日志编码设置(建议显式设置UTF-8)
7.3 性能问题
优化建议:
- 减少不必要的TRACE级别日志
- 使用异步日���记录
- 对高频操作单独配置日志级别
8. 扩展功能实现
8.1 SQL执行时间统计
自定义拦截器实现:
@Interceptor public class SqlTimingInterceptor implements EmptyInterceptor { private static final Logger logger = LoggerFactory.getLogger("SQL_TIMING"); @Override public String onPrepareStatement(String sql) { long startTime = System.currentTimeMillis(); return new StatementWrapper(sql, startTime); } private static class StatementWrapper implements String { private final String sql; private final long startTime; // 实现方法... @Override public String toString() { long cost = System.currentTimeMillis() - startTime; if(cost > 200) { // 超过200ms视为慢SQL logger.warn("Slow SQL detected: {} [{}ms]", sql, cost); } return sql; } } }8.2 SQL美化输出
添加Logback布局配置:
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder"> <layout class="com.example.SqlPrettyLayout"> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> </layout> </encoder>实现自定义Layout:
public class SqlPrettyLayout extends PatternLayout { @Override public String doLayout(ILoggingEvent event) { String message = super.doLayout(event); if(event.getLoggerName().contains("SQL")) { return formatSql(message); } return message; } private String formatSql(String rawSql) { // 实现SQL格式化逻辑 } }在实际项目中,我通常会根据团队需求对这些配置进行定制。一个实用的技巧是为不同的开发人员创建不同的日志配置文件,比如DBA可能更关注执行计划,而业务开发更关心参数值。通过环境变量动态加载不同的logback配置,可以很好地满足这种差异化需求。