一、介绍
p6spy是一个开源项目,通常使用它来跟踪数据库操作,查看程序运行过程中执行的sql语句。
1、原理
p6spy将应用的数据源给劫持了,应用操作数据库其实在调用p6spy的数据源,p6spy劫持到需要执行的sql或者hql之类的语句之后,他自己去调用一个realDatasource,再去操作数据库,
包括P6Log和P6Outage两个模块:P6Log 用来拦截和记录任务应用程序的 JDBC 语句,P6Outage 专门用来检测和记录超过配置条件里时间的 SQL 语句.
2、应用场景
p6spy 可以输出日志到文件中、控制台、或者传递给 Log4j,而且还能配搭 SQL Profiler 或 IronTrackSQL 图形化监控 SQL 语句,监测到哪些语句的执行是耗时的,逐个优化。
3、配置文件spy.properties
# 指定应用的日志拦截模块,默认为com.p6spy.engine.spy.P6SpyFactory
#modulelist=com.p6spy.engine.spy.P6SpyFactory,com.p6spy.engine.logging.P6LogFactory,com.p6spy.engine.outage.P6OutageFactory
#driverlist=
#autoflush=false
#dateformat=yyyy-MM-dd HH:mm:ss
#stacktrace=false
#stacktraceclass=
#reloadproperties=false
#reloadpropertiesinterval=60
#appender=com.p6spy.engine.spy.appender.Slf4JLogger
#appender=com.p6spy.engine.spy.appender.StdoutLogger
#appender=com.p6spy.engine.spy.appender.FileLogger
#logfile=spy.log
#append=true
#logMessageFormat=com.p6spy.engine.spy.appender.SingleLineFormat
#customLogMessageFormat=%(currentTime)|%(executionTime)|%(category)|connection%(connectionId)|%(sqlSingleLine)
#举例
#logMessageFormat=com.p6spy.engine.spy.appender.CustomLineFormat
#customLogMessageFormat=%(currentTime) | SQL耗时: %(executionTime) ms | 连接信息: %(category)-%(connectionId) | 执行语句: %(sql)
#databaseDialectDateFormat=dd-MMM-yy
#databaseDialectBooleanFormat=boolean
#jmx=true
#jmxPrefix=
#useNanoTime=false
#realdatasource=/RealMySqlDS
#realdatasourceclass=com.mysql.jdbc.jdbc2.optional.MysqlDataSource
#realdatasourceproperties=port;3306,serverName;myhost,databaseName;jbossdb,foo;bar
#jndicontextfactory=org.jnp.interfaces.NamingContextFactory
#jndicontextproviderurl=localhost:1099
#jndicontextcustom=java.naming.factory.url.pkgs;org.jboss.naming:org.jnp.interfaces
#filter=false
#include=
#exclude=
#sqlexpression=
#显示指定过滤 Log 时排队的分类列表,取值: error, info, batch, debug, statement,
#commit, rollback, result and resultset are valid values
#excludecategories=info,debug,result,resultset,batch
#excludebinary=false
#executionThreshold=
二、Springboot整合p6spy
1、引入p6spy依赖
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>p6spy</groupId>
<artifactId>p6spy</artifactId>
<version>3.7.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
2、修改数据源连接配置
spring:
datasource:
driver-class-name: com.p6spy.engine.spy.P6SpyDriver
type: com.zaxxer.hikari.HikariDataSource
url: jdbc:p6spy:mysql://127.0.0.1:3306/test?useSSL=false&autoReconnect=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
username: root
password: 123456
3、引入配置文件spy.properties
module.log=com.p6spy.engine.logging.P6LogFactory,com.p6spy.engine.outage.P6OutageFactory
logMessageFormat=com.wjy.log.P6SpyLogger
#logMessageFormat=com.p6spy.engine.spy.appender.SingleLineFormat
#logMessageFormat=com.p6spy.engine.spy.appender.CustomLineFormat
#customLogMessageFormat=%(currentTime) | SQL耗时: %(executionTime) ms | 连接信息: %(category)-%(connectionId) | 执行语句: %(sql)
appender=com.p6spy.engine.spy.appender.StdoutLogger
excludecategories=info,debug,result,batc,resultset
deregisterdrivers=true
dateformat=yyyy-MM-dd HH:mm:ss
driverlist=com.mysql.jdbc.Driver
outagedetection=true
outagedetectioninterval=2
代码结构:
测试:
2019-10-18 09:33:04.714 INFO 4980 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2019-10-18 09:33:04.714 INFO 4980 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2019-10-18 09:33:04.718 INFO 4980 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 4 ms
2019-10-18 09:33:04.744 INFO 4980 --- [nio-8080-exec-1] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting…
Loading class `com.mysql.jdbc.Driver'. This is deprecated. The new driver class is `com.mysql.cj.jdbc.Driver'. The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary.
2019-10-18 09:33:04.915 INFO 4980 --- [nio-8080-exec-1] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
[ 2019-10-18T09:33:04.982 ] --- | took 39ms | insert into users values(null,?,?)|statement | connection 0
insert into users values(null,'wangjunyu',20);
4、格式化日志
logMessageFormat:指定日志格式化类
(1)默认:com.p6spy.engine.spy.appender.SingleLineFormat , 单行输出 不格式化语句
2019-10-18 10:05:49|28|statement|connection 0|insert into users values(null,?,?)|insert into users values(null,'wjy',20)
(2)com.p6spy.engine.spy.appender.CustomLineFormat,需要配合customLogMessageFormat指定格式
# 可用的变量为:
# %(connectionId) connection id
# %(currentTime) 当前时间
# %(executionTime) 执行耗时
# %(category) 执行分组
# %(effectiveSql) 提交的SQL 换行
# %(effectiveSqlSingleLine) 提交的SQL 不换行显示
# %(sql) 执行的真实SQL语句,已替换占位
# %(sqlSingleLine) 执行的真实SQL语句,已替换占位 不换行显示
举例:
customLogMessageFormat=%(currentTime) | SQL耗时: %(executionTime) ms | 连接信息: %(category)-%(connectionId) | 执行语句: %(sql)
2019-10-18 10:09:55 | SQL耗时: 36 ms | 连接信息: statement-0 | 执行语句: insert into users values(null,'wjy',20)
(3)实现MessageFormattingStrategy接口,重写formatMessage方法
package com.wjy.log;
import com.p6spy.engine.spy.appender.MessageFormattingStrategy;
import java.time.LocalDateTime;
public class P6SpyLogger implements MessageFormattingStrategy {
/**
* @Desc: 重写日志格式方法
* now:当前时间
* elapsed:执行耗时
* category:执行分组
* prepared:预编译sql语句
* sql:执行的真实SQL语句,已替换占位
*/
@Override
public String formatMessage(int connectionId, String now, long elapsed, String category, String prepared, String sql) {
return !"".equals(sql.trim())
?
"[ " + LocalDateTime.now() + " ] --- | took " + elapsed + "ms | " + prepared + "|" + category + " | connection " + connectionId + "\n "
+ sql + ";"
: "";
}
}
[ 2019-10-18T09:33:04.982 ] --- | took 39ms | insert into users values(null,?,?)|statement | connection 0
insert into users values(null,'wangjunyu',20);
三、问题:
创建数据库连接时报错:
2019-10-18 07:58:25.265 ERROR 4968 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.jdbc.CannotGetJdbcConnectionException: Failed to obtain JDBC Connection; nested exception is java.sql.SQLNonTransientConnectionException: Could not create connection to database server. Attempted reconnect 3 times. Giving up.] with root cause
com.mysql.cj.exceptions.InvalidConnectionAttributeException: The server time zone value '�й���ʱ��' is unrecognized or represents more than one time zone. You must configure either the server or JDBC driver (via the serverTimezone configuration property) to use a more specifc time zone value if you want to utilize time zone support.
原因和解决措施:指定连接时区:在jdbc连接串后面加上 &serverTimezone=UTC 即可
参考:
手机扫一扫
移动阅读更方便
你可能感兴趣的文章