web-dev-qa-db-ja.com

ログインJavaおよび一般的に:ベストプラクティス?

時々、ログコードを見ると、正しくやっているのだろうかと思います。それに対する決定的な答えはないかもしれませんが、私には以下の懸念があります:

ライブラリクラス

いくつかのINFOメッセージを記録するライブラリクラスがいくつかあります。致命的なエラーは例外として報告されます。現在、クラス名をロギング名として使用する静的ロガーインスタンスがクラスにあります。 (Log4jの場合:Logger.getLogger(MyClass.class)

これは正しい方法ですか?このライブラリクラスのユーザーは、私の実装からのメッセージを望んでいないか、アプリケーション固有のログにリダイレクトしたいかもしれません。ユーザーが「外の世界」からロガーを設定できるようにする必要がありますか?そのような場合はどのように処理しますか?

一般ログ

一部のアプリケーションでは、クラスが、クラスの名前で識別されない特定のログにログメッセージを書き込みたい場合があります。 (つまり:HTTP Request log)そのようなことをする最良の方法は何ですか?検索サービスが思い浮かびます...

63
Malax

あなたの慣習はかなり標準的で、非常に素晴らしいです(私見)。

注目すべき1つのことは、過度のアンデッドデバッグコールによるメモリの断片化です。したがって、Log4J(および他のほとんどのJavaロギングフレームワーク)では、次のような結果になります。

if (log.isDebugEnabled()) {
  log.debug("...");
}

そのログメッセージ(おそらくは使用していない)の構築は、特に数千または数百万回行われた場合、高価になる可能性があるためです。

INFOレベルのロギングは、「チャット」ではないはずです(そして、あなたの言うことから、そうではないように聞こえます)。 INFOメッセージは、アプリケーションの起動や停止のように、一般的に意味があり重要です。問題が発生した場合に知りたいことがあります。デバッグ/ファインレベルのロギングは、診断しようとしている問題が実際にある場合により多く使用されます。デバッグ/ファインロギングは通常、必要な場合にのみオンになります。通常、情報は常にオンになっています。

誰かがあなたのクラスから特定のINFOメッセージを望んでいないなら、彼らはもちろんそれらを取得しないようにlog4j設定を自由に変更できます。 Log4jは、この部門では非常に単純です(Java 1.4ロギング)とは対照的です)。

HTTPに関しては、通常、Javaロギングの問題となることはありません。なぜなら、通常、単一のクラスがあなたが興味を持っていることを担当しているので、それを置くだけです。 (私の経験では珍しい)一見無関係なクラスに共通のログメッセージが必要な場合は、簡単にgrepできるトークンを入れてください。

37
cletus

以下は、すべてのプロジェクトで良好なパフォーマンスを確保するために従うガイドラインのセットです。インターネットのさまざまなソースからの入力に基づいて、この一連のガイドラインを作成しました。

今日のように、Log4j 2がJavaでのログ記録のための最高のオプションであると信じています。

ベンチマークが利用可能です ここ 。最高のパフォーマンスを得るために私が従うプラクティスは次のとおりです。

  1. 次の理由により、現時点ではSLF4Jの使用を避けています。
  2. パフォーマンスを向上させるために、非同期ロガーを使用してすべての定期的なロギングを実行します
  3. エラーメッセージが発生したらすぐに表示するため、同期ロガーを使用してエラーメッセージを別のファイルに記録します。
  4. 通常のロギングでは、ファイル名、クラス名、メソッド名、行番号などのロケーション情報を使用しないでください。これらの情報を取得するために、フレームワークがスタックのスナップショットを取得し、ウォークスルーするためです。これはパフォーマンスに影響します。したがって、通常のログではなく、エラーログでのみ場所情報を使用します
  5. 個別のスレッドで処理される個々のリクエストを追跡するために、説明されているようにスレッドコンテキストとランダムUUIDの使用を検討してください here
  6. エラーを別のファイルに記録しているため、エラーログにもコンテキスト情報を記録することが非常に重要です。例えばファイルの処理中にアプリケーションでエラーが発生した場合は、スタックログとともにエラーログファイルで処理中のファイル名とファイルレコードを出力します
  7. ログファイルはgrep可能で、理解しやすいものでなければなりません。例えばアプリケーションが複数のファイルの顧客レコードを処理する場合、各ログメッセージは次のようになります。
_12:01:00,127 INFO FILE_NAME=file1.txt - Processing starts
12:01:00,127 DEBUG FILE_NAME=file1.txt, CUSTOMER_ID=756
12:01:00,129 INFO FILE_NAME=file1.txt - Processing ends
_
  1. 以下に示すように、SQLマーカーを使用してすべてのSQLステートメントを記録し、フィルターを使用して有効または無効にします。
_private static final Marker sqlMarker = 
  MarkerManager.getMarker("SQL");

private void method1() {
    logger.debug(sqlMarker, "SELECT * FROM EMPLOYEE");
}
_
  1. Java 8 Lambdasを使用してすべてのパラメーターをログに記録します。これにより、特定のログレベルが無効になっている場合にアプリケーションがメッセージをフォーマットしないようにします。
_int i=5, j=10;
logger.info("Sample output {}, {}", ()->i, ()->j);
_
  1. 文字列連結を使用しないでください。上記のようにパラメーター化されたメッセージを使用します

  2. ロギング構成の動的再ロードを使用して、アプリケーションがアプリケーションを再起動することなくロギング構成の変更を自動的に再ロードするようにします

  3. printStackTrace()またはSystem.out.println()を使用しないでください

  4. アプリケーションは、終了する前にロガーをシャットダウンする必要があります。

_LogManager.shutdown();
_
  1. 最後に、皆の参考のために、次の構成を使用します。
_<?xml version="1.0" encoding="UTF-8"?>
<Configuration monitorinterval="300" status="info" strict="true">
    <Properties>
        <Property name="filePath">${env:LOG_ROOT}/SAMPLE</Property>
        <Property name="filename">${env:LOG_ROOT}/SAMPLE/sample
        </Property>
        <property name="logSize">10 MB</property>
    </Properties>
    <Appenders>
        <RollingFile name="RollingFileRegular" fileName="${filename}.log"
            filePattern="${filePath}/sample-%d{yyyy-dd-MM}-%i.log">
            <Filters>
                <MarkerFilter marker="SQL" onMatch="DENY"
                    onMismatch="NEUTRAL" />
            </Filters>
            <PatternLayout>
                <Pattern>%d{HH:mm:ss,SSS} %m%n
                </Pattern>
            </PatternLayout>
            <Policies>
                <TimeBasedTriggeringPolicy
                    interval="1" modulate="true" />
                <SizeBasedTriggeringPolicy
                    size="${logSize}" />

            </Policies>
        </RollingFile>
        <RollingFile name="RollingFileError" 
            fileName="${filename}_error.log"
            filePattern="${filePath}/sample_error-%d{yyyy-dd-MM}-%i.log"
            immediateFlush="true">
            <PatternLayout>
                <Pattern>%d{HH:mm:ss,SSS} %p %c{1.}[%L] [%t] %m%n
                </Pattern>
            </PatternLayout>
            <Policies>
                <TimeBasedTriggeringPolicy
                    interval="1" modulate="true" />
                <SizeBasedTriggeringPolicy
                    size="${logSize}" />
            </Policies>
        </RollingFile>
    </Appenders>
    <Loggers>
        <AsyncLogger name="com"
            level="trace">
            <AppenderRef ref="RollingFileRegular"/>
        </AsyncLogger>
        <Root includeLocation="true" level="trace">
            <AppenderRef ref="RollingFileError" level="error" />
        </Root>
    </Loggers>
</Configuration>
_
  1. 必要なMaven依存関係は次のとおりです。
_<dependency>
    <groupId>org.Apache.logging.log4j</groupId>
    <artifactId>log4j-api</artifactId>
    <version>2.8.1</version>
</dependency>
<dependency>
    <groupId>org.Apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.8.1</version>
</dependency>
<dependency>
    <groupId>com.lmax</groupId>
    <artifactId>disruptor</artifactId>
    <version>3.3.6</version>
</dependency>
<!-- (Optional)To be used when working 
with the applications using Log4j 1.x -->
<dependency>
    <groupId>org.Apache.logging.log4j</groupId>
    <artifactId>log4j-1.2-api</artifactId>
    <version>2.8.1</version>
</dependency>
_
9
Saptarshi Basu

@ cletus 'answer で、彼は

if (log.isDebugEnabled()) {
  log.debug("val is " + value);
}

SL4J を使用することで克服できます。フォーマットのヘルプを提供します

log.debug("val is {}", value);

メッセージは、レベルがデバッグの場合にのみ作成されます。

そのため、今日では、パフォーマンスと安定性の理由から、SL4JとそのコンパニオンロガーであるLogbackを使用するのは 推奨 です。

8
serv-inc

ロガーのインスタンス化に関しては、Eclipse Javaロガーをセットアップするためのテンプレートを使用していくつかの成功を収めました。

private static Logger log = Logger.getLogger(${enclosing_type}.class);

これにより、JVMがスタックトレースをいじる問題を回避し、最初にスタックトレースを作成することによるオーバーヘッドを(おそらく、おそらく)削減します。

このようなテンプレートを使用することの素晴らしい点は、ロガーのために一貫した標準を設定したい場合、チームと共有できることです。

IntelliJは、囲んでいる型の名前を表すテンプレート変数の同じ概念をサポートしているようです。 NetBeansで簡単に行う方法がわかりません。

6
justinpitts

記述している種類のlog4j構成の推奨オプションは、log4j構成ファイルを使用することです。これにより、実装のユーザーは、自分の実装により適したもので構成を後でオーバーライドできるため、要求したとおりに実行できます。非常に徹底的な入門書については、 here を参照してください。

4
John Feminella

私はおそらくどこかからこれを盗んだが、それはいいです。

コピーするときにロガーを混同するリスクを軽減し、リファクタリングを貼り付けます。また、入力する必要が少なくなります。

コード内:

private final static Logger logger = LoggerFactory.make();

...およびLoggerFactory:

public static Logger make() {
    Throwable t = new Throwable();
    StackTraceElement directCaller = t.getStackTrace()[1];
    return Logger.getLogger(directCaller.getClassName());
}

(スタックダンプは初期化中に行われます。スタックトレースはおそらく JVMによって最適化されませんが、実際には保証はありません)

4
KarlP

アプリケーションのログレベルを確認していますが、現在パターンを検出しています:

private static final Logger logger = Logger.getLogger(Things.class)

public void bla() {
  logger.debug("Starting " + ...)
  // Do stuff
  ...
  logger.debug("Situational")

  // Algorithms
  for(Thing t : things) {
    logger.trace(...)
  }

  // Breaking happy things
  if(things.isEmpty){
    logger.warn("Things shouldn't be empty!")
  }

  // Catching things
  try {
    ...
  } catch(Exception e) {
    logger.error("Something bad happened")
  }

  logger.info("Completed "+...)
}

Log4j2-fileは、ソケットアペンダーとフェイルオーバーファイルアペンダーを定義します。コンソールアペンダー。状況によって必要な場合は、log4j2マーカーを使用することがあります。

余分な視点が役立つかもしれないと思った。

4
Patrick

さらに、Java(SLF4J)( http://www.slf4j.org/ )のSimple Logging Facadeを意味することが重要だと思います。大きなプロジェクトの多様化した部分で異なるロギングフレームワークを使用する問題、SLF4Jは、これらの部分を正常に管理するための問題を解決するための事実上の標準ではありませんか?

2番目の概念:一部の旧式のタスクは Aspect-Oriented-Programming で置き換えることができるようです、Spring frmwrkには独自の 実装here StackOverflowで here Springブログで。

2
Yauhen