ActiveObjects にログを吐かせる
XML と完全にお別れしたい諸氏にとって(dicon だって XML だ)、Wicket + Guice + ActiveObjects という組み合わせは魅力的です。
しかしながら、そのままでは懇切丁寧なログを吐く S2Dao や S2JDBC に比べて ActiveObjects は物静かで中で何をやっているのかわかりません。
とはいえ、ActiveObjects も S2Dao や S2JDBC には遠く及びませんが、ログをとってないわけではないのです。
ActiveObjects は、Java Logging API という J2SE 1.4 が出た当時は「お?」と注目されつつも現在は見る影もない(笑)ロガーを使っています。ところが、どういうわけか EntityManager が生成されたときにログレベルを OFF にしてしまっているようなのです。
ということで、以下のようにすれば ActiveObjects が投げた SQL 文がロギングされるようになります。エンティティマネージャを生成した後に設定するのがミソです。
// エンティティマネージャの生成 EntityManager manager = new EntityManager("jdbc:mysql://127.0.0.1:3306/db", "username", "password"); // ActiveObjects のロガーを取得 Logger logger = Logger.getLogger("net.java.ao"); // ログレベルの設定 logger.setLevel(Level.FINE);
で、ここまでは ActiveObjects のドキュメントにも書いてあることなのですが、Wicket と一緒に使っているのならば SLF4J でログを吐きたいというのが人情。ちょっと無理やりくさいですが、以下のようなユーティリティクラスとハンドラを書けばいけそうです。
package example.logging; import java.util.logging.Level; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * ロギングに関するユーティリティ・クラスです。 * * @author itoasuka */ public final class LoggingUtils { /** 非常に詳細なトレースメッセージを示すログレベルです。 */ public static final int FINEST = 300; /** かなり詳細なトレースメッセージを示すログレベルです。 */ public static final int FINER = 400; /** トレース情報を提供するメッセージレベルです。 */ public static final int FINE = 500; /** 静的な構成メッセージのメッセージレベルです。 */ public static final int CONFIG = 700; /** メッセージを情報として提供するメッセージレベルです。 */ public static final int INFO = 800; /** 潜在的な問題を示すメッセージレベルです。 */ public static final int WARNING = 900; /** 重大な障害を示すメッセージレベルです。 */ public static final int SEVERE = 1000; /** * 指定したロガー名を持つ SLF4J のロガーのログレベルから、Java Logging API のログレベルを取得します。 * * @param name * ロガー名 * @return ログレベル */ public static Level getLevel(String name) { Logger logger = LoggerFactory.getLogger(name); if (logger.isTraceEnabled()) { return Level.ALL; } if (logger.isDebugEnabled()) { return Level.FINE; } if (logger.isInfoEnabled()) { return Level.CONFIG; } if (logger.isWarnEnabled()) { return Level.WARNING; } if (logger.isErrorEnabled()) { return Level.SEVERE; } return Level.OFF; } private LoggingUtils() { // 何もしない } }
package example.logging; import java.util.logging.Handler; import java.util.logging.LogRecord; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Java Logging API によるロギングを SLF4J に食わせるためのハンドラです。 * * @author itoasuka */ public class Slf4jHandler extends Handler { private Logger logger; /** * {@inheritDoc} */ @Override public void close() { // 何もしない } /** * {@inheritDoc} */ @Override public void flush() { // 何もしない } /** * {@inheritDoc} */ @Override public void publish(LogRecord record) { int level = record.getLevel().intValue(); String message = StringUtils.defaultString(record.getMessage()); Throwable throwable = record.getThrown(); Logger logger = LoggerFactory.getLogger(record.getLoggerName()); switch (level) { case LoggingUtils.FINEST: case LoggingUtils.FINER: if (throwable == null) { logger.trace(message); } else { logger.trace(message, throwable); } break; case LoggingUtils.FINE: if (throwable == null) { logger.debug(message); } else { logger.debug(message, throwable); } break; case LoggingUtils.CONFIG: case LoggingUtils.INFO: if (throwable == null) { logger.info(message); } else { logger.info(message, throwable); } break; case LoggingUtils.WARNING: if (throwable == null) { logger.warn(message); } else { logger.warn(message, throwable); } break; case LoggingUtils.SEVERE: default: if (throwable == null) { logger.error(message); } else { logger.error(message, throwable); } } } }
それで、これを以下のように組み込みます。
// エンティティマネージャの生成 EntityManager manager = new EntityManager("jdbc:mysql://127.0.0.1:3306/db", "username", "password"); // ActiveObjects のロガーを取得 Logger logger = Logger.getLogger("net.java.ao"); // ログレベルの設定 logger.setLevel(LoggingUtils.getLevel("net.java.ao")); // ハンドラの追加 logger.addHandler(new Slf4jHandler()); // 親ロガーのハンドラは使わない logger.setUseParentHandlers(false);