如何使用SLF4J和Log4j2记录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 !!! 

您是否知道如何有效地从日志输出中删除“错误”?

非常感谢你

Marker并不是你想要的。 Marker用于“丰富”日志消息,使其更容易搜索。 您正在尝试更改日志级别/优先级,这有点不同。

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

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

 logger.fatal(yourMessage); 

要么

 logger.log(priorityLevel, yourMessage); 

更新:

来自SLF4J网站:

标记接口是org.slf4j包的一部分,它使FATAL级别在很大程度上是多余的。 如果给定的错误需要注意超出为普通错误分配的错误,只需使用特殊指定的标记标记日志记录语句,该标记可以命名为“致命”或任何其他您喜欢的名称。

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

因此,使用SLF4J,不可能具有FATAL日志级别。 我强烈反对这一决定背后的理由,但事实就是如此。

这是我与一些同事一起提出的最接近的解决方案:

  1. 使用SLF4J标记创建致命标记类。
  2. 使用RoutingAppender ,使用Marker作为路由模式: “$$ {marker:}”
  3. 配置一个特定Fatal的 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 

到目前为止,我发现的唯一解决方案是使用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删除任何外部库的日志级别。

摘要 :此解决方案不是一个好的解决方案。

更新 :我尝试了另一种解决方案,写了一个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( 这是有意义的 )。

总结 :仍然没有解决方案。

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

编写自己的自定义slf4j静态绑定程序aka桥。 这需要一点点工作,但由于各种复杂的原因非常值得,我将在一天内写博客。

这是你做的。

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

<皂箱咆哮>

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

  • 它使得很难/不可能覆盖硬核静态初始化以及不提供logger.fatal(...)
  • 标记不是必需的,并且本质上很复杂:
    • 很少有项目使用标记。 我实际上看起来/ grepped开源项目和标记使用率接近于零。
    • 使用标记的是因为fatal缺失。 它不是80/20。 标记是1%, fatal是99%。
    • 许多开发人员认为,以某种方式使用标记“致命”会将其映射为致命的。
    • 很少有人知道包括我自己在内的独立标记 。
    • 与MDC上下文提供的内容重叠。 给定面向事件维度的数据库(elasticsearch,druid等),MDC上下文是优越的(名称/值对)。

1 其中一个能够实际上是您的日志框架的启动过程的一部分,反对很难确定几乎任意的静态初始化

我知道问题是log4j。 在查看与logback相关时,我找到了这个页面。 这是sl4j推荐的内容: https ://www.slf4j.org/faq.html#fatal。

您可以在邮件开头添加“致命”。 例如:

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

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