通过 syslog 发送 log4j2 堆栈跟踪
Send log4j2 stack traces over syslog
我正在尝试将堆栈跟踪记录到 Logstash 中。
日志堆栈是 ELK(ElasticSearch、Logstash、Kibana)。
生成日志的应用程序是一个 Java 应用程序,使用 slf4j
作为日志记录接口,log4j2
作为日志记录实现。
log4j2.xml
声明此 syslog
Appender,格式为 RFC5424
:
<Appenders>
<Syslog name="RFC5424" format="RFC5424" host="localhost" port="8514"
protocol="TCP" appName="MyApp" includeMDC="true" mdcId="mdc"
facility="LOCAL0" enterpriseNumber="18060" newLine="true"
messageId="Audit" id="App">
<LoggerFields>
<KeyValuePair key="thread" value="%t"/>
<KeyValuePair key="priority" value="%p"/>
<KeyValuePair key="category" value="%c"/>
<KeyValuePair key="exception" value="%ex{full}"/>
</LoggerFields>
</Syslog>
</Appenders>
我从 Java 应用程序中记录了一个 Throwable,如下所示:
org.slf4j.LoggerFactory.getLogger("exception_test").error("Testing errors", new RuntimeException("Exception message"));
记录异常时,Logstash 会跟踪类似这样的内容以显示它持续存在的内容:
{
"@timestamp":"2016-11-08T11:08:10.387Z",
"port":60397,
"@version":"1",
"host":"127.0.0.1",
"message":"<131>1 2016-11-08T11:08:10.386Z MyComputer.local MyApp - Audit [mdc@18060 category=\"exception_test\" exception=\"java.lang.RuntimeException: Exception message",
"type":"syslog",
"tags":[
"_grokparsefailure"
]
}
并且我确认 Kibana 在其日志条目之一的 _source
字段中显示完全相同的 JSON。
这里有一个问题:没有保存堆栈跟踪。消息 "Testing errors" 丢失了。
"tags":["_grokparsefailure"]
很不幸,但 与此问题无关。
我尝试添加 <ExceptionPattern/>
看看它是否会改变什么:
<Syslog name="RFC5424" format="RFC5424" host="localhost" port="8514"
protocol="TCP" appName="MyApp" includeMDC="true" mdcId="mdc"
facility="LOCAL0" enterpriseNumber="18060" newLine="true"
messageId="Audit" id="App">
<LoggerFields>
<KeyValuePair key="thread" value="%t"/>
<KeyValuePair key="priority" value="%p"/>
<KeyValuePair key="category" value="%c"/>
<KeyValuePair key="exception" value="%ex{full}"/>
</LoggerFields>
<ExceptionPattern>%ex{full}</ExceptionPattern>
</Syslog>
<ExceptionPattern/>
替换了日志消息,并且 也 (遗憾地)省略了所有 loggerFields
。但它确实给了我一个 class 名称和行号:
{
"@timestamp":"2016-11-08T11:54:03.835Z",
"port":60397,
"@version":"1",
"host":"127.0.0.1",
"message":"at com.Whosebug.LogTest.throw(LogTest.java:149)",
"type":"syslog",
"tags":[
"_grokparsefailure"
]
}
同样:没有堆栈跟踪。再一次:消息 "Testing errors" 丢失了。
如何使用 log4j2
将堆栈跟踪记录到 Logstash 中?我不一定要使用 syslog
appender。
基本上约束是:
- 不被锁定到任何特定的日志记录基础设施(这就是我使用 syslog 的原因)
- 多行堆栈跟踪需要理解为单个日志条目。 "each line of the stack trace" 是 "a separate log message"
是不可取的
- 堆栈跟踪必须能够进行过滤。我的一个典型异常可能有一页长的堆栈跟踪。我想过滤掉像 Spring.
这样的帧
Log4j 2.5 的 SyslogAppender 可以仅通过 UDP.
发送堆栈跟踪
<Syslog name="RFC5424" format="RFC5424" host="localhost" port="8514"
protocol="UDP" appName="MyApp" includeMDC="true" mdcId="mdc"
facility="LOCAL0" enterpriseNumber="18060" newLine="true"
messageId="LogTest" id="App">
<LoggerFields>
<KeyValuePair key="thread" value="%t"/>
<KeyValuePair key="priority" value="%p"/>
<KeyValuePair key="category" value="%c"/>
<KeyValuePair key="exception" value="%ex{full}"/>
</LoggerFields>
<ExceptionPattern>%ex{full}</ExceptionPattern>
</Syslog>
使用 UDP:ExceptionPattern
和 LoggerFields.KeyValuePair["exception"]
都开始作为多行堆栈跟踪的解决方案工作。
这是当我通过系统日志通过 UDP 发送异常时 logstash
打印的内容:
{
"@timestamp" => 2016-11-14T13:23:38.304Z,
"@version" => "1",
"host" => "127.0.0.1",
"message" => "<131>1 2016-11-14T13:23:38.302Z BirchBox.local MyApp - LogTest [mdc@18060 category=\"com.Whosebug.Deeply\" exception=\"java.lang.RuntimeException: Exception message\n\tat com.Whosebug.Deeply.complain(Deeply.java:10)\n\tat com.Whosebug.Nested.complain(Nested.java:8)\n\tat com.Whosebug.Main.main(Main.java:20)\n\" priority=\"ERROR\" thread=\"main\"] Example error\njava.lang.RuntimeException: Exception message\n\tat com.Whosebug.Deeply.complain(Deeply.java:10)\n\tat com.Whosebug.Nested.complain(Nested.java:8)\n\tat com.Whosebug.Main.main(Main.java:20)",
"type" => "syslog",
"tags" => [
[0] "_grokparsefailure"
]
}
在 [mdc@18060 exception=\"…\"]
中,我们得到 LoggerFields.KeyValuePair["exception"]
堆栈跟踪。
除此之外:由于 ExceptionPattern
.
,堆栈跟踪被插入 到记录的消息本身
供参考:这是当我通过系统日志通过 TCP 发送异常时 logstash
打印的内容(即与上述相同的 SyslogAppender,但 protocol="TCP"
代替):
{
"@timestamp" => 2016-11-14T19:56:30.293Z,
"port" => 63179,
"@version" => "1",
"host" => "127.0.0.1",
"message" => "<131>1 2016-11-14T19:56:30.277Z BirchBox.local MyApp - Audit [mdc@18060 category=\"com.Whosebug.Deeply\" exception=\"java.lang.RuntimeException: Exception message",
"type" => "syslog",
"tags" => [
[0] "_grokparsefailure"
]
}
{
"@timestamp" => 2016-11-14T19:56:30.296Z,
"port" => 63179,
"@version" => "1",
"host" => "127.0.0.1",
"message" => "at com.Whosebug.Deeply.complain(Deeply.java:10)",
"type" => "syslog",
"tags" => [
[0] "_grokparsefailure"
]
}
{
"@timestamp" => 2016-11-14T19:56:30.296Z,
"port" => 63179,
"@version" => "1",
"host" => "127.0.0.1",
"message" => "at com.Whosebug.Nested.complain(Nested.java:8)",
"type" => "syslog",
"tags" => [
[0] "_grokparsefailure"
]
}
{
"@timestamp" => 2016-11-14T19:56:30.296Z,
"port" => 63179,
"@version" => "1",
"host" => "127.0.0.1",
"message" => "at com.Whosebug.Main.main(Main.java:20)",
"type" => "syslog",
"tags" => [
[0] "_grokparsefailure"
]
}
{
"@timestamp" => 2016-11-14T19:56:30.296Z,
"port" => 63179,
"@version" => "1",
"host" => "127.0.0.1",
"message" => "\" priority=\"ERROR\" thread=\"main\"] Example error",
"type" => "syslog",
"tags" => [
[0] "_grokparsefailure"
]
}
{
"@timestamp" => 2016-11-14T19:56:30.296Z,
"port" => 63179,
"@version" => "1",
"host" => "127.0.0.1",
"message" => "java.lang.RuntimeException: Exception message",
"type" => "syslog",
"tags" => [
[0] "_grokparsefailure"
]
}
{
"@timestamp" => 2016-11-14T19:56:30.297Z,
"port" => 63179,
"@version" => "1",
"host" => "127.0.0.1",
"message" => "at com.Whosebug.Deeply.complain(Deeply.java:10)",
"type" => "syslog",
"tags" => [
[0] "_grokparsefailure"
]
}
{
"@timestamp" => 2016-11-14T19:56:30.298Z,
"port" => 63179,
"@version" => "1",
"host" => "127.0.0.1",
"message" => "at com.Whosebug.Nested.complain(Nested.java:8)",
"type" => "syslog",
"tags" => [
[0] "_grokparsefailure"
]
}
{
"@timestamp" => 2016-11-14T19:56:30.298Z,
"port" => 63179,
"@version" => "1",
"host" => "127.0.0.1",
"message" => "at com.Whosebug.Main.main(Main.java:20)",
"type" => "syslog",
"tags" => [
[0] "_grokparsefailure"
]
}
{
"@timestamp" => 2016-11-14T19:56:30.299Z,
"port" => 63179,
"@version" => "1",
"host" => "127.0.0.1",
"message" => "",
"type" => "syslog",
"tags" => [
[0] "_grokparsefailure"
]
}
看起来 TCP 实际上 "work",但将单个日志消息拆分为 许多 系统日志消息(例如,当遇到 \n
时)。
我正在尝试将堆栈跟踪记录到 Logstash 中。
日志堆栈是 ELK(ElasticSearch、Logstash、Kibana)。
生成日志的应用程序是一个 Java 应用程序,使用 slf4j
作为日志记录接口,log4j2
作为日志记录实现。
log4j2.xml
声明此 syslog
Appender,格式为 RFC5424
:
<Appenders>
<Syslog name="RFC5424" format="RFC5424" host="localhost" port="8514"
protocol="TCP" appName="MyApp" includeMDC="true" mdcId="mdc"
facility="LOCAL0" enterpriseNumber="18060" newLine="true"
messageId="Audit" id="App">
<LoggerFields>
<KeyValuePair key="thread" value="%t"/>
<KeyValuePair key="priority" value="%p"/>
<KeyValuePair key="category" value="%c"/>
<KeyValuePair key="exception" value="%ex{full}"/>
</LoggerFields>
</Syslog>
</Appenders>
我从 Java 应用程序中记录了一个 Throwable,如下所示:
org.slf4j.LoggerFactory.getLogger("exception_test").error("Testing errors", new RuntimeException("Exception message"));
记录异常时,Logstash 会跟踪类似这样的内容以显示它持续存在的内容:
{
"@timestamp":"2016-11-08T11:08:10.387Z",
"port":60397,
"@version":"1",
"host":"127.0.0.1",
"message":"<131>1 2016-11-08T11:08:10.386Z MyComputer.local MyApp - Audit [mdc@18060 category=\"exception_test\" exception=\"java.lang.RuntimeException: Exception message",
"type":"syslog",
"tags":[
"_grokparsefailure"
]
}
并且我确认 Kibana 在其日志条目之一的 _source
字段中显示完全相同的 JSON。
这里有一个问题:没有保存堆栈跟踪。消息 "Testing errors" 丢失了。
"tags":["_grokparsefailure"]
很不幸,但 与此问题无关。
我尝试添加 <ExceptionPattern/>
看看它是否会改变什么:
<Syslog name="RFC5424" format="RFC5424" host="localhost" port="8514"
protocol="TCP" appName="MyApp" includeMDC="true" mdcId="mdc"
facility="LOCAL0" enterpriseNumber="18060" newLine="true"
messageId="Audit" id="App">
<LoggerFields>
<KeyValuePair key="thread" value="%t"/>
<KeyValuePair key="priority" value="%p"/>
<KeyValuePair key="category" value="%c"/>
<KeyValuePair key="exception" value="%ex{full}"/>
</LoggerFields>
<ExceptionPattern>%ex{full}</ExceptionPattern>
</Syslog>
<ExceptionPattern/>
替换了日志消息,并且 也 (遗憾地)省略了所有 loggerFields
。但它确实给了我一个 class 名称和行号:
{
"@timestamp":"2016-11-08T11:54:03.835Z",
"port":60397,
"@version":"1",
"host":"127.0.0.1",
"message":"at com.Whosebug.LogTest.throw(LogTest.java:149)",
"type":"syslog",
"tags":[
"_grokparsefailure"
]
}
同样:没有堆栈跟踪。再一次:消息 "Testing errors" 丢失了。
如何使用 log4j2
将堆栈跟踪记录到 Logstash 中?我不一定要使用 syslog
appender。
基本上约束是:
- 不被锁定到任何特定的日志记录基础设施(这就是我使用 syslog 的原因)
- 多行堆栈跟踪需要理解为单个日志条目。 "each line of the stack trace" 是 "a separate log message" 是不可取的
- 堆栈跟踪必须能够进行过滤。我的一个典型异常可能有一页长的堆栈跟踪。我想过滤掉像 Spring. 这样的帧
Log4j 2.5 的 SyslogAppender 可以仅通过 UDP.
发送堆栈跟踪<Syslog name="RFC5424" format="RFC5424" host="localhost" port="8514"
protocol="UDP" appName="MyApp" includeMDC="true" mdcId="mdc"
facility="LOCAL0" enterpriseNumber="18060" newLine="true"
messageId="LogTest" id="App">
<LoggerFields>
<KeyValuePair key="thread" value="%t"/>
<KeyValuePair key="priority" value="%p"/>
<KeyValuePair key="category" value="%c"/>
<KeyValuePair key="exception" value="%ex{full}"/>
</LoggerFields>
<ExceptionPattern>%ex{full}</ExceptionPattern>
</Syslog>
使用 UDP:ExceptionPattern
和 LoggerFields.KeyValuePair["exception"]
都开始作为多行堆栈跟踪的解决方案工作。
这是当我通过系统日志通过 UDP 发送异常时 logstash
打印的内容:
{
"@timestamp" => 2016-11-14T13:23:38.304Z,
"@version" => "1",
"host" => "127.0.0.1",
"message" => "<131>1 2016-11-14T13:23:38.302Z BirchBox.local MyApp - LogTest [mdc@18060 category=\"com.Whosebug.Deeply\" exception=\"java.lang.RuntimeException: Exception message\n\tat com.Whosebug.Deeply.complain(Deeply.java:10)\n\tat com.Whosebug.Nested.complain(Nested.java:8)\n\tat com.Whosebug.Main.main(Main.java:20)\n\" priority=\"ERROR\" thread=\"main\"] Example error\njava.lang.RuntimeException: Exception message\n\tat com.Whosebug.Deeply.complain(Deeply.java:10)\n\tat com.Whosebug.Nested.complain(Nested.java:8)\n\tat com.Whosebug.Main.main(Main.java:20)",
"type" => "syslog",
"tags" => [
[0] "_grokparsefailure"
]
}
在 [mdc@18060 exception=\"…\"]
中,我们得到 LoggerFields.KeyValuePair["exception"]
堆栈跟踪。
除此之外:由于 ExceptionPattern
.
供参考:这是当我通过系统日志通过 TCP 发送异常时 logstash
打印的内容(即与上述相同的 SyslogAppender,但 protocol="TCP"
代替):
{
"@timestamp" => 2016-11-14T19:56:30.293Z,
"port" => 63179,
"@version" => "1",
"host" => "127.0.0.1",
"message" => "<131>1 2016-11-14T19:56:30.277Z BirchBox.local MyApp - Audit [mdc@18060 category=\"com.Whosebug.Deeply\" exception=\"java.lang.RuntimeException: Exception message",
"type" => "syslog",
"tags" => [
[0] "_grokparsefailure"
]
}
{
"@timestamp" => 2016-11-14T19:56:30.296Z,
"port" => 63179,
"@version" => "1",
"host" => "127.0.0.1",
"message" => "at com.Whosebug.Deeply.complain(Deeply.java:10)",
"type" => "syslog",
"tags" => [
[0] "_grokparsefailure"
]
}
{
"@timestamp" => 2016-11-14T19:56:30.296Z,
"port" => 63179,
"@version" => "1",
"host" => "127.0.0.1",
"message" => "at com.Whosebug.Nested.complain(Nested.java:8)",
"type" => "syslog",
"tags" => [
[0] "_grokparsefailure"
]
}
{
"@timestamp" => 2016-11-14T19:56:30.296Z,
"port" => 63179,
"@version" => "1",
"host" => "127.0.0.1",
"message" => "at com.Whosebug.Main.main(Main.java:20)",
"type" => "syslog",
"tags" => [
[0] "_grokparsefailure"
]
}
{
"@timestamp" => 2016-11-14T19:56:30.296Z,
"port" => 63179,
"@version" => "1",
"host" => "127.0.0.1",
"message" => "\" priority=\"ERROR\" thread=\"main\"] Example error",
"type" => "syslog",
"tags" => [
[0] "_grokparsefailure"
]
}
{
"@timestamp" => 2016-11-14T19:56:30.296Z,
"port" => 63179,
"@version" => "1",
"host" => "127.0.0.1",
"message" => "java.lang.RuntimeException: Exception message",
"type" => "syslog",
"tags" => [
[0] "_grokparsefailure"
]
}
{
"@timestamp" => 2016-11-14T19:56:30.297Z,
"port" => 63179,
"@version" => "1",
"host" => "127.0.0.1",
"message" => "at com.Whosebug.Deeply.complain(Deeply.java:10)",
"type" => "syslog",
"tags" => [
[0] "_grokparsefailure"
]
}
{
"@timestamp" => 2016-11-14T19:56:30.298Z,
"port" => 63179,
"@version" => "1",
"host" => "127.0.0.1",
"message" => "at com.Whosebug.Nested.complain(Nested.java:8)",
"type" => "syslog",
"tags" => [
[0] "_grokparsefailure"
]
}
{
"@timestamp" => 2016-11-14T19:56:30.298Z,
"port" => 63179,
"@version" => "1",
"host" => "127.0.0.1",
"message" => "at com.Whosebug.Main.main(Main.java:20)",
"type" => "syslog",
"tags" => [
[0] "_grokparsefailure"
]
}
{
"@timestamp" => 2016-11-14T19:56:30.299Z,
"port" => 63179,
"@version" => "1",
"host" => "127.0.0.1",
"message" => "",
"type" => "syslog",
"tags" => [
[0] "_grokparsefailure"
]
}
看起来 TCP 实际上 "work",但将单个日志消息拆分为 许多 系统日志消息(例如,当遇到 \n
时)。