Fight the Future

Java言語とJVM、そしてJavaエコシステム全般にまつわること

発行したSQLのログを取るp6spyを設定し、ログのフォーマットを設定する

p6spyのバージョンは1.3。


Mavenで設定する場合は、次のとおり。

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

1.3ではロガーにバグがあるらしく、フォーマットやカテゴリを指定しても、その設定でログ出力されない。
なので、ロガーを作成する。

import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import com.p6spy.engine.common.P6SpyProperties;
import com.p6spy.engine.logging.appender.Log4jLogger;

/*
 * p6spyでのデバッグ用ロガー。
 * 
 * <p>
 * p6spyが提供するLog4jのロガーにバグがあるため。
 * 
 */
public class P6spyLog4jLogger extends Log4jLogger {
    
    private static final String LINE_SEPARATOR = System.getProperty("line.separator");
    
    private boolean initialized = false;

    private List<String> categories = new ArrayList<String>();
    private boolean preparedEnabled = false;
    private boolean sqlEnabled = true;
    private String format = "%ct %c: %e[ms] %s";

    public void intialize() {
        if (!initialized) {
            doInitialize();
        }
    }

    protected void doInitialize() {
        P6SpyProperties p6SpyProperties = new P6SpyProperties();
        Properties properties = p6SpyProperties.forceReadProperties();

        String enableCategory = properties.getProperty("P6spyLog4jLogger.enable.category");
        String enableSqlLevel = properties.getProperty("P6spyLog4jLogger.enable.sql.level");
        String format = properties.getProperty("P6spyLog4jLogger.format");

        if (!isNullString(enableCategory)) {
            for (String category : enableCategory.trim().split(",")) {
                this.categories.add(category.trim());
            }
        } else {
            categories.add("statement");
            categories.add("resultset");
            categories.add("commit");
            categories.add("rollback");
        }

        if (!isNullString(enableSqlLevel)) {
            enableSqlLevel = enableSqlLevel.trim();
            if (enableSqlLevel.equals("all")) {
                this.preparedEnabled = true;
                this.sqlEnabled = true;
            } else if (enableSqlLevel.equals("prepare")) {
                this.preparedEnabled = true;
                this.sqlEnabled = false;
            } else {
                this.preparedEnabled = false;
                this.sqlEnabled = true;
            }
        }

        if (!isNullString(format)) {
            this.format = format;
        }
        initialized = true;
    }

    private boolean isNullString(String string) {
        return string == null || string.trim().length() == 0;
    }

    private String replace(int connectionId, String now, long elapsed, String category, String statement) {
        return this.format
                .replace("%ct", String.valueOf(category))
                .replace("%c", String.valueOf(connectionId))
                .replace("%e", new DecimalFormat("00000").format(elapsed))
                .replace("%n", LINE_SEPARATOR)
                .replace("%t", "    ")
                .replace("%s", statement);
    }

    @Override
    public void logSQL(int connectionId, String now, long elapsed, String category, String prepared, String sql) {
        intialize();
        if (categories.contains(category)) {
            if (preparedEnabled) {
                logText(replace(connectionId, now, elapsed, category, prepared));
            }
            if (sqlEnabled) {
                logText(replace(connectionId, now, elapsed, category, sql));
            }
        }
    }
}

このロガーを利用するように、p6spyの設定ファイルに記述する。
設定ファイルは「classpath:spy.properties」。

appender=net.kronos.p6spy.P6spyLog4jLogger
#出力するカテゴリを複数指定
P6spyLog4jLogger.enable.category=statement
#prepareStatmementを出すかどうか。allは全部出す
P6spyLog4jLogger.enable.sql=sql
#ログフォーマット
P6spyLog4jLogger.format=[%ct][%ems]%n%t%s

Log4jの設定ファイルにロガーを記述する。

  <logger name="p6spy" additivity="false">
    <level value="INFO" />
    <appender-ref ref="STDOUT" />
  </logger>

Springで利用する場合は、P6DataSourceでデータソースをラップする。

  <bean id="dataSource" class="com.p6spy.engine.spy.P6DataSource">
    <constructor-arg>
      <ref local="dataSourceTarget" />
    </constructor-arg>
  </bean>

以上でこんなフォーマットでSQLをログに出力する。

[INFO ]11/09/30 00:00:32,397 - [statement][00002ms]
    SELECT
        ...