---
title: Log4JDBCでSQLログを出力する
tags: []
categories: ["Programming", "Java", "net", "sf", "log4jdbc"]
date: 2011-12-20T13:14:22Z
updated: 2011-12-20T13:14:22Z
---
[Log4JDBC][1]はJDBCにSpyをしかけて、実行するSQL等、JDBC API周りのログを出力させるライブラリです。JDBCレベルに仕込んでいるので上位のフレームワークが何であろうと関係ありません。特にJPA(Hibernate)みたいなSQL自動生成系O/Rマッパーを使っていると、JPQLに慣れないうちは実際にどんなSQLが発行されているのか確認しないと、知らないうちにSQLが大量発行されていて危険だったりします。そんなときにSQLログで実際に発行されるSQLを見ておくと安心。
デバッグ時やトラブル解析用に超有用です。
### 使い方
#### pom
Log4JDBCはmavenレポジトリに登録されていないので、ここではちょっとカスタマイズされた[Log4JDBC-remix][2]を使います(基本設定は同じ)。pom.xmlに
org.lazyluke
log4jdbc-remix
0.2.6
を追加。
Log4JDBCはログファサードにSLF4Jを使っており、その先は好きなものを選びます。個々ではLog4Jを選びます(以下のようなdependencyを追加)。
org.slf4j
slf4j-log4j12
1.6.2
runtime
log4j
log4j
1.2.15
javax.mail
mail
javax.jms
jms
com.sun.jdmk
jmxtools
com.sun.jmx
jmxri
runtime
#### JDBCドライバの設定変更
ドライバクラス名とurlを変えます。
ドライバのクラス名は`net.sf.log4jdbc.DriverSpy`固定で。実際のドライバは有名どころであれば自動で認識されます。urlは`jdbc:`の後に`log4jdbc:`を追加します。
プロパティファイルに設定している場合の変更例を以下に
database.username=user
database.password=password
#database.url=jdbc\:mysql\://localhost\:3306/bookstore
database.url=jdbc\:log4jdbc\:mysql\://localhost\:3306/bookstore
#database.driverClassName=com.mysql.jdbc.Driver
database.driverClassName=net.sf.log4jdbc.DriverSpy
#### ログの設定
log4j.xmlに以下のような設定を。
設定するロガーは
- jdbc.sqlonly (SQLのログ出力)
- jdbc.sqltiming (SQLのログ+実行時間出力)
- jdbc.audit (ResultSet以外のAPIのログ出力。膨大)
- jdbc.resultset (ResultSetのAPIログ出力)
- jdbc.connection (Connectionのopen/close時にダンプログ出力。コネクションリーク発見用)
があります。
デフォルトでは90桁で改行される。個人的には一行ログにしたいので、クラスパス直下の`log4jdbc.properties`に以下の設定をする。
log4jdbc.dump.sql.maxlinelength=0
### 実際に出るログ
Hibernateで以下のようなログが出ていた処理で
[2011/12/21 00:07:57] [org.hibernate.SQL] [DEBUG] select book0_.book_id as book1_0_2_, book0_.author as author0_2_, book0_.book_name as book3_0_2_, book0_.category_id as category5_0_2_, book0_.price as price0_2_, book0_.publisher_id as publisher6_0_2_, category1_.category_id as category1_1_0_, category1_.category_name as category2_1_0_, publisher2_.publisher_id as publisher1_5_1_, publisher2_.publisher_name as publisher2_5_1_ from book book0_ left outer join category category1_ on book0_.category_id=category1_.category_id left outer join publisher publisher2_ on book0_.publisher_id=publisher2_.publisher_id where book0_.book_id=?
[2011/12/21 00:07:57] [org.hibernate.SQL] [DEBUG] delete from book where book_id=?
Log4JDBCのログに差し替えると
[2011/12/21 00:14:52] [jdbc.sqltiming] [DEBUG] org.apache.commons.dbcp.DelegatingPreparedStatement.executeQuery(DelegatingPreparedStatement.java:93)
2. select book0_.book_id as book1_0_2_, book0_.author as author0_2_, book0_.book_name as book3_0_2_, book0_.category_id as category5_0_2_, book0_.price as price0_2_, book0_.publisher_id as publisher6_0_2_, category1_.category_id as category1_1_0_, category1_.category_name as category2_1_0_, publisher2_.publisher_id as publisher1_5_1_, publisher2_.publisher_name as publisher2_5_1_ from book book0_ left outer join category category1_ on book0_.category_id=category1_.category_id left outer join publisher publisher2_ on book0_.publisher_id=publisher2_.publisher_id where book0_.book_id=10001 {executed in 2 msec}
[2011/12/21 00:14:52] [jdbc.sqltiming] [DEBUG] org.apache.commons.dbcp.DelegatingStatement.executeBatch(DelegatingStatement.java:297)
2. batching 1 statements:
0: delete from book where book_id=10001 {executed in 2 msec}
な感じに。バインド変数が埋まり、実行時間とどのメソッドから呼ばれたかまでわかります。こうやるとdelete文は実はバッチ実行だったことがわかるなどしてJPAの理解にもつながります(EnityManager#flushを実行した箇所)。。
[1]: http://code.google.com/p/log4jdbc/
[2]: http://code.google.com/p/log4jdbc-remix/