用 p6spy 来观察 Java 程序中执行的所有 SQL 语句(三. 定制输出)

既然提到 p6spy 的输出,那就有必要说明一下 p6spy 输出日志的格式了

03-16-09 15:12:06:656|16|4|statement|SELECT * FROM OM_CUSTOMERS  WHERE CUSTOMER_ID=? ORDER BY CUSTOMER_ID ASC|SELECT * FROM OM_CUSTOMERS  WHERE CUSTOMER_ID=2194 ORDER BY CUSTOMER_ID ASC
03-16-09 15:12:06:671|15|3|statement|SELECT * FROM OM_ORDER_TYPE WHERE TYPE_ID=?|SELECT * FROM OM_ORDER_TYPE WHERE TYPE_ID=25
03-16-09 15:12:06:687|16|1|statement|select * from sys_lookups where lookup_type=?  and lookup_code=? |select * from sys_lookups where lookup_type='OM_ORDER_STATUS'  and lookup_code='70'
03-16-09 15:12:06:812|-1||resultset|select * from sys_lookups where lookup_type='OM_ORDER_STATUS'  and lookup_code='70' |meaning = 已安排生产

再看 p6spy 官方文档的关于日志文件(控制台输出/Log4J也一样)格式的说明 --http://www.p6spy.com/documentation/other.htm#log。日志格式是:

current time|execution time|category|statement SQL string|effective SQL string

current time -- 当前时间
execution time -- 执行时长,包括执行 SQL 和处理结果集的时间(可以参考来调优)
category  -- 语句分类,statement、resultset 等
statement SQL string -- 查询语句。可能是 prepared statement,表现为 select * from table1 where c1=?,问号参数形式
effective SQL string -- 代入参数值的查询语句,如 select * from from table1 where c1=7

看到上面的日志输出,我们可能会有如下需求:

1) 对于 category 为 resultset 的输出你可能并不关心,查询了什么字段,取哪个字段或许早心理有数。上面例子中的 resultset 是 select *,然而只取了 meaning 一个字段,这是不推荐的。(不输出 resultset 语句)
2) 你可能不想被那些带问号参数的 prepared statement 干扰,而想直接看最终被执行的语句。(statement SQL string 不显示)
3) 你可能会想把控制台或日志文件中的一连串几个语句直接复制,贴到数据库客户端就能执行。(只输出 effective SQL string,并以分号隔开)

对于第一个要求,我们可以利用 p6spy 的显示过滤功能,可在 p6spy.properties 中配置。p6spy 有 resultset 这样一个 category,却未完善对其的过滤控制,为此我们需要修改源代码 com.p6spy.engine.spy.P6ResultSet.java,找到 152 行的

P6LogQuery.log("resultset", query, buffer.toString());

把其改为

1
2
3
//P6LogQuery.log("resultset", query, buffer.toString());
P6Connection p6connection = (P6Connection) this .statement.getConnection();
P6LogQuery.logElapsed(p6connection.getId(), System.currentTimeMillis(), "resultset" , preparedQuery, query);

编译(最好用 1.4 或 1.5 的JDK 来编译,因为 JDK 1.6 的 ResultSet 多了些要实现的方法,修改起来要麻烦很多),把该类替换掉原来 p6spy.jar 中的相应 class。编译好的 FormattedLogger 在附件 changed_p6spy_classes.rar 中有,是 P6ResultSet.class 文件。

然后修改 p6spy.properties 文件,找到

excludecategories=info,debug,result,batch

加上对 resultset 的排除,改为

excludecategories=info,debug,result,batch,resultset

这样,在输出的语句中就没有眼花缭乱的 resultset 语句了,清爽了许多。

对于第二个要求,我们还要修改的是源代码 com.p6spy.engine.logging.appender.FormattedLogger,在 72 行找到

String logEntry = now + "|"+ elapsed + "|"+(connectionId==-1 ? "" : String.valueOf(connectionId))+"|"+category+"|"+prepared+"|"+sql;

我们不希望输出 prepared,所以把它改为

String logEntry = now + "|"+ elapsed + "|"+(connectionId==-1 ? "" : String.valueOf(connectionId))+"|"+category+"|"+sql;

编译,替换掉原 p6spy.jar 中相应类,编译好的 FormattedLogger 是附件 changed_p6spy_classes.rar 中有,是 FormattedLogger.class 文件。

要满足第三个条件,还是修改 com.p6spy.engine.logging.appender.FormattedLogger 的同一行代码,改为

String logEntry = sql + ";";

编译,替换掉原 p6spy.jar 中相应类,编译好的 FormattedLogger 是附件 changed_p6spy_classes.rar 中有,是 FormattedLogger1.class 文件,替换的时候请更名。那么由它指示输出的 SQL 语句就是以分号分隔的一条条了,基本是连续几条一起复制出来,放到 SQL 客户端工具中就能执行了。当然字符串的参数还是要稍加处理的。

注意 Log4j 可能会在其中捣乱,把 log4j-x.x.x.jar 也放在 $TOMCAT_HOME/common/lib 目录下,上面对 FormattedLogger.class 不会起作用,如果没有 log4j-x.x.x.jar 也会报错--类找不到。我把它放在应用的 WEB-INF/lib 中,让应用能加载,但 p6spy 又不能使用它,因为不是同一个类加载器加载的。

转自:http://unmi.cc/p6spy-view-custom-java-executing-sql/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值