代码改变世界

能够使开发和调试更为方便的java日志框架

2013-05-27 17:49  御云  阅读(1898)  评论(0编辑  收藏  举报

在常规项目的开发中可能最容易出问题的地方就在于对数据库的处理了。在大部分的环境下,我们对数据库的操作都是使用流行的框架,比如Hibernate、ibatis等。由于各种原因,我们有时会想知道在这些框架下实际执行的sql究竟是什么。Hibernate可以在配置文件中打开show sql的功能,ibatis则可以在log4j的配置文件中配置sql语句的输出,但这些输出是类似这样的insert … ?  ?  ?语句,并不是一个完整可以运行的SQL,要想知道完整的sql需要手动把参数补齐,如果要调试这样的sql无疑是比较痛苦的。

本文主要介绍几个可以直接显示完整的sql日志框架,希望对大家有所帮助

1.p6spy

p6spy 是针对数据库访问操作的动态监测框架,它使得数据库数据可无缝截取和操纵,而不必对现有应用程序的代码作任何修改。P6Spy 分发包包括P6Log,它是一个可记录任何 Java 应用程序的所有JDBC事务的应用程序。其配置完成使用时,可以进行数据访问性能的监测。

p6spy的官方主页(www.p6spy.com)不知何故无法打开,但可以使用maven引入p6spy:

<dependency>
    <groupId>p6spy</groupId>
    <artifactId>p6spy</artifactId>
    <version>1.3</version>
</dependency>

但使用maven引入的p6spy并没有包含p6spy.properties,而这个配置文件是p6spy必须的。

以下是一个p6psy.properties文件的内容(去除了大部分注释,更多内容请下载p6psy-install.zip或参考http://wenku.baidu.com/view/0897a93067ec102de2bd89d4.html):

module.log=com.p6spy.engine.logging.P6LogFactory
#module.outage=com.p6spy.engine.outage.P6OutageFactory

realdriver=com.mysql.jdbc.Driver
deregisterdrivers=false
executionthreshold=
outagedetection=false
outagedetectioninterval=

# filter what is logged
filter=false

# comma separated list of tables to include when filtering
include     = 
# comma separated list of tables to exclude when filtering
exclude     =

# sql expression to evaluate if using regex filtering
sqlexpression = 

# turn on tracing
autoflush   = true

# sets the date format using Java's SimpleDateFormat routine
dateformat=

#list of categories to explicitly include 
includecategories=

#list of categories to exclude: error, info, batch, debug, statement,
#commit, rollback and result are valid values
excludecategories=info,debug,result,batch

stringmatcher=

# prints a stack trace for every statement logged
stacktrace=false
# if stacktrace=true, specifies the stack trace to print
stacktraceclass=

# determines if property file should be reloaded
reloadproperties=false
# determines how often should be reloaded in seconds
reloadpropertiesinterval=60

#if=true then url must be prefixed with p6spy:
useprefix=false

#specifies the appender to use for logging
appender=com.p6spy.engine.logging.appender.Log4jLogger
#appender=com.p6spy.engine.logging.appender.StdoutLogger
#appender=com.p6spy.engine.logging.appender.FileLogger

# name of logfile to use, note Windows users should make sure to use forward slashes in their pathname (e:/test/spy.log) (used for file logger only)
#logfile = r:/logs/log/spy.log

# append to  the p6spy log file.  if this is set to false the
# log file is truncated every time.  (file logger only)
append=true

#The following are for log4j logging only
log4j.appender.STDOUT=org.apache.log4j.ConsoleAppender
log4j.appender.STDOUT.layout=org.apache.log4j.PatternLayout
log4j.appender.STDOUT.layout.ConversionPattern=p6spy - %m%n

log4j.logger.p6spy=INFO,STDOUT

在这个配置文件中比较重要的配置有:jdbc驱动(realdriver)、日志记录方式(appender)等。

使用p6spy的步骤:

1.将p6spy的jar放入classpath;

2.将p6spy.properties文件放入classpath;

3.修改项目的配置文件:可以直接将项目中原有的jdbc驱动改成com.p6spy.engine.spy.P6SpyDriver,而当项目是使用spring配置时也可以如下修改:(将原dataSource改名,新的dataSource的实现类是P6DataSouce且构造参数指向原dataSource)

如果提示“Warning: driver com.mysql.jdbc.Driver is a real driver in spy.properties, but it has been loaded before p6spy.  p6spy will not wrap these connections.  Either prevent the driver from loading, or try setting 'deregisterdrivers' to true in spy.properties”,把p6spy.properties文件中的deregisterdrivers设置为true即可。

按以上步骤设置后,p6spy已经能记录真实的sql了。不过在实现中发现p6spy的记录还有一些缺点,比如当sql是查询语句且有返回结果集时,结果集的每条记录都会输出,而spy.properties文件中的excludecategories值的有效选项中却没有resultset,这意味着无法能过配置的方式屏蔽这些信息。针对这个问题,网上有通过修改源代码的方式来解决的,如http://blog.csdn.net/ddwcyl/article/details/1766435。在此我给出一个无需修改原有代码的解决方案:新建一个类,如下:

public class P6LogFactory extends com.p6spy.engine.logging.P6LogFactory {
    @Override
    public ResultSet getResultSet(ResultSet real, P6Statement statement, String preparedQuery, String query)
            throws SQLException {
        return real;
    }

}

然后将spy.properties文件中的module.log值改为这个类即可。

 

2.log4jdbc

p6spy虽然已经能实现记录真实sql的功能,但是有一些缺点,且从2003年至今没有更新了。

log4jdbc是p6spy之后流行的一个记录真实sql的日志框架。官方主页是https://code.google.com/p/log4jdbc/,具有以下特点:

  • 完全支持JDBC3和JDBC4。
  • 配置简单,在大多数情况下,只需要将jdbc驱动类改成net.sf.log4jdbc.DriverSpy,同时将jdbc:log4添加到现有的jdbc url之前,最后配置日志记录的种类即可。
  • 将prepared statements中的绑定参数自动插入到对应的位置。在大多数情况下极大改善了可读性及调试工作。
  • SQL的耗时信息能被获取从而帮助判断哪些语句执行得过慢,同时这些信息可以被工具识别得到一个关于慢SQL的报表。
  • SQL连接信息也可以获取从而帮助诊断关于连接池或线程的问题。
  • 兼容任何jdbc驱动,需要JDK1.4及以上与slf4j1.x.
  • 开源软件,使用Apache 2.0 license

使用log4jdbc的步骤:

1.决定使用哪个版本的jar包

  • 如果使用 JDK 1.4 or 1.5, 应该使用 JDBC 3 版本的 log4jdbc即log4jdbc3-1.2.jar.
  • 如果使用 JDK 1.6 or 1.7, 应该使用 JDBC 4 版本的 log4jdbc即log4jdbc4-1.2.jar (即使实际使用的 JDBC 驱动是 JDBC 3 的甚至更老).

将对应的jar包放入classpath。

2.选择具体的java日志系统

log4jdbc使用SLF4J日志框架,支持大多数的日志系统。如果使用log4j来记录日志,则在maven的pom文件中如下配置即可:

        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.5</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.5</version>
        </dependency>        

3.将项目的配置文件中的jdbc驱动类改成net.sf.log4jdbc.DriverSpy

log4jdbc在大多数情况下会自动侦测到实际应该使用的驱动,如果需要特别指定实际使用的jdbc驱动,在系统属性中设置变量log4jdbc.drivers的值为指定的驱动。

4.将jdbc:log4添加到现有的jdbc url之前

例如原来的jdbc url是

jdbc:derby://localhost:1527//db-derby-10.2.2.0-bin/databases/MyDatabase

,则应该改成

jdbc:log4jdbc:derby://localhost:1527//db-derby-10.2.2.0-bin/databases/MyDatabase

5.配置日志记录的种类

log4jdbc用以下几个可以配置的日志种类:

jdbc.sqlonly:仅记录sql

jdbc.sqltiming:记录sql以及耗时信息

jdbc.audit:记录除了resultset之外的所有jdbc调用信息,会产生大量的记录,有利于调试跟踪具体的jdbc问题

jdbc.resultset:会产生更多的记录信息,因为记录了resultset的信息

jdbc.connection:记录连接打开、关闭等信息,有利于调试数据库连接相关问题

以上日志种类都可以设置为DEBUG, INFO 或 ERROR 级别.当设置为FATAL或OFF时,意味关闭记录。

以下是一个采用log4j作为具体日志系统的典型配置:

log4j.logger.jdbc.sqlonly=OFF
log4j.logger.jdbc.sqltiming=INFO  
log4j.logger.jdbc.audit=OFF
log4j.logger.jdbc.resultset=OFF
log4j.logger.jdbc.connection=OFF

效果

3.log4jdbc-remix

log4jdbc-remix是扩展自log4jdbc的日志框架,增加了对resultset以表格方式呈现的处理。

项目主页:https://code.google.com/p/log4jdbc-remix/

可以使用maven引入log4jdbc-remix:

<dependency>
  <groupId>org.lazyluke</groupId>
  <artifactId>log4jdbc-remix</artifactId>
  <version>0.2.7</version>
</dependency>

很奇怪在maven中央仓库中没有找到log4jdbc,却能找到它的扩展log4jdbc-remix。在maven大行其道的今天,这大大有利于log4jdbc-remix的推广,因为log4jdbc-remix包括了log4jdbc的所有功能。

log4jdbc-remix同样需要将jdbc:log4添加到现有的jdbc url之前,而日志配置相比log4jdbc只是多了jdbc.resultsettable(表格方式记录resultset)。

以下是一个采用log4j作为具体日志系统的典型配置:

log4j.logger.jdbc.sqlonly=OFF  
log4j.logger.jdbc.sqltiming=INFO  
log4j.logger.jdbc.audit=OFF  
log4j.logger.jdbc.resultset=OFF  
log4j.logger.jdbc.connection=OFF 
log4j.logger.jdbc.resultsettable=INFO

效果

缺点

在使用mybatis查询含blog字段的表时,log4jdbc-remix会抛异常。(sql在配置文件中,很奇怪sql是注解的话不会发生异常)

将log4j.logger.jdbc.resultsettable设为OFF,可绕过该异常。这样看来当表含有blog字段时,还是把log4jdbc-remix当log4jdbc使吧。