使用 ELK 堆栈进行应用程序日志记录

Posted

技术标签:

【中文标题】使用 ELK 堆栈进行应用程序日志记录【英文标题】:Application logging with ELK stack 【发布时间】:2017-04-09 23:35:34 【问题描述】:

使用 NLog 和 Elasticsearch target 将日志转发到 AWS Elasticsearch as a Service 集群,以便在 Kibana 中进行可视化。

这工作正常,但由于 ES 集群可用性和集群故障转移的影响,当日志通过 HTTP 使用 elasticsearch-net client 发送时,我担心在生产中使用它。

我正在考虑为 NLog 使用不同的目标,将日志发送到更可靠的目的地(文件、S3?),然后让其他东西(Logstash、AWS Lambda)接收它们并将它们发送到 ES,这样可以最大限度地减少应用程序本身的风险。

想听听你的想法

更新

主要关注的是应用可用性,并使用辅助目标来防止丢失日志。

Using latest NLog 和 throwExceptions 设置为 false,此时不使用异步目标,但考虑到这一点,因为我们有很多异步代码。

为了提供更多上下文,“应用程序”是一组 API(WebAPI 和 WCF),它们的转速为 10 - 15K RPM。

场景

请求进来,ES集群不可用。

案例 1 - 没有异步目标的 NLog

<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.nlog-project.org/schemas/NLog.xsd NLog.xsd"
        autoReload="true"
        throwExceptions="false"
        internalLogLevel="Off"
        internalLogFile="c:\temp\nlog-internal.log">

    <targets>
      <target name="elastic"
              xsi:type="BufferingWrapper"
              flushTimeout="5000">
        <target xsi:type="ElasticSearch"
                layout="$logger | $threadid | $message"
                index="logstash-$date:format=yyyy.MM.dd"
                includeAllProperties="true"
                uri="...">

          <field name="user"
                 layout="$windows-identity:userName=True:domain=False"/>
          <field name="host"
                 layout="$machinename"/>
          <field name="number"
                 layout="1"
                 layoutType="System.Int32"/>

        </target>
      </target>
    </targets>
    <rules>
      <logger name="*"
              minlevel="Debug"
              writeTo="elastic" />
    </rules>
  </nlog>

问:

当无法达到目标时,主线程会发生什么?

案例 2 - 带有异步目标的 NLog

对带有 queueLimit="10000" batchSize="100" 的弹性搜索目标使用异步包装器

问:

是否创建了另一个线程[B]? 后续请求是否会重用线程 [B] 并对日志记录请求进行排队? 达到 queueLimit 时会发生什么? 是否会启动其他线程 [B1 ... Bn]? (这将淹没连接池)

【问题讨论】:

【参考方案1】:

好问题。

没什么好担心的,但是正确配置 NLog 很重要。

不确定什么应该是可靠的,运行程序或不丢失日志消息,所以对于这些情况:

如果你害怕丢失一些日志消息

写入多个目标(来自 NLog),例如文件 Elasticsearch。 可选,使用 fallbackgroupwrapper(以防写入目标时出错) 如果启用异步,check the overflow/queue settings - 默认启用丢弃(以防止 CPU 或内存过载)

如果您担心日志记录会破坏您的应用程序:

使用最新的稳定版 NLog 不要启用throwExceptions(默认禁用) 如果您启用async,错误将写入另一个线程中的目标,因此它不会破坏您的应用程序。 当使用async、check the overflow and queue settings

更新

案例一,

当无法达到目标时,主线程会发生什么?

什么都没有。主程序将消息排入缓冲区。另一个 (Timer) 线程正在处理这些消息。如果这将失败,并且throwException 未启用,则只会将错误写入 internalLog(启用时)。所有异常都会被捕获。写入目标失败时,您将丢失消息。

案例 2,

是否创建了另一个线程[B]?

将创建一个Timer。这将创建一个线程来处理消息。

后续请求是否会重用线程 [B] 并将日志记录请求排队?

是的,但不能保证它会是同一个线程。计时器将从池中创建一个线程。注意:只有一个线程会同时处于活动状态。

达到 queueLimit 时会发生什么?

取决于您的配置。默认情况下,它会默认丢弃,如上所述。见check the overflow/queue settings。就内存和 CPU 而言,这是最安全的选择。您可以选择丢弃、阻塞(停止主线程)或增加队列(通过了解内存使用情况)。

会启动额外的线程 [B1 ... Bn] 吗? (这将淹没连接池)

没有。 1个定时器,1个线程池。详情请查看MSDN page for Timer 或reference source。

【讨论】:

没有考虑 throwExceptions 标志。感谢您指出了这一点!我已经更新了问题,你能再看看吗?

以上是关于使用 ELK 堆栈进行应用程序日志记录的主要内容,如果未能解决你的问题,请参考以下文章

使用 ELK 堆栈的最佳 Docker 日志记录架构

精通springcloud:分布式日志记录和跟踪使用,ELK Stack集中日志

ini 使用ELK堆栈(Elasticsearch + Logstash + Kibana)进行脱机SSHD日志分析

基于弹性堆栈(ELK堆栈)的日志分析存储及展示

在 Kubernetes 中运行 ELK 堆栈的 Filebeat 不会在日志中捕获 pod 名称

ELK 堆栈中的 REDIS 有啥意义?