如何使用 SLF4J 和 Log4j2 记录 FATAL(或任何自定义日志级别)

How to log FATAL (or any custom log level) with SLF4J and 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 !!!

你知道如何有效地从日志输出中删除 "ERROR" 吗?

非常感谢

Marker 并不是您真正想要的。 Marker 用于 "enriching" 日志消息,使它们更易于搜索。您正在尝试更改日志 level/priority,这有点不同。

您正在使用 logger.error(),它将消息记录为 ERROR 级别。

如果没有预定义的FATAL级别(通常有,例如logger.fatal()),则使用允许您指定日志级别的通用logger.log()

logger.fatal(yourMessage);

logger.log(priorityLevel, yourMessage);

更新:

来自 SLF4J 网站:

The Marker interface, part of the org.slf4j package, renders the FATAL level largely redundant. If a given error requires attention beyond that allocated for ordinary errors, simply mark the logging statement with a specially designated marker which can be named "FATAL" or any other name to your liking.

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

因此,使用 SLF4J,不可能有 FATAL 日志级别。我非常不同意这个决定背后的理由,但事实就是如此。

到目前为止我找到的唯一解决方案是使用 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"

我有点认为这个解决方案是 hack...它还会以正确的方式使用 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. 使用SLF4J标记创建致命标记class。
  2. 使用 RoutingAppender,使用 Marker 作为路由模式:"$${marker:}"
  3. 配置一个 Fatal-specific appender,它有自己的 PatternLayout,它不包含 LogLevel 但包含硬编码的 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

这是我为 log4j 做的,我也推荐给 log4j2...

编写您自己的自定义 slf4j 静态绑定程序又名桥。这需要一些工作,但由于各种复杂的原因,这是非常值得的 1 我大概有一天会写博客。

这是您的工作。

  1. 您在此处复制此代码:https://github.com/apache/logging-log4j2/tree/master/log4j-slf4j-impl
  2. 然后您想要编辑 Log4jLogger class 并更改标记方法(跟踪、错误、警告、信息等)以适当地分派。即if (marker.contains("FATAL")) fatal(....);
  3. 从您的项目中排除原始 log4j-slf4j-impl 并使用您的新代码。

老实说,我认为 slf4j 存在严重缺陷,因为

  • 它非常 difficult/impossible 覆盖硬核静态初始化以及不提供 logger.fatal(...)
  • 不需要标记,标记本身就很复杂:
    • 非常非常非常少的项目使用标记。我实际上 looked/grepped 开源项目和标记使用率接近于零低。
    • 使用标记的是因为缺少fatal。这不是 80/20。标记用于 1%,fatal 用于 99%。
    • 许多开发人员认为使用标记 "fatal" 会以某种方式将其映射到致命的。
    • Few know what a detached marker is including myself.
    • 与 MDC 上下文提供的内容重叠。给定面向事件维度的数据库(elasticsearch、druid 等),MDC 上下文更优越(name/value 对)。

1 其中之一能够真正成为日志框架启动过程的一部分,而不是难以确定几乎任意的静态初始化

我知道问题是针对 log4j 的。我在查看与 logback 相关的内容时找到了此页面。以下是 sl4j 的建议:https://www.slf4j.org/faq.html#fatal

您可以在邮件开头添加 "FATAL"。例如:

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

您确实丢失了一些东西,比如基于级别的过滤,但这对很多人来说可能没问题,特别是因为您不太可能过滤掉 FATAL 语句(当然是调试和跟踪)。