使用 append 而不是 LogstashMarker 的 appendRaw 时丢失日志消息
Lost log messages when using append instead of appendRaw of LogstashMarker
我在我们的环境中偶然发现了丢失的日志消息。
我们在 Java 应用程序中使用 ELK Stack 和带有 logstash-logback-encoder 的 logback。为了进行结构化查询,我们使用 LogstashMarkers 将结构化参数附加到日志中。在丢失日志消息的情况下,它是一个字符串,其中包含 JSON.
部分日志消息完全丢失。不仅标记没有保存,而且完整的日志消息也没有保存。它们在 Kibana 中不可见。我们的应用程序或 logstash 服务器的日志中没有错误。
我可以用最少的代码创建一个示例存储库并将其发布在 GitHub:https://github.com/seism0saurus/logging-issue
为了运行它,你需要一个logstash服务器和netcat。
该项目中的记录器配置为使用控制台附加程序,因此我们有一个参考,真正记录了哪些消息。
此外,还有一个用于 localhost:8081 上的 netcat 服务器的附加程序和一个用于 logstash 服务器的附加程序。两者具有相同的配置。只是地址不一样。所以我很确定消息被发送到 logstash 服务器,如果它被发送到 netcat。
这里是主要代码 class:
@SpringBootApplication
public class LoggingIssueApplication {
private static final Logger LOGGER = getLogger("customLogger");
private static final String MESSAGE = "{ \"ordernumber\": \"Test1\", \"shippingmethod\": \"homeDelivery\", \"customer\": { \"id\": \"1337\", \"salutation\": \"Mr\", \"name\": \"seism0saurus\", \"birthdate\": \"1900-05-01\", \"email\": \"test@seism0saurus.de\", \"phone\": \"\" }, \"billingAddress\": { \"name\": \"seism0saurus\", \"address\": \"Teststreet 5\", \"address2\": \"\", \"postcode\": \"90451\", \"city\": \"Nürnberg\", \"country\": \"Deutschland\", \"countrycode\": \"DEU\" }, \"shippingAddress\": { \"name\": \"seism0saurus\", \"address\": \"Teststreet 5\", \"address2\": \"\", \"postcode\": \"90451\", \"city\": \"Nürnberg\", \"country\": \"Deutschland\", \"countrycode\": \"DEU\" }, \"totalItems\": 1, \"items\": [ { \"position\": 53, \"bundlenumber\": 53, \"itemnumber\": \"900508\", \"itemdescription\": \"Dinosaur Cookies\", \"quantity\": 4, \"saleType\": \"KL\", \"saleItemType\": \"\", \"grossAmount\": 40.0, \"discounts\": [ ] } ]}";
public static void main(String[] args) {
SpringApplication.run(LoggingIssueApplication.class, args);
}
@EventListener(ApplicationReadyEvent.class)
public void testAppender() throws InterruptedException {
LOGGER.warn("1 - Log without marker and message as parameter: {}", MESSAGE);
LogstashMarker payloadMarker = Markers.append("payload", MESSAGE);
LOGGER.warn(payloadMarker, "2 - Log with payload marker without parameter");
LOGGER.warn(payloadMarker, "3 - Log with payload marker and message as parameter: {}", MESSAGE);
LogstashMarker rawMarker = Markers.appendRaw("raw", MESSAGE);
LOGGER.warn(rawMarker, "4 - Log with raw marker without parameter");
//Needed, so the loggers can finish their job before the application is shut down.
Thread.sleep(5000);
}
}
这是 logstash 附加程序:
<appender name="nc" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
<destination>localhost:8081</destination>
<keepAliveDuration>5 minutes</keepAliveDuration>
<listener class="net.logstash.logback.appender.listener.FailureSummaryLoggingAppenderListener"/>
<encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
<providers>
<mdc/>
<context/>
<version/>
<logLevel/>
<logLevelValue/>
<loggerName/>
<pattern>
<pattern>
{
"serviceName": "logging-issue",
"environment": "dev",
"errorMessage": "%ex{0}"
}
</pattern>
</pattern>
<threadName/>
<message/>
<logstashMarkers/>
<arguments/>
<stackTrace/>
</providers>
</encoder>
</appender>
当我 运行 应用程序时,我收到了预期的 4 条日志消息。所有 4 个都出现在控制台和 netcat 服务器中。但是只有1号和4号出现在kibana中。
因此,我假设在处理 Markers.append
和 Markers.appendRaw
时存在差异。 MESSAGE
是一个包含 JSON 的字符串。但我希望,我不必检查字符串的内容来决定调用哪个方法。
logstash 服务器的日志中没有错误消息。
它似乎不是连接错误、未知或无法访问的主机或已满缓冲区。我重现了所有这些案例,它们在日志中留下了有意义的错误消息。
你知道吗,这里发生了什么?我是否必须检查要放入标记的字符串的内容以防止丢失完整的日志消息?
编辑:
我得到了有关该问题的更多信息。我本地机器上 docker 内的一个 logstash 实例确实接收所有日志并使用以下管道将它们写入文件。
input {
tcp {
port => 9000
codec => json_lines
}
}
output {
file {
path => "/mounted_dir/logstash.out"
codec => line {
format => "%{message}"
}
}
}
编辑 2:
我在https://jaxenter.de/elastic-stack-containern-docker-86374的基础上搭建了一个完整的ELK栈来做对比。它不会丢失任何日志消息。
因此,我们的管理员构建了 ELK 堆栈的问题所在。你有什么想法吗?
我们发现了问题。为了帮助遇到这个问题的其他人,我将在这里解释问题和解决方案。
日志消息已成功传输到 logstash 和 elasticsearch。所以这些日志中没有错误。
我们找不到日志条目的原因是“有效负载”字段。我们有多项服务,其中一项是发送对象而不是字符串。这导致映射冲突并且无法访问日志消息。
我们的解决方案是定义显式映射并引入命名约定。现在我们有一个文本字段 payload 和一个对象字段 payloadAsObject。我们的所有服务都使用此约定。
过去有问题的日志条目已经足够多了,所以我们可以删除旧索引并解决冲突。但是有可能在不停机的情况下重新索引数据。例如这个:https://medium.com/craftsmenltd/rebuild-elasticsearch-index-without-downtime-168363829ea4
我在我们的环境中偶然发现了丢失的日志消息。
我们在 Java 应用程序中使用 ELK Stack 和带有 logstash-logback-encoder 的 logback。为了进行结构化查询,我们使用 LogstashMarkers 将结构化参数附加到日志中。在丢失日志消息的情况下,它是一个字符串,其中包含 JSON.
部分日志消息完全丢失。不仅标记没有保存,而且完整的日志消息也没有保存。它们在 Kibana 中不可见。我们的应用程序或 logstash 服务器的日志中没有错误。
我可以用最少的代码创建一个示例存储库并将其发布在 GitHub:https://github.com/seism0saurus/logging-issue
为了运行它,你需要一个logstash服务器和netcat。 该项目中的记录器配置为使用控制台附加程序,因此我们有一个参考,真正记录了哪些消息。 此外,还有一个用于 localhost:8081 上的 netcat 服务器的附加程序和一个用于 logstash 服务器的附加程序。两者具有相同的配置。只是地址不一样。所以我很确定消息被发送到 logstash 服务器,如果它被发送到 netcat。
这里是主要代码 class:
@SpringBootApplication
public class LoggingIssueApplication {
private static final Logger LOGGER = getLogger("customLogger");
private static final String MESSAGE = "{ \"ordernumber\": \"Test1\", \"shippingmethod\": \"homeDelivery\", \"customer\": { \"id\": \"1337\", \"salutation\": \"Mr\", \"name\": \"seism0saurus\", \"birthdate\": \"1900-05-01\", \"email\": \"test@seism0saurus.de\", \"phone\": \"\" }, \"billingAddress\": { \"name\": \"seism0saurus\", \"address\": \"Teststreet 5\", \"address2\": \"\", \"postcode\": \"90451\", \"city\": \"Nürnberg\", \"country\": \"Deutschland\", \"countrycode\": \"DEU\" }, \"shippingAddress\": { \"name\": \"seism0saurus\", \"address\": \"Teststreet 5\", \"address2\": \"\", \"postcode\": \"90451\", \"city\": \"Nürnberg\", \"country\": \"Deutschland\", \"countrycode\": \"DEU\" }, \"totalItems\": 1, \"items\": [ { \"position\": 53, \"bundlenumber\": 53, \"itemnumber\": \"900508\", \"itemdescription\": \"Dinosaur Cookies\", \"quantity\": 4, \"saleType\": \"KL\", \"saleItemType\": \"\", \"grossAmount\": 40.0, \"discounts\": [ ] } ]}";
public static void main(String[] args) {
SpringApplication.run(LoggingIssueApplication.class, args);
}
@EventListener(ApplicationReadyEvent.class)
public void testAppender() throws InterruptedException {
LOGGER.warn("1 - Log without marker and message as parameter: {}", MESSAGE);
LogstashMarker payloadMarker = Markers.append("payload", MESSAGE);
LOGGER.warn(payloadMarker, "2 - Log with payload marker without parameter");
LOGGER.warn(payloadMarker, "3 - Log with payload marker and message as parameter: {}", MESSAGE);
LogstashMarker rawMarker = Markers.appendRaw("raw", MESSAGE);
LOGGER.warn(rawMarker, "4 - Log with raw marker without parameter");
//Needed, so the loggers can finish their job before the application is shut down.
Thread.sleep(5000);
}
}
这是 logstash 附加程序:
<appender name="nc" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
<destination>localhost:8081</destination>
<keepAliveDuration>5 minutes</keepAliveDuration>
<listener class="net.logstash.logback.appender.listener.FailureSummaryLoggingAppenderListener"/>
<encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
<providers>
<mdc/>
<context/>
<version/>
<logLevel/>
<logLevelValue/>
<loggerName/>
<pattern>
<pattern>
{
"serviceName": "logging-issue",
"environment": "dev",
"errorMessage": "%ex{0}"
}
</pattern>
</pattern>
<threadName/>
<message/>
<logstashMarkers/>
<arguments/>
<stackTrace/>
</providers>
</encoder>
</appender>
当我 运行 应用程序时,我收到了预期的 4 条日志消息。所有 4 个都出现在控制台和 netcat 服务器中。但是只有1号和4号出现在kibana中。
因此,我假设在处理 Markers.append
和 Markers.appendRaw
时存在差异。 MESSAGE
是一个包含 JSON 的字符串。但我希望,我不必检查字符串的内容来决定调用哪个方法。
logstash 服务器的日志中没有错误消息。 它似乎不是连接错误、未知或无法访问的主机或已满缓冲区。我重现了所有这些案例,它们在日志中留下了有意义的错误消息。
你知道吗,这里发生了什么?我是否必须检查要放入标记的字符串的内容以防止丢失完整的日志消息?
编辑: 我得到了有关该问题的更多信息。我本地机器上 docker 内的一个 logstash 实例确实接收所有日志并使用以下管道将它们写入文件。
input {
tcp {
port => 9000
codec => json_lines
}
}
output {
file {
path => "/mounted_dir/logstash.out"
codec => line {
format => "%{message}"
}
}
}
编辑 2: 我在https://jaxenter.de/elastic-stack-containern-docker-86374的基础上搭建了一个完整的ELK栈来做对比。它不会丢失任何日志消息。 因此,我们的管理员构建了 ELK 堆栈的问题所在。你有什么想法吗?
我们发现了问题。为了帮助遇到这个问题的其他人,我将在这里解释问题和解决方案。
日志消息已成功传输到 logstash 和 elasticsearch。所以这些日志中没有错误。
我们找不到日志条目的原因是“有效负载”字段。我们有多项服务,其中一项是发送对象而不是字符串。这导致映射冲突并且无法访问日志消息。
我们的解决方案是定义显式映射并引入命名约定。现在我们有一个文本字段 payload 和一个对象字段 payloadAsObject。我们的所有服务都使用此约定。
过去有问题的日志条目已经足够多了,所以我们可以删除旧索引并解决冲突。但是有可能在不停机的情况下重新索引数据。例如这个:https://medium.com/craftsmenltd/rebuild-elasticsearch-index-without-downtime-168363829ea4