如何在Spring的logback-spring.xml中过滤特定SQL查询日志?
我来帮你搞定这个问题!你遇到的核心痛点是:MyBatis输出SQL日志时,你加的注释-- NOT_SQL_LOG和SQL主体会被拆分成独立的日志事件,所以你的自定义Logback拦截器只过滤了注释那一行,没覆盖整个SQL的查询、参数、结果日志。另外你用EvaluatorFilter启动失败,大概率是因为缺少必要的依赖库。下面给你几个可行的解决方案:
方案一:利用MDC标记+Logback过滤(业务代码侵入式)
这种方法需要在启动时执行的SQL调用逻辑里添加MDC标记,让Logback识别并过滤这些日志:
在业务代码中添加MDC标记
在你启动时调用的retrieveMessage方法里,用MDC临时标记需要过滤的SQL:import org.slf4j.MDC; public Message retrieveMessage(String param) { try { // 标记需要跳过日志的SQL MDC.put("SKIP_SQL_LOG", "true"); return sqlSessionTemplate.selectOne("retrieveMessage", param); } finally { // 执行完毕后移除标记,避免影响其他SQL MDC.remove("SKIP_SQL_LOG"); } }修改Logback配置
更新你的STDOUT-SQL-RESULTSETappender,添加基于MDC的EvaluatorFilter(记得先补全依赖):<appender name="STDOUT-SQL-RESULTSET" class="ch.qos.logback.core.ConsoleAppender"> <filter class="ch.qos.logback.core.filter.EvaluatorFilter"> <evaluator> <!-- 判断MDC中是否存在标记 --> <expression>mdc.get("SKIP_SQL_LOG") != null && mdc.get("SKIP_SQL_LOG").equals("true")</expression> </evaluator> <OnMatch>DENY</OnMatch> <!-- 匹配到标记就拒绝输出 --> <OnMismatch>NEUTRAL</OnMismatch> <!-- 不匹配则继续后续过滤 --> </filter> <encoder> <charset>UTF-8</charset> <pattern>%msg%n</pattern> </encoder> </appender>补充依赖(解决EvaluatorFilter启动失败)
Logback的EvaluatorFilter依赖Janino库,需要在pom.xml中添加:<dependency> <groupId>org.codehaus.janino</groupId> <artifactId>janino</artifactId> <version>3.1.11</version> <!-- 用最新稳定版即可 --> </dependency>
方案二:自定义MyBatis拦截器(无业务代码侵入)
这种方法通过MyBatis的拦截器机制,自动识别带特定注释的SQL并标记MDC,不需要修改业务代码:
编写MyBatis拦截器
拦截MyBatis的SQL执行过程,识别带-- NOT_SQL_LOG注释的SQL并添加MDC标记:import org.apache.ibatis.executor.statement.StatementHandler; import org.apache.ibatis.plugin.*; import org.apache.ibatis.session.ResultHandler; import org.slf4j.MDC; import java.sql.Connection; import java.util.Properties; @Intercepts({ @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class}) }) public class SkipSqlLogInterceptor implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { StatementHandler statementHandler = (StatementHandler) invocation.getTarget(); String sql = statementHandler.getBoundSql().getSql().trim(); // 判断SQL是否包含需要跳过日志的注释 if (sql.startsWith("-- NOT_SQL_LOG")) { MDC.put("SKIP_SQL_LOG", "true"); } try { return invocation.proceed(); } finally { // 执行完毕后移除标记 MDC.remove("SKIP_SQL_LOG"); } } @Override public Object plugin(Object target) { return Plugin.wrap(target, this); } @Override public void setProperties(Properties properties) { // 可配置自定义参数,这里暂时不需要 } }注册MyBatis拦截器
在Spring Boot中,通过@Bean注册拦截器:import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class MyBatisConfig { @Bean public SkipSqlLogInterceptor skipSqlLogInterceptor() { return new SkipSqlLogInterceptor(); } }复用方案一的Logback配置
直接使用方案一中的Logback过滤器配置即可,不需要额外修改。
方案三:直接通过SQL内容过滤(简单但不够灵活)
如果你能确定启动时的SQL有唯一的特征(比如特定的表名或查询内容),可以直接在Logback中过滤包含该特征的日志:
<filter class="ch.qos.logback.core.filter.EvaluatorFilter"> <evaluator> <expression>message.contains("blahblah")</expression> <!-- 替换为你的SQL特征 --> </evaluator> <OnMatch>DENY</OnMatch> <OnMismatch>NEUTRAL</OnMismatch> </filter>
这种方法的缺点是如果运行时也有相同特征的SQL,会被误过滤,所以仅适合启动SQL有唯一标识的场景。
为什么你之前的方法无效?
- 自定义Logback拦截器只过滤了包含
NOT_SQL_LOG的单条日志事件,但MyBatis会把注释、SQL准备、参数、结果拆分成多条日志,所以只有注释行被过滤,其他日志依然输出。 EvaluatorFilter启动失败是因为缺少Janino依赖,补全依赖后就能正常工作。
内容的提问来源于stack exchange,提问作者user11269434

