在日常Java开发中经常用到MyBatis框架,在调试的时候,经常因为看不到全部的SQL而不得不手动去拼接SQL。因为笔者查阅了大量资料发现P6Spy框架可以解决此类问题。
首先需要继承P6Spy的依赖
<!--配置打印SQL语句,执行时间,执行参数--><dependency><groupId>com.github.gavlyukovskiy</groupId><artifactId>p6spy-spring-boot-starter</artifactId><version>${p6spy.version}</version></dependency>
此处我使用的版本是
<p6spy.version>1.8.0</p6spy.version>
接下来需要配置spy.properties配置文件,我的配置文件如下:
#MODULE p6spy提供了两种模块进行日志记录:log和outage,两者有不同的功能,配置参数也有专属,但是它们也共享一些配置参数,如哪些表被记录,日志文件名称和地址,是否显示sql执行轨迹等。#log用来拦截和记录任何使用jdbc的应用的数据库声明日志,默认为开启。#outage主要是用来最低化log所带来的性能问题,只记录超过一定时间的执行语句,默认为关闭。module.log=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactorymodule.outage=com.p6spy.engine.outage.P6OutageFactorylogMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger#实际的数据库驱动,真正的数据库驱动#realdriver=com.mysql.jdbc.Driver#实际的数据库驱动备份,当前面的数据库驱动不对时,顺序查找下一驱动,直到找到合适为止,默认为空。#realdriver2=#realdriver3=#无效化已注册的驱动,如果在其他地方已经定义好了真正的数据库驱动,那么p6spy driver就不会生效,也就不能起到作用,所以需#要把此选项置为true。deregisterdrivers=true#log模块专属的参数,当log模块开启时,如果执行语句超出这个时间(单位为毫秒),才能被记录在文件中,可以重新被载入,默认为0。executionthreshold=#outage专属的参数,当outage模块开启时,outagedetection为true时,会根据outagedetectioninterval(单位为秒)的大小, 间隔的去捕获执行语句,一般用来捕获长时间执行的语句。。outagedetection=trueoutagedetectioninterval=2#以下参数是公共的属性,log和outage都可以公用的参数过滤器开关,是否根据参数过滤一些记录内容filter=true#当过滤器开启时,需要记录的表,默认为都记录#include=#当过滤器开启时,不需要记录的表,默认为都记录exclude= foreign_key_checks,variable_name,GET_LOCK,RELEASE_LOCK,flyway_schema_history,information_schema,@,SELECT DATABASE(),SELECT version(),ACT_,QRTZ_# 过滤 Log 时的 SQL 正则表达式名称 默认为空#当过滤器开启时,根据sql表达式过滤#sqlexpression =#是否自动刷新autoflush = true#输出的日志文件的日期格式,也就是用Java的SimpleDateFormat程序。dateformat= yyyy-MM-dd HH:mm:ss#定义包含的日志级别,当日志级别属于此类型时,才能被记录,属性值有error, info, batch, debug, statement, commit, rollback 和result#includecategories=statement#定义不包含的日志级别,当日志级别属于此类型时,不会被记录;#可去掉的结果集有error,info,batch,debug,statement,commit,rollback,result,resultset.excludecategories=info,debug,result,commit,resultset#使用正则表达式来过滤 Log,匹配时才会被记录,例如: #stringmatcher=com.p6spy.engine.common.GnuRegexMatcher #stringmatcher=com.p6spy.engine.common.JakartaRegexMatcher#stringmatcher=#是否对每一SQL的执行语句进行打印堆栈跟踪信息,通常在进行长时间执行SQL的情况下打开进行监控。stacktrace=false#当上一轨迹开关打开时,可以指定具体的类名来进行过滤。stacktraceclass =#监测属性配置文件是否进行重新加载,一般应用服务器在启动时进行加载一次就够了。reloadproperties=false#当是否重新加载开关打开时,定义重新加载时间周期。reloadpropertiesinterval = 60#是否加上前缀,设置为 true,会加上 p6spy: 作为前缀useprefix=false#指定 Log 的 appender,与 Log4J 有点同义,取值:com.p6spy.engine.logging.appender.Log4jLogger com.p6spy.engine.logging.appender.StdoutLogger com.p6spy.engine.logging.appender.FileLogger# 日志只输出到控制台,不会记录到日志文件appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger# 使用日志系统记录 sql#appender=com.p6spy.engine.spy.appender.Slf4JLogger#指定记录的日志文件名称和地址,根目录在应用服务器的发布端.如tomcat在%TOMCAT_HOME%/bin目录下。logfile = ./logs/logs.log#文件续载标识,在log的appender类型为FileLogger时,才生效,如果为true,则在生成的日志文件后面继续进行记录,否则删除之前的内容。#append=true#类似与log4j的记录器的布局:#log4j.appender.STDOUT=org.apache.log4j.ConsoleAppender#log4j.appender.STDOUT.layout=org.apache.log4j.PatternLayout#log4j.appender.STDOUT.layout.ConversionPattern=p6spy - #%m%n
其中会涉及到两个类
pro.skywalking.configuration.p6spy.P6spyLogFormatStrategy
以及
pro.skywalking.configuration.p6spy.P6SpyLogger
这两个类的主要作用是自定义日志打印规则。
下面是这两个类的主要内容
package pro.skywalking.configuration.p6spy;import com.p6spy.engine.logging.Category;import com.p6spy.engine.spy.appender.FormattedLogger;import lombok.extern.slf4j.Slf4j;import org.apache.commons.lang3.StringUtils;/*** @author Singer create by singer - Singer email:singer-coder@qq.com* @projectName multi-platform* @Description:* @date 2024-05-22*/4jpublic class P6SpyLogger extends FormattedLogger {public void logException(Exception e) {log.info("", e);}public void logText(String text) {log.info(text);}/*** 根据类别打印不同等级的日志** @param connectionId: 连接ID* @param now: 当前时间* @param elapsed: 花费时间* @param category: 类别* @param prepared: 预编译SQL* @param sql: 最终执行的SQL* @param url: 数据库连接地址* @date 2020/1/16 9:52* @author lixiangx@leimingtech.com**/public void logSQL(int connectionId, String now, long elapsed, Category category, String prepared, String sql, String url) {final String msg = strategy.formatMessage(connectionId, now, elapsed,category.toString(), prepared, sql, url);if (StringUtils.isEmpty(msg)) {return;}if (Category.ERROR.equals(category)) {log.error(msg);} else if (Category.WARN.equals(category)) {log.warn(msg);} else if (Category.DEBUG.equals(category)) {log.debug(msg);} else {log.info(msg);}}/*** 根据类别开启指定的日志级别** @param category 日志类别* @return 是否开启* @date 2020/1/16 10:42* @author lixiangx@leimingtech.com**/public boolean isCategoryEnabled(Category category) {if (Category.ERROR.equals(category)) {return log.isErrorEnabled();} else if (Category.WARN.equals(category)) {return log.isWarnEnabled();} else if (Category.DEBUG.equals(category)) {return log.isDebugEnabled();} else {return log.isInfoEnabled();}}}
package pro.skywalking.configuration.p6spy;import cn.hutool.core.util.StrUtil;import com.p6spy.engine.spy.appender.MessageFormattingStrategy;/*** @author Singer create by singer - Singer email:singer-coder@qq.com* @projectName multi-platform* @Description:* @date 2024-05-22*/public class P6spyLogFormatStrategy implements MessageFormattingStrategy {/*** 日志格式化方式(打印SQL日志会进入此方法,耗时操作,生产环境不建议使用)** @param connectionId: 连接ID* @param now: 当前时间* @param elapsed: 花费时间* @param category: 类别* @param prepared: 预编译SQL* @param sql: 最终执行的SQL* @param url: 数据库连接地址* @return 格式化日志结果* @date 2020/1/16 9:52* @author lixiangx@leimingtech.com**/public String formatMessage(int connectionId, String now, long elapsed,String category, String prepared, String sql,String url) {String sqlMessage = StrUtil.format("链接ID:{},SQL耗时:{}毫秒,\n 连接信息:{},\n 最终执行SQL:{}\n ",connectionId,elapsed,url,sql);return sqlMessage;}}
完成以上操作,启动项目,部署系统,即可打印SQL到控制台。

下面是spy.properties配置文件的其他配置说明,以供参考。
指定应用的日志拦截模块,默认为com.p6spy.engine.spy.P6SpyFactorymodulelist=com.p6spy.engine.spy.P6SpyFactory,com.p6spy.engine.logging.P6LogFactory,com.p6spy.engine.outage.P6OutageFactory真实JDBC driver , 多个以 逗号 分割 默认为空driverlist=是否自动刷新 默认 flaseautoflush=false配置SimpleDateFormat日期格式 默认为空dateformat=打印堆栈跟踪信息 默认flasestacktrace=false如果 stacktrace=true,则可以指定具体的类名来进行过滤。stacktraceclass=监测属性配置文件是否进行重新加载reloadproperties=false属性配置文件重新加载的时间间隔,单位:秒 默认60sreloadpropertiesinterval=60指定 Log 的 appender,取值:appender=com.p6spy.engine.spy.appender.Slf4JLoggerappender=com.p6spy.engine.spy.appender.StdoutLoggerappender=com.p6spy.engine.spy.appender.FileLogger指定 Log 的文件名 默认 spy.loglogfile=spy.log指定是否每次是增加 Log,设置为 false 则每次都会先进行清空 默认trueappend=true指定日志输出样式 默认为com.p6spy.engine.spy.appender.SingleLineFormat , 单行输出 不格式化语句logMessageFormat=com.p6spy.engine.spy.appender.SingleLineFormat也可以采用 com.p6spy.engine.spy.appender.CustomLineFormat 来自定义输出样式, 默认值是%(currentTime)|%(executionTime)|%(category)|connection%(connectionId)|%(sqlSingleLine)可用的变量为:%(connectionId) connection id%(currentTime) 当前时间%(executionTime) 执行耗时%(category) 执行分组%(effectiveSql) 提交的SQL 换行%(effectiveSqlSingleLine) 提交的SQL 不换行显示%(sql) 执行的真实SQL语句,已替换占位%(sqlSingleLine) 执行的真实SQL语句,已替换占位 不换行显示customLogMessageFormat=%(currentTime)|%(executionTime)|%(category)|connection%(connectionId)|%(sqlSingleLine)date类型字段记录日志时使用的日期格式 默认dd-MMM-yydatabaseDialectDateFormat=dd-MMM-yyboolean类型字段记录日志时使用的日期格式 默认boolean 可选值numericdatabaseDialectBooleanFormat=boolean是否通过jmx暴露属性 默认truejmx=true如果jmx设置为true 指定通过jmx暴露属性时的前缀 默认为空com.p6spy(.<jmxPrefix>)?:name=<optionsClassName>jmxPrefix=是否显示纳秒 默认falseuseNanoTime=false实际数据源 JNDIrealdatasource=/RealMySqlDS实际数据源 datasource classrealdatasourceclass=com.mysql.jdbc.jdbc2.optional.MysqlDataSource实际数据源所携带的配置参数 以 k=v 方式指定 以 分号 分割realdatasourceproperties=port;3306,serverName;myhost,databaseName;jbossdb,foo;barjndi数据源配置设置 JNDI 数据源的 NamingContextFactory。jndicontextfactory=org.jnp.interfaces.NamingContextFactory设置 JNDI 数据源的提供者的 URL。jndicontextproviderurl=localhost:1099设置 JNDI 数据源的一些定制信息,以分号分隔。jndicontextcustom=java.naming.factory.url.pkgs;org.jboss.naming:org.jnp.interfaces是否开启日志过滤 默认false, 这项配置是否生效前提是配置了 include/exclude/sqlexpressionfilter=false过滤 Log 时所包含的表名列表,以逗号分隔 默认为空include=过滤 Log 时所排除的表名列表,以逗号分隔 默认为空exclude=过滤 Log 时的 SQL 正则表达式名称 默认为空sqlexpression=显示指定过滤 Log 时排队的分类列表,取值: error, info, batch, debug, statement,commit, rollback, result and resultset are valid values(默认 info,debug,result,resultset,batch)excludecategories=info,debug,result,resultset,batch是否过滤二进制字段(default is false)excludebinary=falseP6Log 模块执行时间设置,整数值 (以毫秒为单位),只有当超过这个时间才进行记录 Log。 默认为0executionThreshold=P6Outage 模块是否记录较长时间运行的语句 默认falseoutagedetection=true|falseP6Outage 模块执行时间设置,整数值 (以秒为单位)),只有当超过这个时间才进行记录 Log。 默认30soutagedetectioninterval=integer time (seconds)
至此,完成配置spy.properties配置文件的其他配置内容说明,读者可以自行参考。
