如何使用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
日志级别。 我强烈反对这一决定背后的理由,但事实就是如此。
这是我与一些同事一起提出的最接近的解决方案:
- 使用SLF4J标记创建致命标记类。
- 使用RoutingAppender ,使用Marker作为路由模式: “$$ {marker:}”
- 配置一个特定于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桥。 这需要一点点工作,但由于各种复杂的原因非常值得,我将在一天内写博客。
这是你做的。
- 您可以在此处复制此代码: https : //github.com/apache/logging-log4j2/tree/master/log4j-slf4j-impl
- 然后,您需要编辑
Log4jLogger
类并更改标记方法(跟踪,错误,警告,信息等)以适当地分派。 即if (marker.contains("FATAL")) fatal(....);
- 从项目中排除原始
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语句(调试和跟踪,当然)。