web-dev-qa-db-ja.com

Javaでデバッグ出力を処理する正しい方法は何ですか?

現在のJavaプロジェクトがどんどん大きくなっているので、デバッグ出力をコードのいくつかのポイントに挿入する必要性も同様に高まっています。

この機能を適切に有効または無効にするには、テストセッションの開始または終了に応じて、通常private static final boolean DEBUG = false私のテストが検査しているクラスの最初で、(たとえば)このように簡単に使用します。

public MyClass {
  private static final boolean DEBUG = false;

  ... some code ...

  public void myMethod(String s) {
    if (DEBUG) {
      System.out.println(s);
    }
  }
}

など。

しかし、もちろんそれはうまくいくので、私を喜ばせるわけではありませんが、2つだけを見つめていなければ、DEBUGをtrueに設定するクラスが多すぎる可能性があります。

逆に、出力されるテキストの量が膨大になる可能性があるので、(私は-私が思うに-他の多くの人のように)アプリケーション全体をデバッグモードにしたくありません。

それで、そのような状況をアーキテクチャ的に処理する正しい方法はありますか、または最も正しい方法はDEBUGクラスメンバーを使用することですか?

32
Federico Zancan

ロギングフレームワーク、おそらくロギングファサードフレームワークを見たいと思います。

複数のロギングフレームワークが存在し、多くの場合、機能が重複しているため、時間の経過とともに、多くのものが共通のAPIに依存するように進化したり、ファサードフレームワークを介して使用を抽象化したり、それらを所定の場所に入れ替えたりできるようになりました必要に応じて。

フレームワーク

一部のロギングフレームワーク

  • Java Logging Framework (JDKの一部)、
  • Apache Log4J (少し古くなっていますが、まだ強力でアクティブに維持されています)、
  • LogBackLog4J の作成者の1人により、Log4Jよりも新しいアプローチを提供するために作成されました)。

一部のロギングファサード

  • SLF4JLogBack の作成者によるが、他のフレームワーク用のアダプターがある)、
  • Apache Commons Logging (ただし、日付が少し変更されています)。

使用法

基本的な例

これらのフレームワークのほとんどは、次のような形式で記述できます(ここでは_slf4j-api_および_logback-core_を使用しています)。

_package chapters.introduction;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

// copied from: http://www.slf4j.org/manual.html
public class HelloWorld {

  public static void main(String[] args) {
    final Logger logger = LoggerFactory.getLogger(HelloWorld.class);

    logger.debug("Hello world, I'm a DEBUG level message");
    logger.info("Hello world, I'm an INFO level message");
    logger.warn("Hello world, I'm a WARNING level message");
    logger.error("Hello world, I'm an ERROR level message");
  }
}
_

現在のクラスを使用して専用ロガーを作成することに注意してください。これにより、SLF4J/LogBackが出力をフォーマットし、ロギングメッセージの送信元を示すことができます。

SLF4Jマニュアル に記載されているように、クラスの一般的な使用パターンは通常次のとおりです。

_import org.slf4j.Logger;  
import org.slf4j.LoggerFactory;  

public class MyClass {

    final Logger logger = LoggerFactory.getLogger(MyCLASS.class);

    public void doSomething() {
        // some code here
        logger.debug("this is useful");

        if (isSomeConditionTrue()) {
            logger.info("I entered by conditional block!");
        }
    }
}
_

しかし実際には、ロガーを次の形式で宣言するのがより一般的です。

_private static final Logger LOGGER = LoggerFactory.getLogger(MyClass.class);
_

これにより、ロガーを静的メソッド内からも使用でき、クラスのすべてのインスタンス間で共有されます。これはあなたの好みの形である可能性が高いです。ただし、コメントでBrendan Longが述べたように、必ず 意味を理解する を決定し、それに応じて決定する必要があります(これは、これらのイディオムに従うすべてのロギングフレームワークに適用されます)。

ロガーをインスタンス化する方法は他にもあります。たとえば、文字列パラメーターを使用して名前付きロガーを作成します。

_Logger logger = LoggerFactory.getLogger("MyModuleName");
_

デバッグレベル

デバッグレベルはフレームワークごとに異なりますが、一般的なものは次のとおりです(重要度の順に、良性からバットバットまで、そしておそらく非常に一般的なものから非常にまれなものまで)。

  • TRACE 非常に詳細な情報。ログにのみ書き込む必要があります。チェックポイントでのプログラムのフローを追跡するためにのみ使用されます。

  • DEBUG 詳細情報。ログのみに書き込む必要があります。

  • INFO 注目すべきランタイムイベント。コンソールにすぐに表示されるはずなので、控えめに使用してください。

  • WARNING 実行時の異常と回復可能なエラー

  • ERROR その他の実行時エラーまたは予期しない状態。

  • FATAL 重大なエラーにより、途中で終了します。

ブロックとガード

ここで、いくつかのデバッグステートメントを記述しようとしているコードセクションがあるとします。ロギング自体の影響と、ロギングメソッドに渡すパラメータの生成の両方が原因で、これはパフォーマンスにすぐに影響する可能性があります。

この種の問題を回避するために、次のような形式で書きたいことがよくあります。

_if (LOGGER.isDebugEnabled()) {
   // lots of debug logging here, or even code that
   // is only used in a debugging context.
   LOGGER.debug(" result: " + heavyComputation());
}
_

メッセージが出力されない場合でも、デバッグステートメントのブロックの前にこのガードを使用していなかった場合(たとえば、ロガーが現在INFOレベルを超えるもののみを出力するように構成されている場合)、 heavyComputation()メソッドは引き続き実行されます。

構成

構成はロギングフレームワークに大きく依存しますが、これらはほとんど同じテクニックを提供します。

  • プログラムによる設定(実行時、API経由-は実行時の変更を許可します)、
  • 静的な宣言的構成(開始時、通常はXMLまたはプロパティファイルを介して-最初に必要なものになる可能性が高い)。

また、ほとんど同じ機能を提供します。

  • 出力メッセージのフォーマットの構成(タイムスタンプ、マーカーなど...)、
  • 出力レベルの設定、
  • きめの細かいフィルターの構成(たとえば、パッケージまたはクラスを含める/除外するため)
  • ログする場所(コンソール、ファイル、Webサービスなど)を決定するためのアペンダーの構成、およびおそらく古いログ(たとえば、自動ローリングファイル)をどうするか。

以下は、_logback.xml_ファイルを使用した宣言型構成の一般的な例です。

_<configuration>

  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <!-- encoders are assigned the type
         ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
    <encoder>
      <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
    </encoder>
  </appender>

  <root level="debug">
    <appender-ref ref="STDOUT" />
  </root>
</configuration>
_

前述のように、これはフレームワークに依存し、他の代替手段がある可能性があります(たとえば、LogBackではGroovyスクリプトを使用することもできます)。 XML構成形式も、実装によって異なる場合があります。

その他の設定例については、(特に)以下を参照してください。

歴史的な楽しみ

Log4J は現在、メジャーな更新が行われており、バージョン 1.x から 2.x に移行しています。もっと歴史的な面白さや混乱のために両方を見たいと思うかもしれません、そしてあなたが Log4J を選択した場合、おそらく2.xバージョンで行くことを好むでしょう。

Mike Partridgeがコメントで述べたように、LogBackが元のLog4Jチームメンバーによって作成されたことは注目に値します。これは、Javaロギングフレームワークの欠点に対処するために作成されました。また、次期メジャーLog4J 2.xバージョン自体が、LogBackから取得したいくつかの機能を統合するようになりました。

勧告

結論として、できる限り分離して、少数で遊んで、何が最適かを確認します。最後にそれは単なるロギングフレームワークです。非常に具体的な理由がある場合を除いて、使いやすさや個人的な好みは別として、これらはどれでも大丈夫ですので、それを忘れる必要はありません。それらのほとんどは、ニーズに合わせて拡張することもできます。

それでも、今日組み合わせを選択する必要がある場合は、LogBack + SLF4Jを使用します。しかし、数年後に私に尋ねた場合、Apache Commons Loggingを備えたLog4Jをお勧めします。したがって、依存関係に注意を払い、それらとともに進化してください。

52
haylem

ロギングフレームワークを使用する

ほとんどの場合、静的なファクトリメソッドがあります。

private static final Logger logger = Logger.create("classname");

次に、さまざまなレベルでロギングコードを出力できます。

logger.warning("error message");
logger.info("informational message");
logger.trace("detailed message");

次に、各クラスのどのメッセージをログ出力(ファイルまたはstderr)に書き込むかを定義できる単一のファイルがあります。

2
ratchet freak

これは、log4jや新しいslf4jなどのロギングフレームワークがまさに対象とするものです。ロギングを非常に詳細に制御し、アプリケーションの実行中でもログを構成できます。

1

ロギングフレームワークは間違いなく進むべき道です。ただし、優れたテストスイートも必要です。多くの場合、適切なテストカバレッジにより、デバッグ出力をまとめて行う必要がなくなります。

0
Dima