79

ロギング コードを見ると、正しく実行できているか疑問に思うことがあります。明確な答えはないかもしれませんが、以下の懸念があります。

ライブラリ クラス

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

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

一般ログ

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

4

8 に答える 8

45

あなたの慣習はかなり標準的で、非常に優れています(imho)。

注意すべき点の 1 つは、過剰な不要なデバッグ呼び出しによるメモリの断片化です。Log4J (および他のほとんどの Java ロギング フレームワーク) では、次のような結果になります。

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

そのログ メッセージ (おそらく使用していないもの) を作成すると、特に何千回または何百万回も作成すると、コストがかかる可能性があるためです。

あなたの INFO レベルのロギングは、あまりにも「おしゃべり」であってはなりません (そして、あなたの言うことから、そうではないように思えます)。INFO メッセージは、アプリケーションの開始と停止のように、一般的に意味のある重要なものである必要があります。問題が発生した場合に知っておきたいこと。デバッグ/詳細レベルのロギングは、診断しようとしている問題が実際に発生した場合に使用されます。デバッグ/詳細ログは、通常、必要な場合にのみ有効になります。通常、情報は常にオンになっています。

あなたのクラスから特定の INFO メッセージを受け取りたくない人がいれば、もちろん、log4j の設定を自由に変更して、メッセージを受け取らないようにすることができます。Log4j は、(Java 1.4 ロギングとは対照的に) この分野では非常に単純です。

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

于 2009-05-25T10:48:50.520 に答える
23

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

今日と同じように、Log4j 2 は Java でログインするための最良のオプションであると信じています。

ベンチマークはこちらから入手できます。最高のパフォーマンスを得るために私が従う練習は次のとおりです。

  1. 次の理由から、現時点では SLF4J の使用を避けています。
  2. パフォーマンスを向上させるために、非同期ロガーを使用してすべての定期的なログを実行します
  3. エラーメッセージが発生したらすぐに確認したいので、同期ロガーを使用してエラーメッセージを別のファイルに記録します
  4. 通常のロギングでは、ファイル名、クラス名、メソッド名、行番号などの位置情報を使用しないでください。これらの情報を取得するために、フレームワークはスタックのスナップショットを取得し、それをウォークスルーするためです。これはパフォーマンスに影響します。したがって、通常のログではなく、エラー ログでのみ位置情報を使用します。
  5. 個別のスレッドによって処理される個々のリクエストを追跡する目的で、こちらで説明されているように、スレッド コンテキストとランダム UUID の使用を検討してください。
  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 Lambda を使用してすべてのパラメーターをログに記録します。これにより、指定されたログ レベルが無効になっている場合に、アプリケーションがメッセージをフォーマットするのを防ぐことができます。
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>
于 2018-01-21T05:22:12.687 に答える
14

@cletus ' answerで、彼は次の問題について書いています

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

SLF4Jを使用することで克服できる可能性があります。書式設定のヘルプを提供します

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

ここで、レベルがデバッグの場合にのみメッセージが構築されます。

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

于 2015-10-17T20:13:29.087 に答える
7

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

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

これにより、JVM がスタック トレースをいじるという問題が回避され、最初にスタック トレースを作成することによるオーバーヘッドが (些細なことですが) 削減されます。

このようなテンプレートを使用することの優れた点は、ロガーに対して全面的に一貫した基準を設定したい場合に、チームと共有できることです。

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

于 2012-11-14T14:18:35.983 に答える
5

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

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 は、socket-appender とフェイルオーバー file-appender を定義します。そしてコンソールアペンダー。状況に応じて、log4j2 マーカーを使用することがあります。

余分な視点が役立つかもしれないと考えました。

于 2016-05-12T09:23:11.833 に答える
4

どこかで盗んだのかもしれませんが、いいですね。

これにより、コピーおよび貼り付けのリファクタリング時にロガーが混同されるリスクが軽減され、入力が少なくなります。

あなたのコードでは:

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によって最適化されませんが、実際には保証はありません)

于 2009-05-25T11:10:56.153 に答える
4

説明している種類の log4j 構成の推奨オプションは、log4j 構成ファイルを使用することです。これにより、実装のユーザーは、後で独自の実装により適したもので構成をオーバーライドできるため、要求されていることを正確に行うことができます。非常に完全な入門書については、こちらを参照してください。

于 2009-05-25T10:48:54.390 に答える
3

さらに、Simple Logging Facade for Java (SLF4J) ( http://www.slf4j.org/ )を意味することが重要だと思います。大きなプロジェクトの多様な部分で異なるロギング フレームワークを使用するといういくつかの問題があるため、SLF4J はこれらの部分をうまく管理するための問題を解決するためのデファクト スタンダードですよね。

2 番目の概念: いくつかの古い学校のタスクはAspect-Oriented-Programmingで置き換えることができるようです。Spring frmwrk には独自の実装があり、ロギングの AOP アプローチは、ここ StackOverflow とここ Spring ブログで検討れてます。

于 2012-12-19T23:35:23.127 に答える