概要
SLF4JのFAQにも記載されている、Loggerインスタンス作成時のTipsです。
Loggerの宣言に関するあるあるネタと解決策です。
【問題】間違ったLogger名
Loggerに関するありがちなバグとして、Logger名の間違いがあります。
これはLoggerの宣言を別のソースからコピペした場合に発生しがちです。
public class MyClass {
private static final Logger LOGGER = LoggerFactory.getLogger(User.class); // バグ! Userクラスのコードからコピペしたまま
//...
}
【解決】MethodHandleを利用する
Java7から導入されたMethodHandle1を利用すると、呼び出し元のClassオブジェクトをstaticに取得することができます。
(MethodHandlesクラスのstaticメソッド^mhsを使用します ← MethodHandleクラス1ではないので要注意)
Class<?> clazz = MethodHandles.lookup().lookupClass();
これを利用したLoggerの生成のイディオムが、SLF4JのFAQ2に掲載されています。
これならコピペしてそのまま(引数のLogger名を書き換えずに)使うことができます。
import java.lang.invoke.MethodHandles;
public class MyClass {
private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
//...
}
自前のLoggerクラスの場合
自前のLoggerクラスであれば、以下のようにファクトリメソッドの引数の型を限定することで誤ったLogger名の指定を防止することができます。
自前のLoggerクラスについては以前に書きました。 → 自作LoggerではLocationAwareLoggerを使おう
import java.lang.invoke.MethodHandles;
public class MyLocationAwareLogger {
// (今回の話題に無関係な箇所は省略)
// コンストラクタは非公開
private MyLocationAwareLogger(org.slf4j.spi.LocationAwareLogger logger) {
this.logger = logger;
}
// ファクトリメソッド(このメソッドのみ公開)
public static MyLocationAwareLogger getLogger(MethodHandles.Lookup lookup) {
return getLogger(lookup.lookupClass());
}
// 上記以外のファクトリメソッドは非公開
private static MyLocationAwareLogger getLogger(Class<?> clazz) {
org.slf4j.spi.LocationAwareLogger logger = (org.slf4j.spi.LocationAwareLogger) LoggerFactory.getLogger(clazz);
return new MyLocationAwareLogger(logger);
}
// (今回の話題に無関係な箇所は省略)
}
private static final MyLocationAwareLogger LOGGER = MyLocationAwareLogger.getLogger(java.lang.invoke.MethodHandles.lookup());
// 以下はコンパイルエラーとなる
// private static final MyLocationAwareLogger LOGGER_NG1 = MyLocationAwareLogger.getLogger(User.class);
// private static final MyLocationAwareLogger LOGGER_NG2 = MyLocationAwareLogger.getLogger("User");
さいごに
たかがLogger名ですが、間違った値が指定されていると不具合調査に甚大な悪影響を与えます。
Logger名に騙されてバグと無関係な箇所を延々と調査する羽目に陥った事がある方、多いのではないでしょうか。
簡単に導入できるTipsなので、是非実践してみてください。