Spring Boot+Hibernate应用:如何禁用Hibernate查询输出至标准输出?
问题背景
使用Spring Boot 3.2.3、Hibernate 6.4.4、Logback和ojdbc10时,Hibernate查询以Hibernate: SELECT A FROM B格式直接输出到stdout,无时间戳,疑似通过System.out.println输出。已尝试多种Spring配置(如spring.jpa.properties.hibernate.show_sql=false、调整各类Hibernate日志级别为ERROR)均未生效。
核心原因
Hibernate的show_sql参数不通过日志框架输出,而是直接写入System.out,因此仅调整Logback日志级别无法屏蔽该输出。即使配置了show_sql=false,也可能存在配置被其他优先级更高的设置覆盖的情况。
解决方案
1. 确保show_sql被正确禁用
检查配置优先级,确保spring.jpa.properties.hibernate.show_sql=false未被覆盖:
- 排查启动参数、环境变量中是否存在
-Dspring.jpa.properties.hibernate.show_sql=true类配置 - 确认代码中未硬编码设置该参数(例如在
LocalContainerEntityManagerFactoryBean的自定义配置中) - 若使用多环境配置,验证当前生效的配置文件中该值为
false
2. 强制重定向System.out到Logback(彻底拦截)
如果上述步骤无效,可通过Logback捕获System.out输出并按日志级别过滤:
步骤1:修改Logback配置添加System.out处理逻辑
更新logback.xml,新增捕获System.out的Appender和Logger:
<configuration scan="true" scanPeriod="30 seconds" debug="false"> <!-- 原有STDOUT Appender保留 --> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <filter class="ch.qos.logback.classic.filter.ThresholdFilter"> <level>${APP_LOG_LEVEL:-ERROR}</level> </filter> <encoder> <pattern>%d{dd/MM/yyyy HH:mm:ss.SSS} %-5level [%.-30thread][%X{gatewayId}| %X{requestId}] %logger{70} - %msg%n%ex</pattern> </encoder> </appender> <!-- 新增:处理System.out的Appender,仅输出ERROR级别 --> <appender name="SYSTEM_OUT" class="ch.qos.logback.core.ConsoleAppender"> <filter class="ch.qos.logback.classic.filter.ThresholdFilter"> <level>ERROR</level> </filter> <encoder> <pattern>%d{dd/MM/yyyy HH:mm:ss.SSS} %-5level [%.-30thread][%X{gatewayId}| %X{requestId}] SYSTEM_OUT - %msg%n</pattern> </encoder> </appender> <!-- 新增:将System.out的输出桥接到Logback的SYSTEM_OUT Logger --> <logger name="SYSTEM_OUT" level="INFO" additivity="false"> <appender-ref ref="SYSTEM_OUT"/> </logger> <!-- 原有Logger配置保留 --> <logger name="etcd" level="${ETCD_LOG_LEVEL:-ERROR}"/> <logger name="org.apache.http" level="${APACHE_HTTP_LOG_LEVEL:-ERROR}"/> <logger name="org.apache.kafka" level="${KAFKA_LOG_LEVEL:-ERROR}"/> <logger name="org.springframework" level="${SPRING_FWK_LOG_LEVEL:-INFO}"/> <logger name="n" additivity="false"> <appender-ref ref="LF-ASYNC"/> <appender-ref ref="STDOUT"/> </logger> <root level="${APP_LOG_LEVEL:-INFO}"> <appender-ref ref="STDOUT" /> </root> </configuration>
步骤2:在Spring Boot启动类中重定向System.out
添加@PostConstruct方法替换System.out输出流,将所有System.out内容转发到Logback:
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import javax.annotation.PostConstruct; import java.io.PrintStream; @SpringBootApplication public class YourApplication { private static final Logger SYSTEM_OUT_LOGGER = LoggerFactory.getLogger("SYSTEM_OUT"); public static void main(String[] args) { SpringApplication.run(YourApplication.class, args); } @PostConstruct public void redirectSystemOut() { System.setOut(new PrintStream(new LoggerOutputStream(SYSTEM_OUT_LOGGER), true)); } // 自定义输出流,将System.out内容转为Logback日志 private static class LoggerOutputStream extends java.io.OutputStream { private final Logger logger; private final StringBuilder sb = new StringBuilder(); public LoggerOutputStream(Logger logger) { this.logger = logger; } @Override public void write(int b) { if (b == '\n') { logger.info(sb.toString()); sb.setLength(0); } else { sb.append((char) b); } } } }
这样Hibernate通过System.out输出的SQL会被转为Logback的INFO级别日志,而由于SYSTEM_OUT Logger的Appender阈值设为ERROR,这些SQL输出会被自动过滤。
3. 排查第三方依赖或自定义代码
检查是否有第三方库(如JPA扩展、监控工具)或自定义配置类中强制开启了Hibernate的SQL输出:
- 排查代码中是否存在
org.hibernate.cfg.Configuration.setProperty("hibernate.show_sql", "true")这类硬编码配置 - 确认是否使用了自动开启SQL日志的Starter组件
内容的提问来源于stack exchange,提问作者Daniele

