JDBCのロギング
データベースを使用したアプリケーションを作成していると、実行されているSQLや、その実行時間を把握したいことがあります。また、JDBCのコネクションリークなどのデバッグ時にはJDBC呼び出しをトレースしたくなります。
実行されているSQLはソースコードからも、ある程度把握可能ですが、動的なSQL文などを多用している場合などは少々面倒です。
log4jdbc
log4jdbc
を使用すると、既存ソースを変更せずにSQL文やJDBC呼び出し、実行時間などをログに収集することが可能になります。同様のJDBCドライバにはP6SPY
があります。
log4jdbcのセットアップ
以下は Tomcat5.5 で動作している webアプリケーションにおける log4jdbc のセットアップ手順です。データベースは PostgreSQL を使用しています。
バイナリのダウンロード
log4jdbcのバイナリと依存ライブラリのSLF4J
をダウンロードします。この例では Logback
を使用しますので、そちらもダウンロードします。
この例では
- log4jdbc-1.1alpha2
- slf4j-1.4.3
- logback-0.9.8
のバージョンを使用しました。
jarファイルのコピー
${catalina.home}/common/lib に以下のjarファイルをコピーします。
log4jdbc3-1.1alpha2.jar
slf4j-api-1.4.3.jar
logback-core-0.9.8.jar
logback-classic-0.9.8.jar
JNDIデータソースの設定変更
データソースを本来のJDBCドライバ(この例ではPostgreSQL)から log4jdbcに変更します。以下は変更前の設定です。
<Resource name="jdbc/postgres" auth="Container"
type="javax.sql.DataSource"
driverClassName="org.postgresql.Driver"
url="jdbc:postgresql:testdb"
username="testdb"
password="testdb"
maxActive="10"
maxIdle="3"
maxWait="-1"
defaultAutoCommit="false" />
以下は変更後の設定です。
<Resource name="jdbc/postgres" auth="Container"
type="javax.sql.DataSource"
driverClassName="net.sf.log4jdbc.DriverSpy"
url="jdbc:log4jdbc:postgresql:testdb"
username="testdb"
password="testdb"
maxActive="10"
maxIdle="3"
maxWait="-1"
defaultAutoCommit="false" />
上記のように driverClassNameが "net.sf.log4jdbc.DriverSpy"に、urlが本来のurlの先頭に "jdbc:log4" を付加したものになります。
Logbackの設定
${webapp.root}/WEB-INF/classes/logback.xml ファイルを作成し、ログ出力の設定を行います。
<configuration>
<appender name="FILE"
class="ch.qos.logback.core.FileAppender">
<file>${catalina.base}/logs/jdbc.log</file>
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>
%date %logger %msg%n
</Pattern>
</layout>
</appender>
<logger name="jdbc.sqltiming">
<level value="info" />
</logger>
<root>
<level value="off" />
<appender-ref ref="FILE" />
</root>
</configuration>
以上で設定は完了です。log4jdbcのログレベルやカテゴリの詳細は log4jdbc
のサイトで確認してください。Logbackの設定は Logbackマニュアル
を確認してください。
ログ出力の確認
以下のテストプログラムを実行し、ログ出力を確認します。(エラー処理は無視)
Context c = new InitialContext();
DataSource ds = (DataSource)c.lookup("java:comp/env/jdbc/postgres");
Connection conn = ds.getConnection();
PreparedStatement pstmt = conn.prepareStatement(
"select * from emp");
ResultSet rs = pstmt.executeQuery();
以下のようにログが出力されます。実行時間(この例では 2msec)も取得できています。
2007-10-07 09:20:39,647 jdbc.sqltiming select * from emp {executed in 2 msec}
バインド変数を使ったSQL
log4jdbcはバインド変数を使ったSQLを自動的に展開してロギングします。以下はプログラム例です。
PreparedStatement pstmt = conn.prepareStatement(
"select * from emp where empno = ?");
pstmt.setInt(1, 7369);
ResultSet rs = pstmt.executeQuery();
ログは以下のようになります。
2007-10-07 09:25:19,987 jdbc.sqltiming select * from emp where empno = 7369 {executed in 2 msec}
この機能のおかげで、収集したSQL文から実行計画を取得する作業(Oracleのexplain planやPostgreSQLのexplain analyzeなど)が非常に簡単になります。
JDBC呼び出しのトレース
log4jdbcには jdbc.sqlonly, jdbc.sqltiming, jdbc.audit, jdbc.resultsetのログカテゴリがあり、それぞれのログレベルを調整することで様々なデータを収集可能になります。詳細はlog4jdbc
のサイトで確認してください。
以下はJDBC呼び出しをトレースするための logback.xml の設定例です。
<logger name="jdbc.audit">
<level value="info" />
</logger>
この設定により、以下のようなログを取得可能になります。
2007-10-07 09:49:04,540 jdbc.audit 2. Connection.getAutoCommit() returned true
2007-10-07 09:49:04,541 jdbc.audit 2. Connection.setAutoCommit(false) returned
2007-10-07 09:49:04,541 jdbc.audit 2. Connection.prepareStatement(select * from emp where empno = ?)
returned net.sf.log4jdbc.PreparedStatementSpy@1318b
2007-10-07 09:49:04,541 jdbc.audit 2. PreparedStatement.setInt(1, 7369) returned
2007-10-07 09:49:04,544 jdbc.audit 2. PreparedStatement.executeQuery() returned
net.sf.log4jdbc.ResultSetSpy@5bb966
2007-10-07 09:49:04,544 jdbc.audit 2. PreparedStatement.close() returned
2007-10-07 09:49:04,544 jdbc.audit 2. Connection.isClosed() returned false
2007-10-07 09:49:04,544 jdbc.audit 2. Connection.getAutoCommit() returned false
2007-10-07 09:49:04,544 jdbc.audit 2. Connection.isReadOnly() returned false
2007-10-07 09:49:04,545 jdbc.audit 2. Connection.rollback() returned
2007-10-07 09:49:04,545 jdbc.audit 2. Connection.clearWarnings() returned
2007-10-07 09:49:04,545 jdbc.audit 2. Connection.getAutoCommit() returned false
2007-10-07 09:49:04,546 jdbc.audit 2. Connection.setAutoCommit(true) returned