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


32

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

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

public MyClass {
  private static final boolean DEBUG = false;

  ... some code ...

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

など。

もちろん、それはうまくいきますが、数個を見つめていなければ、DEBUGをtrueに設定するクラスが多すぎる可能性があります。

逆に、出力されるテキストの量が圧倒的になる可能性があるため、私は(他の多くの人と同じように)アプリケーション全体をデバッグモードにすることを好みません。

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


14
Javaでは、ロギングにhomebrewコードを使用するのは正しい方法ではありません。確立されたフレームワークを選択し、車輪を再発明しないでください
-gnat

あなたが述べたのと同じ理由で、より複雑なクラスのいくつかでブール値のデバッグを使用します。通常、アプリケーション全体をデバッグするのではなく、クラスで問題が発生するだけです。この習慣はCOBOL時代に由来し、DISPLAYステートメントが利用可能なデバッグの唯一の形式でした。
ギルバートルブラン

1
また、可能な場合はデバッガーに依存し、デバッグステートメントでコードを散らかさないことをお勧めします。
アンドリューTフィネル

1
単体テストでテスト駆動開発(TDD)を実践していますか?それを始めてから、「デバッグコード」の大幅な削減に気付きました。
JW01

回答:


52

ロギングフレームワーク、そしておそらくロギングファサードフレームワークを見たいと思うでしょう。

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

フレームワーク

いくつかのロギングフレームワーク

  • Java Logging Framework(JDKの一部)、
  • Apache Log4J(少し古いですが、まだ強力であり、積極的に維持されています)、
  • LogBack(のクリエイターの一つで、Log4Jのより現代的なアプローチを提供するために作成Log4Jの)。

いくつかのロギングファサード

使用法

基本的な例

これらのフレームワークのほとんどでは、次のような形式(ここではslf4j-apiand を使用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.x2.xの。より歴史的な楽しみや混乱のために両方を見たいと思うかもしれません。Log4Jを選ぶなら、おそらく2.xバージョンで行くことを好むでしょう。

Mike Partridgeがコメントで述べたように、LogBackは元Log4Jチームメンバーによって作成されたことに注意する価値があります。これは、Java Loggingフレームワークの欠点に対処するために作成されました。また、今後の主要なLog4J 2.xバージョンは、LogBackからいくつかの機能を統合したものです。

勧告

結論として、できる限り分離した状態を保ち、いくつかを試してみて、何が最適かを確認してください。最終的は、単なるロギングフレームワークです。使いやすさや個人的な好み以外に、非常に具体的な理由がある場合を除いて、これらのいずれかがかなりうまくいくので、それにぶら下がることは意味がありません。それらのほとんどは、ニーズに合わせて拡張することもできます。

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


1
SLF4JとLogBackは、もともとLog4Jを書いた人によって書かれました。
マイクパートリッジ

4
ロギングのパフォーマンスへの影響が心配な場合:slf4j.org/faq.html#logging_performance
Mike Partridge

2
それの価値は、それはあなたがあなたのロガーを作る必要があるかどうかとても明確ではないことを言及するstaticことは少量のメモリを節約するために、しかし、特定の場合に問題が発生します。slf4j.org/faq.html#declared_static
復活モニカ

1
@MikePartridge:リンクの内容は知っていますが、それでもインスタンスのパラメーター評価を妨げることはありません。bytパラメータ化されたロギングのパフォーマンスが向上する理由は、ログメッセージの処理が発生しないためです(特に連結)。ただし、メソッド呼び出しがパラメーターとして渡されると、実行されます。そのため、ユースケースによっては、ブロックが役立つ場合があります。また、投稿で述べたように、DEBUGレベルが有効になっているときに発生する他のデバッグ関連アクティビティ(ロギングだけでなく)をグループ化するだけでも役立ちます。
ヘイレム

1
@haylem-それは本当です、私の間違い。
マイクパートリッジ

2

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

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

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

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

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

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


1

これはまさに、log4jまたは新しいslf4jのようなロギングフレームワークの目的です。これらを使用すると、ロギングを非常に詳細に制御し、アプリケーションの実行中でも設定できます。


0

ロギングフレームワークは間違いなく行くべき方法です。ただし、適切なテストスイートも必要です。多くの場合、テストカバレッジが良好であれば、デバッグ出力の必要性をすべて排除できます。


ロギングフレームワークを使用し、デバッグロギングを利用できる場合-これにより、非常に悪い日を避けることができます。
Fortyrunner

1
ログを記録すべきではないとは言いませんでした。最初にテストが必要だと言いました。
ディマ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.