在日常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.MybatisPlusLogFactory
module.outage=com.p6spy.engine.outage.P6OutageFactory
logMessageFormat=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=true
outagedetectioninterval=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
*/
4j
public 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.P6SpyFactory
modulelist=com.p6spy.engine.spy.P6SpyFactory,com.p6spy.engine.logging.P6LogFactory,com.p6spy.engine.outage.P6OutageFactory
真实JDBC driver , 多个以 逗号 分割 默认为空
driverlist=
是否自动刷新 默认 flase
false autoflush=
配置SimpleDateFormat日期格式 默认为空
dateformat=
打印堆栈跟踪信息 默认flase
false stacktrace=
true,则可以指定具体的类名来进行过滤。 如果 stacktrace=
stacktraceclass=
监测属性配置文件是否进行重新加载
false reloadproperties=
属性配置文件重新加载的时间间隔,单位:秒 默认60s
reloadpropertiesinterval=60
指定 Log 的 appender,取值:
appender=com.p6spy.engine.spy.appender.Slf4JLogger
appender=com.p6spy.engine.spy.appender.StdoutLogger
appender=com.p6spy.engine.spy.appender.FileLogger
指定 Log 的文件名 默认 spy.log
logfile=spy.log
false 则每次都会先进行清空 默认true 指定是否每次是增加 Log,设置为
true append=
指定日志输出样式 默认为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-yy
databaseDialectDateFormat=dd-MMM-yy
boolean类型字段记录日志时使用的日期格式 默认boolean 可选值numeric
databaseDialectBooleanFormat=boolean
true 是否通过jmx暴露属性 默认
true jmx=
true 指定通过jmx暴露属性时的前缀 默认为空 如果jmx设置为
com.p6spy(.<jmxPrefix>)?:name=<optionsClassName>
jmxPrefix=
false 是否显示纳秒 默认
false useNanoTime=
实际数据源 JNDI
realdatasource=/RealMySqlDS
实际数据源 datasource class
realdatasourceclass=com.mysql.jdbc.jdbc2.optional.MysqlDataSource
实际数据源所携带的配置参数 以 k=v 方式指定 以 分号 分割
realdatasourceproperties=port;3306,serverName;myhost,databaseName;jbossdb,foo;bar
jndi数据源配置
设置 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/sqlexpression 是否开启日志过滤 默认
false filter=
过滤 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
是否过滤二进制字段
false) (default is
false excludebinary=
P6Log 模块执行时间设置,整数值 (以毫秒为单位),只有当超过这个时间才进行记录 Log。 默认为0
executionThreshold=
false P6Outage 模块是否记录较长时间运行的语句 默认
true|false outagedetection=
P6Outage 模块执行时间设置,整数值 (以秒为单位)),只有当超过这个时间才进行记录 Log。 默认30s
integer time (seconds) outagedetectioninterval=
至此,完成配置spy.properties配置文件的其他配置内容说明,读者可以自行参考。