Beats: 使用 Filebeat 进行日志结构化 - Python

145 篇文章 29 订阅
90 篇文章 73 订阅

结构化日志背后的想法很简单:让应用程序直接编写 JSON 对象,而不是让应用程序将需要通过正则表达式解析的日志写入到你索引到 Elasticsearch 的 JSON 对象中。

 举例来说,假设你正在编写 Python Web 应用程序,并且正在使用标准库进行记录。 用户登录后,你可能会使用如下所示的日志记录语句:

createlogs.py

import logging

user = {
  "name": "liuxg",
  "id": "1"
}

session_id = "91e5b9d"

logging.basicConfig( filename="test.log", level=logging.DEBUG )

logging.debug( "User '{}' (id: {}) successfully logged in. Session id: {}"
               .format(user["name"], user["id"], session_id) )

logging.debug("User '{}' (id: {}) changed state to verified."
              .format(user["name"], user["id"]))

 上面的 python 应用将生成如下的日志信息:

DEBUG:root:User 'liuxg' (id: 1) successfully logged in. Session id: 91e5b9d
DEBUG:root:User 'liuxg' (id: 1) changed state to verified.

我们知道 logging 有不同的 level。我们可以通过配置 level 为我们提供 logging 提供输出控制:

logging_test.py

import logging

logging.basicConfig( filename="test.log", level=logging.INFO )

logging.debug("debug")
logging.info("info")
logging.warning("warning")
logging.error("error")
logging.critical("critical")

如上所示,我们定义了 level 为 INFO,也就是说低于 info 的 logging 就不会被输出。运行上面的代码:

$ cat test.log 
INFO:root:info
WARNING:root:warning
ERROR:root:error
CRITICAL:root:critical

从上面的输出中,我们可以看出来,由于 debug 的级别比 info 低,所以,我们看不到 debug 的输出。自从 printf 以来,这种日志记录方法就很流行。那时,你通常只有一台服务器,并且会拖尾并 grep 日志文件,一切都很好。

但是,时代已经变了,如今,你很有可能拥有数十,数百或数千个服务器/虚拟机/容器来创建大量日志,因此你将它们集中在  Elasticsearch 中并使用其神奇的功能(这就是它的感受)搜索和汇总功能以在它们之间进行导航。

只有把上面的数据进行结构化处理,才能在 Elasticsearch 中发挥更好的效效果,因此你可以在各个字段上进行搜索和汇总。因此,在建立索引之前,你通常会使用 Logstash 出色的 Grok 过滤器将应用程序日志解析为 JSON 对象,但这意味着您必须编写和维护 Grok 模式并花费 CPU 周期来进行解析。当然你也可以使用 Filebeat 结合 pipeline processors 来完成这项工作。对于还不是很熟悉的开发者来说,请参阅我之前的文字:

现在,让我们尝试使用结构化日志记录的相同示例。尽管通常不在标准库中,但是所有主要的编程语言都具有使结构化日志记录变得容易的库。 James Turnbull 在他的博客文章中创建了一个列表,其中还详细介绍了如何为 Rails 应用程序执行此操作。在 Python 中,有一个 structlog 库,我们将在这里使用它。

我们可以使用如下的方法来进行安装:

pip2 install structlog

如果你想在开发环境中看到彩色的输出,那么你可以使用如下的方法进行安装:

pip2 install structlog colorama

我们使用同样分方法,来进行生产日志:

createlogs_1.py

import logging
import structlog

user = {
  "name": "liuxg",
  "id": "1"
}

session_id = "91e5b9d"

log = structlog.get_logger()

log = log.bind(user='arthur', id=42, verified=False)
log.msg('logged_in')
log.msg('changed_state', verified=True)

上面的应用将生成如下的日志:

2020-06-11 14:50.41 logged_in                      id=42 user=arthur verified=False
2020-06-11 14:50.41 changed_state                  id=42 user=arthur verified=True

需要注意的一件事是,代码的重复性较低,它鼓励开发人员包括所有数据,而不是仅在编写代码时才包含重要的数据。 还要注意,以这种格式,对于开发人员来说,日志行仍然相当容易遵循。 但是,在投入生产时,使用 JSON 渲染器会更有意义:

createlogs_2.py

import logging
import structlog
from structlog import wrap_logger, PrintLogger, wrap_logger
from structlog.processors import JSONRenderer

file = open('json_logs', 'w')
log = wrap_logger(PrintLogger(file), processors=[JSONRenderer()])

log = log.bind(user_name='arthur', id=42, verified=False)
log.msg('logged_in')
log.msg('changed_state', verified=True)

file.close

上面的应用运行后,生产一个叫做 json_logs 的文件:

{"user_name": "arthur", "id": 42, "verified": false, "event": "logged_in"}
{"user_name": "arthur", "id": 42, "verified": true, "event": "changed_state"}

这种 JSON 格式的文件人眼难以理解,但是具有的优点是,数据已经按照 Elasticsearch 喜欢的格式进行了结构化。

Filebeat 是一个用 Go 语言编写的开源日志传送器,可以将日志行发送到 Logstash 和 Elasticsearch。 它提供了“至少一次”保证的数据传输,因此你永远不会丢失日志行,并且它使用了背压敏感协议,因此不会使你的管道过载。 还包括基本过滤和多行关联。

如果你的日志就像上面的示例一样,Filebeat 每行存储一个JSON对象,它还可以本地解码 JSON 对象。

这是一个示例配置文件,该文件配置Filebeat来拾取文件并将 JSON 对象发送到 Elasticsearch:

filebeat_json.yml
 

filebeat.inputs:
- type: log
  enabled: true
  tags: ["i", "love", "json"]
  json.message_key: event
  json.keys_under_root: true
  json.add_error_key: true
  fields:
    planet: liuxg
  paths:
    - /Users/liuxg/python/logs/json_logs

output.elasticsearch:
  hosts: ["localhost:9200"]
  index: "json_logs1"

setup.ilm.enabled: false
setup.template.name: json_logs1
setup.template.pattern: json_logs1

在运行 Filebeat 之前,我们可以在 Kibana 中执行如下的命令:

PUT json_logs1
{
  "mappings": {
    "properties": {
      "event": {
        "type": "keyword"
      },
      "id": {
        "type": "long"
      },
      "user_name": {
        "type": "keyword"
      },
      "verified": {
        "type": "boolean"
      }
    }
  }
}  

在这里,我们定义这个索引的 mapping。

我们接着执行运行 Filebeat:

./filebeat -e -c ~/python/logs/filebeat_json.yml

那么在我们的 Kibana 中,我们可以查询到最新生成的文档:

GET json_logs1/_search
{
  "took" : 0,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 2,
      "relation" : "eq"
    },
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "json_logs1",
        "_type" : "_doc",
        "_id" : "uw-jonIBB4HethT_mOIZ",
        "_score" : 1.0,
        "_source" : {
          "@timestamp" : "2020-06-11T09:08:48.550Z",
          "user_name" : "arthur",
          "verified" : false,
          "tags" : [
            "i",
            "love",
            "json"
          ],
          "fields" : {
            "planet" : "liuxg"
          },
          "ecs" : {
            "version" : "1.5.0"
          },
          "agent" : {
            "ephemeral_id" : "0c9b96dd-76c8-45c5-96ef-00859f9e12dc",
            "hostname" : "liuxg",
            "id" : "be15712c-94be-41f4-9974-0b049dc95750",
            "version" : "7.7.0",
            "type" : "filebeat"
          },
          "id" : 42,
          "event" : "logged_in",
          "log" : {
            "offset" : 0,
            "file" : {
              "path" : "/Users/liuxg/python/logs/json_logs"
            }
          },
          "input" : {
            "type" : "log"
          },
          "host" : {
            "name" : "liuxg"
          }
        }
      },
      {
        "_index" : "json_logs1",
        "_type" : "_doc",
        "_id" : "vA-jonIBB4HethT_mOIZ",
        "_score" : 1.0,
        "_source" : {
          "@timestamp" : "2020-06-11T09:08:48.551Z",
          "event" : "changed_state",
          "input" : {
            "type" : "log"
          },
          "log" : {
            "offset" : 75,
            "file" : {
              "path" : "/Users/liuxg/python/logs/json_logs"
            }
          },
          "user_name" : "arthur",
          "id" : 42,
          "verified" : true,
          "tags" : [
            "i",
            "love",
            "json"
          ],
          "fields" : {
            "planet" : "liuxg"
          },
          "ecs" : {
            "version" : "1.5.0"
          },
          "host" : {
            "name" : "liuxg"
          },
          "agent" : {
            "version" : "7.7.0",
            "type" : "filebeat",
            "ephemeral_id" : "0c9b96dd-76c8-45c5-96ef-00859f9e12dc",
            "hostname" : "liuxg",
            "id" : "be15712c-94be-41f4-9974-0b049dc95750"
          }
        }
      }
    ]
  }
}

如你所见,Filebeat 自动添加一个时间戳。 请注意,这是读取日志行的时间,可能与应用程序写入日志行的时间不同。 如果需要更好的准确性,可以设置 structlog 库以生成时间戳。

Filebeat 还会自动添加一些元数据(例如主机名),并使其易于通过配置文件添加自定义字段和标签。 这意味着应用程序不必担心从环境中添加元数据。

这就是你所需要的。 简单的事情应该很简单:-)

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 15
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值