Java
log
slf4j

コピペによるLogger名の間違いを防止する

概要

SLF4JのFAQにも記載されている、Loggerインスタンス作成時のTipsです。
Loggerの宣言に関するあるあるネタと解決策です。

【問題】間違った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メソッド2(の戻り値のメソッド3)を使用します ← MethodHandleクラス1ではないので要注意)

Classオブジェクトの取得
Class<?> clazz = MethodHandles.lookup().lookupClass();

これを利用したLoggerの生成のイディオムが、SLF4JのFAQ4に掲載されています。
これならコピペしてそのまま(引数のLogger名を書き換えずに)使うことができます。

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を使おう

ファクトリメソッドの引数をMethodHandles.Lookupだけにする
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);
  }

  // (今回の話題に無関係な箇所は省略)
}
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なので、是非実践してみてください。