web-dev-qa-db-ja.com

SLF4JおよびLog4j2を使用してFATAL(または任意のカスタムログレベル)を記録する方法

特定の要件があります:

  • ログインできる必要があります[〜#〜] fatal [〜#〜]レベル
  • 使用する必要があるSLF4J
  • 使用する必要があるLog4j2

今、これが私の実装です

final Logger logger = LoggerFactory.getLogger(HelloWorld.class);
final Marker marker = MarkerFactory.getMarker("FATAL");
logger.error(marker, "!!! Fatal World !!!");

これが私のPatternLayout(yamlで)です:

PatternLayout:
  Pattern: "%d{ISO8601_BASIC} %-5level %marker [%t] %logger{3.} - %msg%n"

これが私のログ出力です

20150506T155705,158 ERROR FATAL [main] - !!! Fatal World !!!

ログ出力から「エラー」を効率的に削除する方法について何か考えがありますか?

どうもありがとうございました

13
Daniel Marcotte

これは私が何人かの同僚と一緒に来た最も近い実用的な解決策です:

  1. SLF4Jマーカーを使用してFatalマーカークラスを作成します。
  2. RoutingAppender を使用して、ルーティングパターンとしてMarkerを使用します: "$$ {marker:}"
  3. LogLevelを含まず、ハードコードされたFATALレベルを含む独自のPatternLayoutを持つFatal固有のアペンダーを構成します。

これはJavaサンプルです:

Marker fatal = MarkerFactory.getMarker("FATAL");
// Usage example
final Logger logger = LoggerFactory.getLogger(FatalLogger.class);
logger.log(fatal, "this is a fatal message");

// Log sample : 
20150514T115144,279  FATAL [main] FatalLogger - this is a fatal message

これが[〜#〜] yaml [〜#〜]サンプルです:

Configuration:
  status: debug

  Appenders:
    RandomAccessFile:
      - name: APPLICATION_APPENDER
        fileName: logs/application.log
        PatternLayout:
          Pattern: "%d{ISO8601_BASIC} %-5level %msg%n"
      - name: FATAL_APPENDER
        fileName: logs/application.log
        PatternLayout:
          Pattern: "%d{ISO8601_BASIC} FATAL %msg%n"

    Routing:
      name: ROUTING_APPENDER
      Routes:
        pattern: "$${marker:}"
        Route:
        - key: FATAL
          ref: FATAL_APPENDER
        - ref: APPLICATION_APPENDER #DefaultRoute

  Loggers:
    Root:
      level: trace
      AppenderRef:
        - ref: ROUTING_APPENDER
6
Daniel Marcotte

Markerは、ここで本当に望んでいるものではありません。 Markerは、ログメッセージを「強化」して、より簡単に検索できるようにするためのものです。ログレベル/優先度を変更しようとしていますが、これは少し異なります。

メッセージをERRORレベルとして記録するlogger.error()を使用しています。

事前定義されたFATALレベルがない場合(通常はlogger.fatal()など)、ログレベルを指定できる汎用のlogger.log()を使用します。

logger.fatal(yourMessage);

OR

logger.log(priorityLevel, yourMessage);

更新:

SLF4Jウェブサイトから:

Org.slf4jパッケージの一部であるMarkerインターフェイスは、FATALレベルを大幅に冗長にします。特定のエラーに通常のエラーに割り当てられたエラー以外の注意が必要な場合は、特別に指定されたマーカーでロギングステートメントをマークします。

http://www.slf4j.org/faq.html#fatal

したがって、SLF4Jでは、FATALログレベルを設定することはできません。私はこの決定の背後にある理論的根拠には強く反対しますが、それはそれがそうであるものです。

9
SnakeDoc

質問がlog4jに関するものであることはわかっています。このページは、logbackに関して調べたときに見つかりました。 sl4jの推奨事項は次のとおりです https://www.slf4j.org/faq.html#fatal

3

これは私がlog4jのためにしたことですが、log4j2についてもお勧めします。 ...

独自のカスタムslf4j静的バインダー(別名、ブリッジ)を記述します。これには少しの作業が必要ですが、さまざまな複雑な理由から価値があります 1 ブログを書くのは1日ほどです。

これがあなたのやることです。

  1. このコードをここにコピーします: https://github.com/Apache/logging-log4j2/tree/master/log4j-slf4j-impl
  2. 次に、 _Log4jLogger_ クラスを編集し、適切にディスパッチするようにマーカーメソッド(trace、error、warn、infoなど)を変更します。すなわちif (marker.contains("FATAL")) fatal(....);
  3. プロジェクトから元の_log4j-slf4j-impl_を除外し、新しいコードをそのまま使用します。

<soap-box-rant>

正直なところ、slf4jには重大な欠陥があると思います

  • logger.fatal(...)を提供せずにハードコア静的初期化をオーバーライドすることは非常に困難/不可能になります。
  • マーカーは不要であり、本質的に複雑です。
    • マーカーを使用するプロジェクトはごくわずかです。私は実際にオープンソースプロジェクトを調べたり、手探りしたりしており、マーカーの使用法はゼロに近いです。
    • マーカーを使用するのは、fatalがないためです。 80/20ではありません。マーカーは1%用で、fatalは99%用です。
    • 多くの開発者は、どういうわけか「致命的」というマーカーを使用すると、それを致命的とマッピングすると考えています。
    • 分離されたマーカーが自分自身を含むものを知っている人はほとんどいません
    • MDCコンテキストが提供するものと重複しています。イベントディメンション指向のデータベース(elasticsearch、druidなど)がある場合、MDCコンテキストは優れています(名前と値のペア)。

</ soap-box-rant>

1それらの1つは、ほとんど任意の静的な初期化を決定するのが難しいのとは対照的に、実際にロギングフレームワークのブートプロセスの一部になることができます

1
Adam Gent

これまでに見つけた唯一の解決策は、5つのマーカーを使用することです。

final Marker traceMarker = MarkerFactory.getMarker("TRACE");
final Marker debugMarker = MarkerFactory.getMarker("DEBUG");
final Marker infoMarker = MarkerFactory.getMarker("INFO");
final Marker warnMarker = MarkerFactory.getMarker("WARN");
final Marker errorMarker = MarkerFactory.getMarker("ERROR");
final Marker fatalMarker = MarkerFactory.getMarker("FATAL");

そして、毎回マーカーを渡すログ:

logger.info(infoMarker, "!!! INFO World !!!");
logger.error(errorMarker, "!!! ERROR World !!!");
logger.error(fatalMarker, "!!! FATAL World !!!");

そしてPatternLayoutを変更してLogLevelを完全に削除し、次のように常にマーカーをログに記録します。

PatternLayout:
  Pattern: "%d{ISO8601_BASIC} %marker [%t] %logger{3.} - %msg%n"

このソリューションはハックだと思います... LogLevelを正しい方法で使用すると、外部ライブラリのログレベルも削除されます。

概要:この解決策は良い解決策ではありません。

[〜#〜] update [〜#〜]:RewritePolicyを記述して別のソリューションを試しました:

public class FatalRewritePolicy implements RewritePolicy {

    public static final String FATAL = "FATAL";

    @Override
    public LogEvent rewrite(final LogEvent logEvent) {

        final Marker marker = logEvent.getMarker();
        if (marker == null)
            return logEvent;

        // Log Level is final in the LogEvent, there's no way we can modify it.
        Level level = logEvent.getLevel();

        return null;
    }
}

Log4j2でLogEventのLogLevelを変更する方法はないようです)。

概要:まだ解決策はありません。

1
Daniel Marcotte

メッセージの最初に「致命的」を追加できます。例えば:

LOGGER.error("FATAL: database connection lost.");

レベルに基づくフィルタリングなど、いくつかのことは失われますが、特にFATALステートメント(デバッグとトレース、確かに)をフィルターで除外する可能性が低いため、これは多くの人にとって問題ないかもしれません。

0