Spring Boot中SQL日志的配置与优化实践 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框架的日志配置有所差异框架类型配置项示例特点说明Hibernatelogging.level.org.hibernate控制Hibernate内部日志级别JPAlogging.level.org.springframework.ormSpring Data JPA日志MyBatislogging.level.org.mybatisMyBatis框架日志JDBC Templatelogging.level.org.springframework.jdbcSpring JDBC日志3. Logback完整配置方案3.1 基础配置文件在resources目录下创建logback-spring.xml文件?xml version1.0 encodingUTF-8? configuration scantrue scanPeriod30 seconds !-- 控制台输出 -- appender nameCONSOLE classch.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 nameSQL_FILE classch.qos.logback.core.rolling.RollingFileAppender filelogs/sql.log/file rollingPolicy classch.qos.logback.core.rolling.TimeBasedRollingPolicy fileNamePatternlogs/sql.%d{yyyy-MM-dd}.log/fileNamePattern maxHistory30/maxHistory /rollingPolicy encoder pattern%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n/pattern /encoder /appender !-- SQL日志记录器 -- logger nameorg.hibernate.SQL levelDEBUG additivityfalse appender-ref refSQL_FILE/ appender-ref refCONSOLE/ /logger logger nameorg.hibernate.type.descriptor.sql.BasicBinder levelTRACE additivityfalse appender-ref refSQL_FILE/ appender-ref refCONSOLE/ /logger root levelINFO appender-ref refCONSOLE/ /root /configuration3.2 关键配置解析Hibernate SQL日志org.hibernate.SQLDEBUG输出执行的SQL语句org.hibernate.type.descriptor.sql.BasicBinderTRACE输出SQL参数值MyBatis日志logger nameorg.mybatis levelDEBUG/ logger namejava.sql.Connection levelDEBUG/ logger namejava.sql.Statement levelDEBUG/ logger namejava.sql.PreparedStatement levelDEBUG/日志文件滚动策略TimeBasedRollingPolicy按天分割日志文件maxHistory30保留最近30天的日志4. application.properties配置方案4.1 基础配置对于快速验证场景可以在application.properties中添加# Hibernate日志配置 logging.level.org.hibernate.SQLDEBUG logging.level.org.hibernate.type.descriptor.sql.BasicBinderTRACE # MyBatis日志配置 logging.level.org.mybatisDEBUG logging.level.java.sql.ConnectionDEBUG logging.level.java.sql.StatementDEBUG logging.level.java.sql.PreparedStatementDEBUG # 日志格式配置 logging.pattern.console%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n4.2 配置说明日志级别选择DEBUG显示SQL语句TRACE显示参数值需要更详细的日志时使用性能考虑生产环境不建议开启TRACE级别可以针对特定包路径开启DEBUG5. 高级配置技巧5.1 敏感数据脱敏在Logback配置中添加转换规则conversionRule conversionWordmsg converterClasscom.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 nameorg.hibernate.stat.Statistics levelDEBUG appender-ref refSLOW_SQL_FILE/ /logger对应的appender配置appender nameSLOW_SQL_FILE classch.qos.logback.core.rolling.RollingFileAppender filter classch.qos.logback.core.filter.EvaluatorFilter evaluator classch.qos.logback.classic.boolex.JaninoEventEvaluator expression return event.getFormattedMessage().contains(executed slowly); /expression /evaluator onMatchACCEPT/onMatch onMismatchDENY/onMismatch /filter !-- 其他配置同上 -- /appender6. 生产环境最佳实践6.1 日志分级策略建议采用以下分级策略环境SQL日志级别参数日志级别输出目标开发环境DEBUGTRACE控制台文件测试环境DEBUGDEBUG文件生产环境WARNOFF仅慢SQL日志6.2 性能优化建议异步日志记录appender nameASYNC_SQL classch.qos.logback.classic.AsyncAppender queueSize512/queueSize discardingThreshold0/discardingThreshold appender-ref refSQL_FILE/ /appender日志采样appender nameSAMPLING_SQL classch.qos.logback.classic.sift.SiftingAppender discriminator classcom.example.SQLSamplingDiscriminator/ !-- 其他配置 -- /appender7. 常见问题排查7.1 日志不输出问题检查清单确认配置文件名正确logback-spring.xml检查包路径是否正确org.hibernate.SQL等确认没有其他配置覆盖如application.properties中的配置检查日志级别是否足够DEBUG/TRACE7.2 日志格式混乱解决方案检查pattern配置中的占位符确保没有多个appender冲突检查日志编码设置建议显式设置UTF-87.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 classch.qos.logback.core.encoder.LayoutWrappingEncoder layout classcom.example.SqlPrettyLayout pattern%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n/pattern /layout /encoder实现自定义Layoutpublic 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配置可以很好地满足这种差异化需求。