web-dev-qa-db-ja.com

Logger.getLogger(MyClass.class)は、log4jロガーを初期化する最良の方法ですか?

このMkyongチュートリアルでは、ロガーを次のように初期化することを提案しています。

@Controller
public class WelcomeController {

    private static final Logger logger = Logger.getLogger(WelcomeController.class);

   // etc

}

今、おそらくあなたが使用する他のすべてのクラスには、ロガーがあり、ロガーを同じ方法で初期化します。

私の質問は-これはそれを行うための最良の方法ですか?それは...繰り返しのようです。

13
dwjohnston

あなたのコメントは、「冗長」はすべてのクラスでこのコード行を繰り返す必要性を指すと述べています。私の最初の応答は、全体像で、すべてのクラスに2行のコード(変数定義とインポートステートメント)を追加することはそれほど大きな問題ではないということです。特に、動作するクラスに追加するだけでよいため、ロギングを行う必要があります。とはいえ、使用している特定のコード行では、コピーアンドペーストエラーが発生する傾向があります(これについては後で詳しく説明します)。

ただし、代替手段が必要なため、以下にいくつか示します。理由は、それらを使用したい場合と使用しない場合があります。

アプリ全体に単一のロガーを使用します

どのクラスがレポートしているのかを気にしない場合、または必要なすべてのコンテキストをメッセージに入れても構わない場合は、単純なシングルトンロガーが機能します。

_LoggerSingleton.getInstance().debug("MyController is running")
_

私の意見では、ロギングフレームワークの大きな利点の1つは、ログメッセージを別の宛先に送信する場合にのみ、別のロガーインスタンスによってコンテキストが提供されることです。 1行のコードを保存するためだけにそれをあきらめることはしません(まだインポートが必要です)。

さらに、これにより、使用時の冗長性が向上し、キーストロークがはるかに多くなります。

使用時にロガーを作成します

変数を削除するという理由だけで、これを捨てています。私はそれにコメントする必要はないと思います。それはロガーのインスタンスを取得するための私の好ましいテクニックを示していますが。

_Logger.getLogger(getClass()).debug("blah blah blah");
_

Beanポストプロセッサを使用してロガーを挿入します

この例ではSpringを使用しており、Springを使用すると、Bean初期化コードにフックできます。 Beanのloggerメンバー変数を検査し、見つかったときにLoggerインスタンスを作成するポストプロセッサーを作成できます。

このようなポストプロセッサは数十行のコードにすぎませんが、これはアプリケーションのもう1つの重要な部分であり、別の潜在的なバグの原因になります。私はそれらをできるだけ少なくすることを好みます。

ミックスインを使用してください

ScalaとGroovyはtraitsを提供し、動作をカプセル化できます。典型的なScalaパターンはLoggingトレイトを作成し、ロギングが必要なクラスに追加することです:

_class MyController with Logging
_

残念ながら、これは言語を切り替える必要があることを意味します。 Java 8を使用している場合を除き、この場合、「デフォルトのメソッド」でLoggingインターフェースを作成できます。

_public interface Logging {
    default Logger getLogger() {
        return Logger.getLogger(getClass());
    } 
}
_

今、あなたのクラスコード内で、あなたは単に使うことができます

_getLogger().debug("blah blah blah");
_

これは簡単ですが、いくつかの欠点があります。 1つには、すべてのインターフェースメソッドがパブリックであるため、それを使用するすべてのクラスのインターフェースを汚染します。特にインターフェイス/実装の分離に従う場合は、Springによってインスタンス化および挿入されるクラスにのみ使用する場合は、おそらくそれほど悪くはありません。

より大きな問題は、すべての呼び出しで実際のロガーインスタンスを検索する必要があることです。高速ですが不要です。

そして、まだimportステートメントが必要です。

ロガーをスーパークラスに移動します

繰り返します。繰り返しロガー定義は冗長ではありませんが、そうであれば、これを排除するための最良のアプローチだと思います。

_public abstract class AbstractController {
    protected Logger logger = Logger.getLogger(getClass());
}
_

これで、コントローラークラスはAbstractControllerから継承され、logger変数にアクセスできます。 _@Controller_アノテーションを具象クラスに配置する必要があることに注意してください。

一部の人々はこれを継承の倒錯だと思うでしょう。クラスをAbstractControllerではなくAbstractProjectClassと命名することで、それらを軟化させようとしました。 is-a関係があるかどうかを自分で決めることができます。

他の人々は、静的変数ではなくインスタンス変数の使用に反対します。 [〜#〜] imo [〜#〜] 静的ロガーは、クラス名を明示的に参照する必要があるため、コピーアンドペーストエラーが発生しやすくなります。 getClass()は、ロガーが常に正しいことを保証します。

16
kdgregory

@kdgregoryによって提供される答えを拡張するために、Groovyは@Slf4jgroovy.util.logging.Slf4j)クラスでAST変換を実行して、指定されていない場合はデフォルトで変数名logと想定するロガーを使用して変換するアノテーションとして使用できます。

0