0

我在我们的环境中偶然发现了丢失的日志消息。

我们在 Java 应用程序中使用 ELK 堆栈和 logback 和 logstash-logback-encoder。为了进行结构化查询,我们使用 LogstashMarkers 将结构化参数附加到日志中。在丢失日志消息的情况下,它是一个包含 JSON 的字符串。

一些日志消息完全丢失。不仅没有保存标记,还保存了完整的日志消息。它们在 Kibana 中不可见。我们的应用程序或 logstash 服务器的日志中没有错误。

我可以用最少的代码创建一个示例存储库并将其发布在 GitHub 上:https ://github.com/seism0saurus/logging-issue

要运行它,您需要一个 logstash 服务器和 netcat。该项目中的记录器配置为使用控制台附加程序,因此我们有一个参考,即真正记录了哪些消息。此外,还有一个用于 localhost:8081 上的 netcat 服务器的附加程序和一个用于 logstash 服务器的附加程序。两者具有相同的配置。只是地址不同。所以我很确定消息会发送到logstash服务器,如果它是发送到netcat的话。

下面是主类的代码:

@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.appendMarkers.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 堆栈的问题所在。你有什么想法?

4

1 回答 1

0

我们发现了问题。为了帮助遇到这个问题的其他人,我将在这里解释问题和解决方案。

日志消息已成功传输到 logstash 和 elasticsearch。所以这些日志中没有错误。我们找不到日志条目的原因是“有效负载”字段。我们有多个服务,其中一个是发送一个对象而不是一个字符串。这会导致映射冲突,并且无法访问日志消息。错误信息

我们的解决方案是定义一个显式映射并引入一个命名约定。现在我们有一个文本字段payload和一个对象字段payloadAsObject。我们的所有服务都使用此约定。

过去有问题的日志条目已经足够远了,所以我们可以删除旧索引并解决冲突。但是有可能在不停机的情况下重新索引数据。例如这个:https ://medium.com/craftsmenltd/rebuild-elasticsearch-index-without-downtime-168363829ea4

于 2021-03-03T08:32:20.433 回答