在将完整日志发送到logstash / elasticsearch之前将半日志发送到RabbitMQ的方法

时间:2015-09-27 17:24:40

标签: elasticsearch rabbitmq logstash apache-kafka logstash-forwarder

我有几个函数,每个函数都创建特定于一个事务的日志;它是一个多线程应用程序,因此func1的函数入口可以是随机的事务,但对于单个事务,它将仅通过func1,func2和func3顺序。

func1(transactionId) {
     log("%d Now in func1", transactionId);
}

func2(transactionId) {
     log("%d Now in func2", transactionId);
}

func3(transactionId) {
     log("%d Now in func3", transactionId);
}

现在,我想一次只为每个事务写入logstash;

 1 Now in func1 Now in func2 Now in fun3

然后这需要最终进入elasticsearch;

我正在考虑将一半事务日志写入RabbitMQ临时队列,然后在完成完成事务后,我将其提交给RabbitMQ生成器队列以将消息发送到logstash;

func1(transactionId) {
     add2RMQ(transactionId, "Now in func1");
}

func2(transactionId) {
     add2RMQ("transactionId, "Now in func2");
}

func3(transactionId) {
      add2RMQ("transactionId, "Now in func3");
      /* Last point of transaction */
      commit2RMQ(transactionId);
}

commit2RMQ执行logstash的时间应该收到特定于要写入elasticsearch的事务的完整消息。

问题:

  1. 解决此问题的正确解决方案是将特定于事务的数据一次性发送到elasticsearch?
  2. 我们可以用RabbitMQ解决这个问题吗?如果是这样,我需要使用哪种API?
  3. 有没有什么方法可以在没有RabbitMQ的情况下实现相同目标,但只能使用logstash和elasticsearch?
  4. 我不想使用elasticsearch update API,因为它可能会为特定于事务的每条日志消息消耗大量搜索操作。

1 个答案:

答案 0 :(得分:3)

尝试聚合与单个事务相关的不同日志行并不是一个需要解决的简单问题,尤其是当您将消息队列系统添加到混合中作为要聚合的日志的中间存储时。我会采用不同的方式,不涉及像RabbitMQ这样的另一个子系统。

此外,如果您尝试将多个日志行连接成一个日志行,则会丢失每个日志行可以提供的细化信息,例如每个函数执行所花费的时间。如果func2分别func3引发异常,会发生什么?您是否应该仅存储仅由func1制成的部分日志,仅存储func1func2

我要编写的内容可能会转换为任何语言和任何日志记录解决方案,但为了说明的目的,我假设您的程序是用Java编写的,而您使用的是Log4J。

所以我会利用Log4J's Mapped Diagnostic Context(MDC)来存储您的每个日志行中的事务ID(以及可能的其他数据,如用户名等)。这样,您可以轻松检索与单个事务相关的所有日志行。这样做的好处是您不必聚合任何东西,只需提供足够的上下文信息,以便Kibana稍后可以为您完成。

在您的伪代码中,您将直接在邮件中添加交易ID。使用MDC而不是将ID记录到消息中的优点是,它可以帮助您解析Logstash中的所有消息,以重新发现创建日志行时已经知道的事务ID。

所以我的想法是,在你的代码中,只要你有一个事务ID,就可以将它添加到当前的每线程日志记录上下文中,如下所示:

import org.apache.log4j.MDC;

...
func1(transactionId) {
     // add the transaction ID to the logging context
     MDC.put("transactionID", transactionId);
     log("Now in func1");
}

func2(transactionId) {
     log("Now in func2");
}

func3(transactionId) {
     log("Now in func3");
}

然后在你的Log4J配置文件中,你可以使用%X{transactionID}模式指定appender来存储它,在这种情况下我只是在线程名称之后添加它,但你可以把它放在你的任何地方像:

log4j.appender.consoleAppender.layout.ConversionPattern = %d [%t] [%X{transactionID}] %5p %c - %m%n

您的日志看起来与此类似:

2015-09-28T05:07:28.425Z [http-8084-2] [625562271762]  INFO YourClass - Now in func1
2015-09-28T05:07:29.776Z [http-8084-2] [625562271762]  INFO YourClass - Now in func2
2015-09-28T05:07:30.652Z [http-8084-2] [625562271762]  INFO YourClass - Now in func3
                                              ^
                                              |
                                  the transaction ID is here

当你有这样的日志行时,通过Logstash grok过滤器检索事务ID并将其存储在logstash索引中自己的transactionID字段中是一件小事。在Kibana中,您可以搜索事务ID并按时间戳desc排序,并且您将显示该事务的所有上下文。

试一试!