fluentd 学习笔记

最近为了做一些数据分析,把我自己服务器上所有应用的日志都通过 fluentd 转存到 mongodb 了,第一次用 fluentd,记录一些笔记。

因为是初学,绝大部分内容来源于官方文档2,等实际线上使用一段时间后再来更新一些心得。


一、Install

fluent 比较烦的一点是,从 gem 安装和从 rpm、yum 安装的名字不一样,连配置文件的路径都不一样,需要记住的是:

  • 从 gem 安装的,配置文件和执行程序都叫做 fluent;
  • 从 rpm 安装的,配置文件和执行程序都叫做 td-agent。

1、安装 fluentd

详细可参见官方文档

以 CentOS 为例:

 
  1. # 安装

  2. $ sudo curl -L https://toolbelt.treasuredata.com/sh/install-redhat-td-agent2.sh | sh

  3.  
  4. # 启动

  5. $ sudo /etc/init.d/td-agent start

2、安装插件

 
  1. # 从 rpm 安装的话,

  2. # 比如要使用下例的 mongo,需要安装

  3. # $ sudo td-agent-gem install fluent-plugin-mongo

  4. $ sudo td-agent-gem <PLUGIN_NAME>

  5.  
  6. # 从 gem 安装的话

  7. $ sudo gem install <PLUGIN_NAME>


二、简介

fluentd 是一个日志收集系统,通过丰富的插件,可以收集来自于各种系统或应用的日志,然后根据用户定义将日志做分类处理。

通过 fluentd,你可以非常轻易的实现像追踪日志文件并将其过滤后转存到 MongoDB 这样的操作。fluentd 可以彻底的将你从繁琐的日志处理中解放出来。

用图来做说明的话,使用 fluentd 以前,你的系统是这样的:

null

使用了 fluentd 后,你的系统会成为这样:

null

(图片来源3


三、配置文件

1、路径

分为两种情况:

  • 如果是通过 gem 安装的,那么可以通过下列命令生成和编辑配置文件
 
  1. $ sudo fluentd --setup /etc/fluent

  2. $ sudo vi /etc/fluent/fluent.conf

  • 如果是通过 RPM, Deb 或 DMG 安装的,那么配置文件在:
$ sudo vi /etc/td-agent/td-agent.conf

2、重用

你可以在配置文件里使用 @include 来切分你的配置文件,include 支持多种写法:

 
  1. # 绝对路径

  2. include /path/to/config.conf

  3. # 相对路径

  4. @include conf.d/*.conf

  5. # 甚至 URL

  6. @include http://example.com/fluent.conf

3、数据格式

在配置文件里你需要为很多参数赋值,这些值必须使用 fluentd 支持的数据格式,有下列这些:

  • string:字符串,最常见的格式,详细支持语法见文档1
  • integer:整数
  • float:浮点数;
  • size 大小,仅支持整数
    • <INTEGER>k 或 <INTERGER>K
    • <INTEGER>m 或 <INTERGER>M
    • <INTEGER>g 或 <INTERGER>G
    • <INTEGER>t 或 <INTERGER>T
  • time:时间,也只支持整数;
    • <INTEGER>s 或 <INTERGER>S
    • <INTEGER>m 或 <INTERGER>M
    • <INTEGER>h 或 <INTERGER>H
    • <INTEGER>d 或 <INTERGER>D
  • array:按照 JSON array 解析;
  • hash:按照 JSON object 解析。

四、命令

配置文件的核心是各种命令块(directives),每一种命令都是为了完成某种处理,命令与命令之前还可以组成串联关系,以 pipline 的形式流式的处理和分发日志。

最常见的方式就是 source 收集日志,然后由串联的 filter 做流式的处理,最后交给 match 进行分发。

同时你还可以用 label 将任务分组,用 error 处理异常,用 system 修改运行参数。

下面是详细的说明。

1、source

source 是 fluentd 的一切数据的来源,每一个 source 内都包含一个输入模块,比如原生集成的包含 http 和 forward 两个模块,分别用来接收 HTTP 请求和 TCP 请求:

 
  1. # Receive events from 24224/tcp

  2. # This is used by log forwarding and the fluent-cat command

  3. <source>

  4. @type forward

  5. port 24224

  6. </source>

  7.  
  8. # http://this.host:9880/myapp.access?json={"event":"data"}

  9. <source>

  10. @type http

  11. port 9880

  12. </source>

当然,除了这两个外,fluentd 还有大量的支持各种协议或方式的 source 插件,比如最常用的 tail 就可以帮你追踪文件。

每一个具体的插件都包含其特有的参数,比如上例中 port 就是一个参数,当你要使用一个 source 插件的时候,注意看看有哪些参数是需要配置的,然后将其写到 source directive 内。

source dirctive 在获取到输入后,会向 fluent 的路由抛出一个事件,这个事件包含三个要素:

  • tag
  • time
  • record

那上例代码中的第二个 source 举例,当我们发起一个 http://this.host:9880/myapp.access?json={"event":"data"} 的请求时,这个 source 会抛出:

 
  1. # generated by http://this.host:9880/myapp.access?json={"event":"data"}

  2. tag: myapp.access

  3. time: (current time)

  4. record: {"event":"data"}

关于如何编写一个输入插件,可以参考文档4

2、match

match 用来指定动作,通过 tag 匹配 source,然后执行指定的命令来分发日志,最常见的用法就是将 source 收集的日志转存到数据库。

 
  1. # http://this.host:9880/myapp.access?json={"event":"data"}

  2. <source>

  3. @type http

  4. port 9880

  5. </source>

  6.  
  7. # 将标记为 myapp.access 的日志转存到文件

  8. <match myapp.access>

  9. @type file

  10. path /var/log/fluent/access

  11. </match>

上例中的 myapp.access 就是 tag,tag 有好几种匹配模式:

  • *:匹配任意一个 tag;
  • **:匹配任意数量个 tag;
  • a b:匹配 a 或 b;
  • {X,Y,Z}:匹配 X, Y, Z 中的一个。

比如我可以写成这样:

 
  1. <match a.*>

  2. <match **>

  3. <match a.{b,c}>

  4. <match a.* b.*>

fluentd 按照 match 出现的顺序依次匹配,一旦匹配成功就不会再往下匹配,所以如果你先写了一个 match **,然后后面的所有的 match 都会被忽略。

然后我们使用了 @type file 插件来处理事件,这个插件有一个 path 属性,用来指定输出文件。

用法和 source 几乎一模一样,不过 source 是抛出事件,match 是接收并处理事件。你同样可以找到大量的各式各样的输出插件,也可以参考文档5自己写一个。

3、filter

filter 和 match 的语法几乎完全一样,但是 filter 可以串联成 pipeline,对数据进行串行处理,最终再交给 match 输出。

 
  1. # http://this.host:9880/myapp.access?json={"event":"data"}

  2. <source>

  3. @type http

  4. port 9880

  5. </source>

  6.  
  7. <filter myapp.access>

  8. @type record_transformer

  9. <record>

  10. host_param "#{Socket.gethostname}"

  11. </record>

  12. </filter>

  13.  
  14. <match myapp.access>

  15. @type file

  16. path /var/log/fluent/access

  17. </match>

这个例子里,filter 获取数据后,调用原生的 @type record_transformer 插件,在事件的 record 里插入了新的字段 host_param,然后再交给 match 输出。

你可以参考文档6来学习如何编写自定义的 filter。

虽然各个插件都有各自的参数,不过 fluentd 为所有的插件都设定了一组默认的参数: - @type:指定插件类型; - @id:给插件指定一个 id; - @label:指定 label; - @log_level:指定插件接收的日志级别。

你可以在任意插件内指定这些参数。

4、system

fluentd 的相关设置,可以在启动时设置,也可以在配置文件里设置,包含:

  • log_level
  • suppress_repeated_stacktrace
  • emit_error_log_interval
  • suppress_config_dump
  • without_source

5、label

label 用于将任务进行分组,方便复杂任务的管理。

你可以在 source 里指定 @label @<LABEL_NAME>,这个 source 所触发的事件就会被发送给指定的 label 所包含的任务,而不会被后续的其他任务获取到。

看个例子:

 
  1. <source>

  2. @type forward

  3. </source>

  4.  
  5. <source>

  6. # 这个任务指定了 label 为 @SYSTEM

  7. # 会被发送给 <label @SYSTEM>

  8. # 而不会被发送给下面紧跟的 filter 和 match

  9. @type tail

  10. @label @SYSTEM

  11. </source>

  12.  
  13. <filter access.**>

  14. @type record_transformer

  15. <record>

  16. # ...

  17. </record>

  18. </filter>

  19. <match **>

  20. @type elasticsearch

  21. # ...

  22. </match>

  23.  
  24. <label @SYSTEM>

  25. # 将会接收到上面 @type tail 的 source event

  26. <filter var.log.middleware.**>

  27. @type grep

  28. # ...

  29. </filter>

  30. <match **>

  31. @type s3

  32. # ...

  33. </match>

  34. </label>

6、error

用来接收插件通过调用 emit_error_event API 抛出的异常,使用方法和 label 一样,通过设定 <label @ERROR> 就可以接收到相关的异常。


五、Demo

1、Config

一个监听 Nginx 日志的例子:

 
  1. <source>

  2. @type tail

  3. @id nginx-access

  4. @label @nginx

  5. path /var/log/nginx/access.log

  6. pos_file /var/lib/fluentd/nginx-access.log.posg

  7. tag nginx.access

  8. format /^(?<remote>[^ ]*) (?<host>[^ ]*) \[(?<time>[^\]]*)\] (?<code>[^ ]*) "(?<method>\S+)(?: +(?<path>[^\"]*) +\S*)?" (?<size>[^ ]*)(?: "(?<referer>[^\"]*)" "(?<agent>[^\"]*)")?$/

  9. time_format %d/%b/%Y:%H:%M:%S %z

  10. </source>

  11.  
  12. <source>

  13. @type tail

  14. @id nginx-error

  15. @label @nginx

  16. path /var/log/nginx/error.log

  17. pos_file /var/lib/fluentd/nginx-error.log.posg

  18. tag nginx.error

  19.  
  20. format /^(?<time>\d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2}) \[(?<log_level>\w+)\] (?<pid>\d+).(?<tid>\d+): (?<message>.*)$/

  21. </source>

  22.  
  23. <label @nginx>

  24. <match nginx.access>

  25. @type mongo

  26. database nginx

  27. collection access

  28. host 10.47.12.119

  29. port 27016

  30.  
  31. time_key time

  32. flush_interval 10s

  33. </match>

  34. <match nginx.error>

  35. @type mongo

  36. database nginx

  37. collection error

  38. host 10.47.12.119

  39. port 27016

  40.  
  41. time_key time

  42. flush_interval 10s

  43. </match>

  44. </label>

为了匹配,你也需要修改 Nginx 的 log_format 为:

log_format main '$remote_addr $host [$time_local] $status "$request" $body_bytes_sent "$http_referer" "$http_user_agent"';

2、Docker

 

我自己在用的一个 docker 的镜像和 docker-compose.yml


  1. fluentd: 字符串格式 

  2. fluentd: configure 

  3. fluentd安装配置简介 

  4. fluentd: Input Plugin Overview 

  5. fluentd: Output Plugin Overview 

  6. fluentd: Filter Plugin Overview 

 

java API

 

 
  1. // private static FluentLogger LOG = FluentLogger.getLogger("app");

  2. // for remote fluentd

  3. // port 24225

  4. // bind 172.16.5.20

  5. // The method should be called only once.

  6. private static FluentLogger LOG = FluentLogger.getLogger("docker",

  7. "172.16.5.20", 24225);

  8.  
  9. public static void main(String[] args) {

  10. FluentLoggerTest fluentLoggerTest = new FluentLoggerTest();

  11.  
  12.  
  13. fluentLoggerTest.doApplicationLogic();

  14. System.out.println("=====end main ======");

  15. }

  16.  
  17.  
  18. public void doApplicationLogic() {

  19. // Then, please create the events like this.

  20. // This will send the event to fluentd, with tag 'app.follow' and the

  21. // attributes 'from' and 'to'.

  22. Map<String, Object> data = new HashMap<String, Object>();

  23. data.put("from", "userA");

  24. data.put("to", "userB");

  25. LOG.log("test", data);

  26. // Close method in FluentLogger class should be called explicitly when

  27. // application is finished.

  28. // The method closes socket connection with the fluentd.

  29.  
  30.  
  31. FluentLogger.close();

  32. //FluentLogger.closeAll();

  33. }

  34.  

Maven pom

<logger.version>0.2.1</logger.version>
<dependency>
    <groupId>org.fluentd</groupId>
    <artifactId>fluent-logger</artifactId>
    <version>${logger.version}</version>
  </dependency>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值