本文要解决的是如何将 mybatis 的 sql 日志打印到特定文件问题。
业务场景
在使用 mybatis-plus 的时候,我们有时需要将 SQL 打印到控制台,便于排查代码问题。
Mybatis-plus 需要通过下面的方式开启控制台 SQL 日志打印:
mybatis-plus:
configuration:
log-impl:
看起来确实没问题,但是,日志只能打印到控制台,而在项目上线初期,我们是希望线上环境也打印 sql 的。
不过,线上环境错综复杂,日志刷新飞快,我们当然只希望打印关键的信息,像 sql 日志这种刷屏的 log 显然是要禁止打印到控制台的。
日志配置
在 mybatis-plus 开启与关闭 SQL 日志打印 中,已经介绍过,mybatis 有多种日志实现方式。
下面这种配置是基于 log4j2 的日志配置:
mybatis-plus:
configuration:
log-impl: .log4j2.Log4j2Impl
注意,为了让上面的配置生效,还要在 文件中配置如下片段:
<Logger name="" level="DEBUG">
<AppenderRef ref="SQL-APPENDER"/>
</Logger>
- name 是当前项目 mapper 文件的包路径,
- level 是固定值 DEBUG ,
- appendref 需要我们定义一个 appender.
自定义的 appender 如下:
<RollingFile name="SQL-APPENDER"
fileName="logs/"
filePattern="logs/.%d{yyyy-MM-dd}"
append="true">
<PatternLayout
pattern="%d %-5p %c{2} - %m%n%throwable"
charset="UTF-8"/>
<TimeBasedTriggeringPolicy/>
<DefaultRolloverStrategy/>
</RollingFile>
配置结束,启动项目后,触发业务请求,然后打开 文件,看到执行的 sql 确实存在。
效果达到了,但是不完美,日志文件里多了很多 mybatis 其他的启动日志,通过观察,发现他们基本都是 INFO 级别的。
日志过滤
我们在上面设置日志的级别为 DEBUG,怎么把 INFO 也输出了呢?
这就得从 log 的 日志级别 说起了。
日志级别
日志级别是按严重(重要)程度来分的:
- OFF ↓
- FATAL ↓
- ERROR ↓
- WARN ↓
- INFO ↓
- DEBUG ↓
- TRACE ↓
- ALL ↓
从上到下,日志级别依次降低,打印的日志内容也越来越详细。
打印日志的规则:
levelP>=levelQ,则levelP会打印在levelQ的log里。
即:低级别的日志会包含高级别的日志内容。
举个例子:如果设置的日志级别是 debug,则会包括debug、info、error等高级别的日志。
ThresholdFilter
在 log4j2 中,ThresholdFilter 用来过滤日志相对应的日志级别。
它的三个重要参数:
- level: 标识需要过滤的日志级别,取值同上 日志级别
- INFO: info 日志级别
- DEBUG: debug 日志级别
- onMatch:
- ACCEPT: 表示匹配该级别及以上
- DENY:表示不匹配该级别及以上
- NEUTRAL:表示该级别及以上的,由下一个filter处理;如果当前是最后一个,则同 ACCEPT
- onMismatch:
- ACCEPT:表示匹配该级别以下
- DENY:表示不匹配该级别以下的
- NEUTRAL:表示该级别及以下的,由下一个filter处理,如果当前是最后一个,则同 DENY
基于上面的规则,很容易想到的方式来打印 DEBUG 级别日志:
<ThresholdFilter level="DEBUG" onMatch="ACCEPT" onMismatch="DENY"/>
打开 发现,并没有如你所愿,除了 debug 级别的日志, info 日志也打印了。
实际上,上面的配置是说:debug 及以上级别日志,包含 info 、warn、error等都接受,其他级别如 trace 丢弃。
于是,想办法把 info 级别的日志去除:
<ThresholdFilter level="INFO" onMatch="DENY" onMismatch="NEUTRAL"/>
上面的配置表示: info 级别包含该级别及以上,如 warn、error 日志拒绝,debug、trace 级别日志由下一个 filter 处理。
经典实用场景
要打印错误日志
项目中,经常会留一个文件只存放错误日志,然后监控错误日志的变化来感知项目健康状态。
<ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY"/>
上面的配置相当于取日志中的 ERROR 和 FATAL 级别内容,可以将其输出到 文件。
上面的配置也可以做如下简化:
<ThresholdFilter level="ERROR"/>
只打印 DEBUG 级别到文件
开头的问题,如果希望只打印 sql 到对应的文件,应该如下配置:
<ThresholdFilter level="INFO" onMatch="DENY" onMismatch="NEUTRAL"/>
<ThresholdFilter level="DEBUG" onMatch="ACCEPT" onMismatch="DENY"/>
这里使用了组合 filter ,组合可以让场景更为复杂,也嫩更为精准地控制日志内容。
组合需要注意一点:先定义日志级别 level 高的Filter。
总结
完整的 文件:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<!--定义appender -->
<Appenders>
...
<RollingFile name="SQL-APPENDER"
fileName="logs/"
filePattern="logs/.%d{yyyy-MM-dd}"
append="true">
<PatternLayout
pattern="%d %-5p %c{2} - %m%n%throwable"
charset="UTF-8"/>
<TimeBasedTriggeringPolicy/>
<DefaultRolloverStrategy/>
<ThresholdFilter level="INFO" onMatch="DENY" onMismatch="NEUTRAL"/>
<ThresholdFilter level="DEBUG" onMatch="ACCEPT" onMismatch="DENY"/>
</RollingFile>
<RollingFile name="ERROR-APPENDER"
fileName="logs/"
filePattern="logs/.%d{yyyy-MM-dd}"
append="true">
<PatternLayout
pattern="%d %-5p %c{2} - %m%n%throwable"
charset="UTF-8"/>
<TimeBasedTriggeringPolicy/>
<DefaultRolloverStrategy/>
<ThresholdFilter level="ERROR"/>
</RollingFile>
...
</Appenders>
<Loggers>
<Root level="info">
<AppenderRef ref="SQL-APPENDER"/>
<AppenderRef ref="ERROR-APPENDER"/>
</Root>
...
<Logger name="" level="DEBUG">
<AppenderRef ref="SQL-APPENDER"/>
</Logger>
</Loggers>
</Configuration>
上面的配置,可以让 文件中仅仅存在 debug 级别的 sql 日志,在 error 中包含系统错误日志。