如何让 log4j syslog appender 在一行中写入堆栈跟踪?
Posted
技术标签:
【中文标题】如何让 log4j syslog appender 在一行中写入堆栈跟踪?【英文标题】:How to make log4j syslog appender write a stack trace in one line? 【发布时间】:2016-07-20 16:43:17 【问题描述】:我正在使用 log4j syslog appender,并注意到当发生异常时,appender 会将堆栈跟踪中的每个条目写入一个新行。
有没有办法配置它,以便整个堆栈跟踪将作为一行而不是多行?
【问题讨论】:
我不确定你能做到这一点,因为我不相信实际上是 log4j 正在执行堆栈跟踪写入。您可以将其写入字符串并删除回车符。 模式布局怎么样? 看起来 @davidhxx 回答了你的问题,但我的意思是我的印象是 Log4J 和所有日志记录工具都只是使用Throwable.printStackTrace()
来输出堆栈跟踪,在这种情况下您可能无法轻松更改格式。但是,根据下面的答案,Log4J 似乎有某种规定来处理它,我猜这是使用覆盖的 printStackTrace()
调用之一将值写入流/写入器,以便您可以更改它.
【参考方案1】:
我正在使用以下 log4j2 配置,它可以很好地发送到 syslog,并且不需要更改代码即可将所有这些异常转换为字符串。它只是用异常管道替换默认的linesep。
<Syslog name="SYSLOG" host="127.0.0.1" port="515" protocol="TCP" charset="UTF-8"
immediateFail="false" ignoreExceptions="true" reconnectionDelayMillis="250">
<PatternLayout pattern="[%d] %-5p %m%n %throwableseparator(|)"></PatternLayout>
</Syslog>
【讨论】:
很好的解决方案。也许您应该考虑更换恰好为此设计的 \u2028 管道。 明确一点,这就是您在 PatternLayout 中使用\u2028
的方式(因为它需要 XML 转义码;\u
代码不起作用):%throwableseparator(&#8232;)
【参考方案2】:
Log4j2
见james' answer。
Log4j v1
默认情况下,syslog appender 会将格式化的日志以及堆栈跟踪的每一行作为 syslog 中的单独日志条目发送。因为 syslog 使用 UDP,所以这些行可能是乱序的。
如果这是您要解决的问题,那么james mentioned 最好的方法是使用带有%throwable
转换字符的模式布局。他记录了如何为 log4j2 做到这一点。对于 log4j1,它需要 EnhancedPatternLayout:
<appender name="syslog" class="org.apache.log4j.net.SyslogAppender">
<param name="SyslogHost" value="127.0.0.1:514"/>
<layout class="org.apache.log4j.EnhancedPatternLayout">
<param name="ConversionPattern" value="[%d] %-5p %m%n%throwable"/>
</layout>
</appender>
注意上述解决方案仍会使用换行符来分隔堆栈跟踪的各个行,但它会解决堆栈跟踪行在 syslog 中作为单独的日志条目发送的问题。
如果您真的希望堆栈跟踪位于一行,快速解决方法是将上述示例与%throwableshort
或%throwable1
一起使用,这将只包括堆栈跟踪的第一行。
如果您希望将整个堆栈跟踪放在一行中,您可能更喜欢使用自定义 ThrowableRenderer
作为
proposed by davidxxx。 (请注意,这仍然会将格式化的日志与堆栈跟踪分开发送到 syslog,因此为避免您可以将此解决方案与上述解决方案结合使用。)
例如:
import org.apache.log4j.DefaultThrowableRenderer;
import org.apache.log4j.spi.ThrowableRenderer;
import java.util.ArrayList;
import java.util.Arrays;
public class CustomThrowableRenderer implements ThrowableRenderer
private final DefaultThrowableRenderer defaultRenderer = new DefaultThrowableRenderer();
@Override
public String[] doRender(Throwable throwable)
String[] defaultRepresentation = defaultRenderer.doRender(throwable);
String[] newRepresentation = String.join("|", Arrays.asList(defaultRepresentation));
return newRepresentation;
然后在你的 log4j.xml 中:
<throwableRenderer class="CustomThrowableRenderer"/>
【讨论】:
在 log4j 1.x 中使用 EnhancedPatternLayout 和 %throwable 似乎没有任何用处。它仍然在多行中打印堆栈跟踪。你为什么说这行得通? 此解决方案(您建议的)是否特定于附加程序?应该不会吧?我正在使用 ConsoleAppender。 在logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/… 中,我没有看到任何建议支持在一行中显示堆栈跟踪。 @peter.petrov 很好的反馈,谢谢!我已经更新了我的答案,希望能更清楚。【参考方案3】:您可以自定义堆栈跟踪的渲染器。
Log4J 中有两个可用的ThrowableRenderer
(org.apache.log4j.spi.ThrowableRenderer
接口)
默认使用一个。
因此,使用您希望的格式实现您自己的 ThrowableRenderer,然后在您的配置中进行设置。
在 Log4j 1 中,它可以工作。永远不要在 Log42 中尝试。 https://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/PropertyConfigurator.html
ThrowableRenderer 您可以自定义 Throwable 实例转换为 String 的方式 在被记录之前。这是通过指定 ThrowableRenderer 来完成的。 语法是: log4j.throwableRenderer=fully.qualified.name.of.rendering.class log4j.throwableRenderer.paramName=paramValue 如中, log4j.throwableRenderer=org.apache.log4j.EnhancedThrowableRenderer
【讨论】:
谢谢,忘了提到我在 JBOSS Fuse 中使用它,所以我只能在代码方面做的非常有限,只能更改 Log4J 属性。有没有我可以使用的渲染器? 我不明白为什么您不能创建自己的渲染类并将其放入 JBoss 的类路径位置(例如 lib 目录)。 JBoss 有这个目录:common/lib。 JBoss的导火索有没有?有两个渲染器,但它们不能满足您的需求。 DefaultThrowableRenderer 和 EnhancedThrowableRenderer。 虽然这可行,但它仍会将日志消息和堆栈跟踪作为两个单独的系统日志消息发送。 @bmaupin 你确定吗?那是遗憾。无论您和 James 提出什么解决方案,显然都更好,因为它避免了在 log4j 配置文件之外创建一个类。 @davidxxx 无论 bmaupin 建议什么听起来不错,但不起作用。至少对我来说它不起作用。请参阅我添加到他的答案中的 cmets。你有什么主意吗?另见:***.com/questions/63623141/…你能提供一些想法吗?【参考方案4】:不要只是将异常传递给记录器(例如log.error(e)
),而是首先将堆栈跟踪转换为字符串并记录该字符串。
投票最多的答案to this question 有几种简单的方法可以将堆栈跟踪转换为字符串。
【讨论】:
这不是传统的记录方式。您可以忘记执行 log.error(e) 并调用经典和常见的共享方式。保持共同共享的良好实践在软件开发中很重要。以上是关于如何让 log4j syslog appender 在一行中写入堆栈跟踪?的主要内容,如果未能解决你的问题,请参考以下文章
Log4J 2 Syslog Appender 无法正常工作
log4j2 - Syslog 附加程序和 PatternLayout