使用 Fluentd 及 Elastic Stack 进行应用日志采集

原创
06/03 08:00
阅读数 63

导语



日志记录是任何应用程序中最重要的方面。每个应用程序都有不同风格的日志记录机制。但是,设计良好的日志记录机制对于系统管理员和开发人员来说是一个巨大的实用工具。日志对于调试影响你的服务的原因至关重要。


现在我们知道了在应用程序设计中收集日志的价值,我们将使用 Bunyan 库在 NodeJS 应用程序中实现日志记录。Bunyan 是一个用于 NodeJS 服务的简单快速的 JSON 日志库。

作为应用程序设计的日志记录还应该考虑诸如记录什么、何时以及记录多少,如何控制日志记录等问题。


例如:你不能仅为了调试,记录密码或任何敏感信息,这是使用日志记录的一种糟糕方式。日志记录具有 fatal、error、info、debug、warn、trace等级别。每个编程都有自己的级别,但这是主要所有日志库中可用的标准级别。

在我今天的练习中,我将使用如下的架构来部署:


我们在 Ubuntu OS 上部署 Fluentd。我们使用一个 nodejs 的应用程序来生产 JSON 格式的应用日志,并让 Fluentd 来收集并发送至 Elasticsearch。


Fluentd


Fluentd 是一个开源的数据收集器,它可以让你统一数据的收集和使用,以便更好地使用和理解数据。Fluentd 是一个广泛使用的用 Ruby 编写的工具用于收集日志并将其流式传输到第三方服务(如 loggly、Elasticsearch、mongo)以进行进一步处理。


Fluentd 提供了大量功能,我们将讨论其中的一些。



统一日志



JSON 格式的日志总是优于任何日志工具。Fluentd 尝试尽可能将数据结构化为 JSON:这允许 Fluentd 统一处理日志数据的所有方面:收集、过滤、缓冲和发送日志到多个来源和目的地。



可插拔架构



Fluentd 有一个灵活的插件系统,允许用户扩展其核心功能。用户可以编写自己的自定义插件,前提是它们应该用 ruby 编写。一些 Fluentd 插件是 fluentd-elasticsearch, fluentd-mongo, fluentd-splunk-hec, fluentd-kafka。



最少资源



因为它是用 C 和 Ruby 的组合编写的,并且需要很少的系统资源。Fluentd vanilla 实例在 30-40MB 内存上运行,可以处理 13,000 个事件/ps。



内置可靠性



Fluentd 支持基于内存和文件的缓冲,以防止数据节点间丢失。Fluentd 还支持强大的故障转移,并且可以设置为高可用性。


安装 Fluentd Linux (td-agent)


设置 Fluentd 是一个非常简单直接的过程。在安装之前,我们只会看看 Fluentd 和 td-agent 有什么区别。Fluentd 是一个用于统一数据记录层的开源数据收集器。Fluentd 是一个由 Treasure Data 制作和赞助的项目。Treasure Data 负责分发 Fluentd 的稳定版本,称为 td-agent。所以基本上这只是一个名字的不同,所有的内部部分仍然是 Fluentd。


我们可以到地址

https://docs.treasuredata.com/display/public/PD/Installing+TD+Toolbelt+and+Treasure+Agent

来查找针对 Linux 的安装:

curl -L https://docs.treasuredata.com/display/public/PD/Installing+TD+Toolbelt+and+Treasure+Agent | sh


我们可以使用如下的命令来进行启动:

sudo systemctl start td-agent


我们可以使用如下的命令来检查 td-agent 的运行状态:

systemctl status td-agent


或者:

service td-agent status


$ service td-agent status● td-agent.service - td-agent: Fluentd based data collector for Treasure Data     Loaded: loaded (/lib/systemd/system/td-agent.service; enabled; vendor pr>     Active: active (running) since Tue 2021-06-01 20:00:25 CST; 3s ago       Docs: https://docs.treasuredata.com/display/public/PD/About+Treasure+D>    Process: 155046 ExecStart=/opt/td-agent/bin/fluentd --log $TD_AGENT_LOG_F>   Main PID: 155068 (fluentd)      Tasks: 8 (limit: 18984)     Memory: 98.2M     CGroup: /system.slice/td-agent.service             ├─155068 /opt/td-agent/bin/ruby /opt/td-agent/bin/fluentd --log >             └─155071 /opt/td-agent/bin/ruby -Eascii-8bit:ascii-8bit /opt/td->


上面显示 active 状态,表明它的运行是正常的。


安装 Elastic Stack


在今天的练习中,我将使用 docker 来部署 Elasticsearch 及 Kibana。我创建了一个叫做 fluentd 的目录:

$ pwd/Users/liuxg/data/fluentd$ ls -altotal 16drwxr-xr-x    4 liuxg  staff   128 Jun  1 16:01 .drwxr-xr-x  134 liuxg  staff  4288 Jun  1 15:59 ..-rw-r--r--    1 liuxg  staff    29 Jun  1 16:01 .env-rw-r--r--    1 liuxg  staff   832 Jun  1 16:16 docker-compose.yml$ cat .envELASTIC_STACK_VERSION=7.13.0


上面 docker-compose.yml 的内容如下:

docker-compose.yml

version: '3.7'services:  elasticsearch:    image: docker.elastic.co/elasticsearch/elasticsearch:${ELASTIC_STACK_VERSION}    container_name: es01    environment:      - discovery.type=single-node      - network.host=0.0.0.0    ulimits:      memlock:        soft: -1        hard: -1    volumes:      - esdata01:/usr/share/elasticsearch/data            ports:      - 9200:9200    networks:      - elastic   kibana:    image: docker.elastic.co/kibana/kibana:${ELASTIC_STACK_VERSION}    container_name: kibana    ports: ['5601:5601']        networks: ['elastic']    environment:      - SERVER_NAME=kibana.localhost      - ELASTICSEARCH_HOSTS=http://elasticsearch:9200      - I18N_LOCALE=zh-CN    depends_on: ['elasticsearch'] volumes:  esdata01:    driver: local networks:  elastic:    driver: bridge


在上面需要注意的是:我需要让 Elasticsearch 被另外一台机器所访问,我做了这个设置 :

      - discovery.type=single-node      - network.host=0.0.0.0


它将使 Elasticsearch 绑定于所有的网路接口上,也即它可以同时被 localhost:9200 访问,也可以被 <private ip>:9200 所访问。


我们把 docker 运行起来,并打入在 docker-compose.yml 所在的目录打入如下的命令:

docker-compose up


这样我们的 Elasticsearch 及 Kibana 就会被运行起来。如果你还有问题,请参考我之前的文章 “用 Docker 部署 Elastic 栈”。


Nodejs server


我们在 Ubuntu 上安装好 nodejs 的环境,并创建如下的 NodeJS API Server:

server.js

const bunyan     = require('bunyan');const format     = require('bunyan-format');const express    = require('express');const bodyParser = require('body-parser')const FS         = require('fs');const _          = require('lodash'); global.logger = configureLogger(); const app = express(); // for logging requestconst logRequest = function (req, res, next) {  const start = new Date();  const end   = res.end;   res.end = function(payload, encoding){    let responseTime = Date.now() - start.getTime();    end.call(res, payload, encoding);    const contentLength = Number(res.getHeader('Content-Length'));    const _data = {      req: _.pick(req, ['method', 'url']),      res: _.pick(res, ['statusCode']),      responseTime: responseTime,      contentLength: isNaN(contentLength) ? 0 : contentLength    }    logger.info('%s %s %s %d %dms - %d bytes', (req.hostname || ''), _data.req.method, _data.req.url, _data.res.statusCode, _data.responseTime, _data.contentLength);      }  next();} app.use(logRequest);app.use(bodyParser.json()); function configureLogger(){  const logFile = './api.LOG';  const ws = FS.createWriteStream(logFile, { flags: 'a' });    // formatting options for stdout  const formatOut = format({ outputMode: 'short', levelInString: true, colorFromLevel: {    20: 'blue',    30: 'green',    40: 'yellow',    50: 'red',    60: 'brightRed'  }});      const fileFormatter = format({outputMode: 'bunyan', level: true }, ws);    const logger = bunyan.createLogger({   name: 'API Logger',    level: 'debug',   streams: [     {      level: 'debug',      stream: fileFormatter     },     {      level:  'info',       stream: fileFormatter     },     {      level: 'debug',       stream: process.stdout     }   ]  });    logger.log = function (message) {    logger.debug(message);  }  return logger;} app.listen(4044, () => {   logger.log("API Server Listening on PORT 4044");});


在运行时,我们需要安装相应的库。我们可以使用如下的命令来运行:

nodejs server.js


$ nodejs server.js {"name":"API Logger","hostname":"liuxgu","pid":155774,"level":20,"msg":"API Server Listening on PORT 4044","time":"2021-06-01T12:04:04.926Z","v":0}{"name":"API Logger","hostname":"liuxgu","pid":155774,"level":30,"msg":"ubuntu GET /favicon.ico 404 5ms - 150 bytes","time":"2021-06-01T12:04:09.389Z","v":0}


当我们从任何一个电脑上访问这个服务器时,我们可以看到不断增加的日志输出:


我们可以在当前运行的目录下找到一个叫做 api.LOG 的文件:

$ pwd/home/liuxg/nodejs/nodejs_logliuxg@liuxgu:~/nodejs/nodejs_log$ lsapi.LOG  index.js  node_modules  server.js


它里面包含的就是我们的应用输出日志。


配置 Fluentd


配置 Fluentd 或 td-agent 很简单,所有插件配置都写在 fluentd.conf 或 td-agent.conf 中。配置位置可以是:

/etc/td-agent/td-agent.conf


或者是:

/etc/fluentd/fluent.conf


由于我们使用 Elasticsearch 插件将数据发送到 Elasticsearch,因此我们需要 fluentd-plugin-elasticsearch 输出插件,你可以使用 gem 安装它:

sudo gem install fluent-plugin-elasticsearch


更多关于 Fluentd 插件输出插件。


我们可以把之前安装默认的 td-agent.conf 保存到另外一个文件中:

cd /etc/td-agent/mv td-agent.conf td-agent.conf-default


最终 td-agent.conf:

td-agent.conf

<system>  workers 1  log_level trace</system># logs for api<source>    @type tail    format json    tag cli.stdout    path /home/liuxg/nodejs/nodejs_log/api.LOG    pos_file /var/log/td-agent/tmp/cli.log.pos</source># send logs of application to Elasticsearch<match cli.stdout>  @type elasticsearch  host 192.168.0.3  port 9200  index_name fluentd.${tag}  flush_interval 5s</match>


请注意:你需要依据自己的 log 的位置修改上面的 path。你也需要根据自己的配置修改上面的 elasticsearch 的 IP 地址。经过上面的修改过后,你需要使用如下的命令来重新启动 fluentd:

sudo service td-agent restart


接下来,我们访问 nodejs 服务器的网址 <ip>:4044:


查看收集的日志



我们可以打开 Kibana,并查看最新的索引:

GET _cat/indices


我们可以看到:

yellow open logstash                            AUIwddr8SmuwE41h6Xdv6A 1 1  9651    0   3.6mb   3.6mbgreen  open .kibana_task_manager_7.13.0_001     TK3xQufNQgmgIct-A-7G-g 1 0    10 6997 798.5kb 798.5kbyellow open fluentd.cli.stdout                  GPNyDu1tSdOuEAzTcncWUg 1 1    15    0  25.7kb  25.7kbgreen  open .apm-custom-link                    QstaYgpPSlGaQZLAJ3lq8Q 1 0     0    0    208b    208bgreen  open .kibana-event-log-7.13.0-000001     vcUtY3K7T6aTrI7yXYDjUg 1 0     1    0   5.6kb   5.6kbgreen  open .apm-agent-configuration            Dmisg-peQ2OaEagejNzcxg 1 0     0    0    208b    208byellow open metricbeat-7.13.0-2021.06.01-000001 ckCl19l5TNmfrDtDzAe9rA 1 1 73050    0  60.7mb  60.7mbgreen  open .kibana_7.13.0_001                  pqYzeAQ9Sk2w_1mrRO2_Lw 1 0    23    8   2.1mb   2.1mb


在上面,我们可以看到一个叫做 fluentd.cli.stdout  的索引。我们可以通过如下的命令来查询该索引:

GET fluentd.cli.stdout/_search


上面命令显示的结果为:


我们可以看到被收集的日志。


正文完



 作者:刘晓国

本文编辑:喝咖啡的猫



嗨,互动起来吧!

喜欢这篇文章么?

欢迎留下你想说的,留言 100% 精选哦!

Elastic 社区公众号长期征稿,如果您有 Elastic  技术的相关文章,也欢迎投稿至本公众号,一起进步! 投稿请添加微信:medcl123



招聘信息

Job board


社区招聘栏目是一个新的尝试,帮助社区的小伙伴找到心仪的职位,也帮助企业找到所需的人才,为伯乐和千里马牵线搭桥。有招聘需求的企业和正在求职的社区小伙伴,可以联系微信 medcl123 提交招聘需求和发布个人简历信息。


Elastic中文社区公众号 (elastic-cn)

为您汇集 Elastic 社区的最新动态、精选干货文章、精华讨论、文档资料、翻译与版本发布等。

喜欢本篇内容就请给我们点个[在看]吧




本文分享自微信公众号 - Elastic中文社区(elastic-cn)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部