日志收集工具 Fluentd 使用教程
Fluentd 是一個(gè)開源的數(shù)據(jù)收集器,致力于為用戶搭建統(tǒng)一的日志收集層,它可以讓你統(tǒng)一日志的收集和消費(fèi),以便更好地使用和理解日志,統(tǒng)一的日志記錄層可讓你和你的團(tuán)隊(duì)更好地利用數(shù)據(jù)并更快地迭代你的應(yīng)用。

安裝
Fluentd 有多種安裝方式,比如可以通過 Docker 或者手動(dòng)進(jìn)行安裝,在手動(dòng)安裝 Fluentd 之前,請(qǐng)確保你的環(huán)境已正確設(shè)置,以避免以后出現(xiàn)任何不一致。
環(huán)境配置
請(qǐng)遵循以下建議:
設(shè)置 NTP
增加文件描述符的最大數(shù)量
優(yōu)化網(wǎng)絡(luò)內(nèi)核參數(shù)
設(shè)置 NTP
強(qiáng)烈建議你在節(jié)點(diǎn)上設(shè)置 NTP 守護(hù)進(jìn)程(例如 chrony、ntpd 等)以獲得準(zhǔn)確的當(dāng)前時(shí)間戳,這對(duì)于所有生產(chǎn)服務(wù)至關(guān)重要。
增加文件描述符的最大數(shù)量
我們可以使用 ulimit -n 命令查看現(xiàn)有配置:
$ ulimit -n
65535
如果你的控制臺(tái)顯示 1024,那是不夠的。請(qǐng)將以下幾行添加到 /etc/security/limits.conf 文件并重啟機(jī)器:
root soft nofile 65536
root hard nofile 65536
* soft nofile 65536
* hard nofile 65536
如果使用 systemd 下運(yùn)行 fluentd,也可以使用選項(xiàng) LimitNOFILE=65536 進(jìn)行配置,如果你使用的是 td-agent 包,則默認(rèn)會(huì)設(shè)置該值。
優(yōu)化網(wǎng)絡(luò)內(nèi)核參數(shù)
對(duì)于具有許多 Fluentd 實(shí)例的高負(fù)載環(huán)境,可以將以下配置添加到 /etc/sysctl.conf 文件中:
net.core.somaxconn = 1024
net.core.netdev_max_backlog = 5000
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_wmem = 4096 12582912 16777216
net.ipv4.tcp_rmem = 4096 12582912 16777216
net.ipv4.tcp_max_syn_backlog = 8096
net.ipv4.tcp_slow_start_after_idle = 0
net.ipv4.tcp_tw_reuse = 1
net.ipv4.ip_local_port_range = 10240 65535
使用 sysctl -p 命令或重新啟動(dòng)節(jié)點(diǎn)使更改生效。
td-agent 包
這里我們使用 td-agent deb 包進(jìn)行安裝,這是由 Treasure Data, Inc 和 Calyptia, Inc 維護(hù)的穩(wěn)定 Fluentd 分發(fā)包。
Fluentd 是用 Ruby 編寫的,具有靈活性,對(duì)性能敏感的部分用 C 語(yǔ)言編寫。但是,一些用戶可能難以安裝和操作 Ruby 守護(hù)程序。所以 Treasure Data, Inc 專門提供了一個(gè) Fluentd 的穩(wěn)定發(fā)行版,稱為 td-agent。
我這里是 Ubuntu Focal 的版本,可以使用下面的命令進(jìn)行一鍵安裝:
curl -fsSL https://toolbelt.treasuredata.com/sh/install-ubuntu-focal-td-agent4.sh | sh
上面的腳本會(huì)自動(dòng)配置 systemd 的啟動(dòng)腳本,腳本內(nèi)容如下所示:
$ cat /lib/systemd/system/td-agent.service;
[Unit]
Description=td-agent: Fluentd based data collector for Treasure Data
Documentation=https://docs.treasuredata.com/display/public/PD/About+Treasure+Data%%27s+Server-Side+Agent
After=network-online.target
Wants=network-online.target
[Service]
User=td-agent
Group=td-agent
LimitNOFILE=65536
Environment=LD_PRELOAD=/opt/td-agent/lib/libjemalloc.so
Environment=GEM_HOME=/opt/td-agent/lib/ruby/gems/2.7.0/
Environment=GEM_PATH=/opt/td-agent/lib/ruby/gems/2.7.0/
Environment=FLUENT_CONF=/etc/td-agent/td-agent.conf
Environment=FLUENT_PLUGIN=/etc/td-agent/plugin
Environment=FLUENT_SOCKET=/var/run/td-agent/td-agent.sock
Environment=TD_AGENT_LOG_FILE=/var/log/td-agent/td-agent.log
Environment=TD_AGENT_OPTIONS=
EnvironmentFile=-/etc/default/td-agent
PIDFile=/var/run/td-agent/td-agent.pid
RuntimeDirectory=td-agent
Type=forking
# XXX: Fix fluentd executables path
ExecStart=/opt/td-agent/bin/fluentd --log $TD_AGENT_LOG_FILE --daemon /var/run/td-agent/td-agent.pid $TD_AGENT_OPTIONS
ExecStop=/bin/kill -TERM ${MAINPID}
ExecReload=/bin/kill -HUP ${MAINPID}
Restart=always
TimeoutStopSec=120
[Install]
WantedBy=multi-user.target
所以我們可以使用 systemctl 來(lái)管理 td-agent 服務(wù):
$ sudo systemctl start td-agent
$ sudo systemctl status td-agent
● td-agent.service - td-agent: Fluentd based data collector for Treasure Data
Loaded: loaded (/lib/systemd/system/td-agent.service; enabled; vendor preset: enabled)
Active: active (running) since Sat 2022-06-11 11:00:04 CST; 4min 6s ago
Docs: https://docs.treasuredata.com/display/public/PD/About+Treasure+Data%27s+Server-Side+Agent
Process: 3664 ExecStart=/opt/td-agent/bin/fluentd --log $TD_AGENT_LOG_FILE --daemon /var/run/td-agent/td-agent.pid $TD_AGE>
Main PID: 3677 (fluentd)
Tasks: 9 (limit: 19106)
Memory: 99.0M
CGroup: /system.slice/td-agent.service
├─3677 /opt/td-agent/bin/ruby /opt/td-agent/bin/fluentd --log /var/log/td-agent/td-agent.log --daemon /var/run/td>
└─3680 /opt/td-agent/bin/ruby -Eascii-8bit:ascii-8bit /opt/td-agent/bin/fluentd --log /var/log/td-agent/td-agent.>
Jun 11 11:00:03 ydzsio systemd[1]: Starting td-agent: Fluentd based data collector for Treasure Data...
Jun 11 11:00:04 ydzsio systemd[1]: Started td-agent: Fluentd based data collector for Treasure Data.
lines 1-14/14 (END)
td-agent 啟動(dòng)后通過下面的命令來(lái)發(fā)送一條日志:
$ curl -X POST -d 'json={"json":"message"}' http://localhost:8888/debug.test
發(fā)送后查看 td-agent 的日志,正常會(huì)收到如下所示的日志信息:
$ tail -n 1 /var/log/td-agent/td-agent.log
2022-06-11 11:09:02.377608475 +0800 debug.test: {"json":"message"}
Docker 方式
當(dāng)然更多的時(shí)候使用 Docker 方式使用 Fluentd 會(huì)更方便,這里我們創(chuàng)建一些簡(jiǎn)單的文件來(lái)進(jìn)行測(cè)試:
$ mkdir fluentd
$ cd fluentd
# 創(chuàng)建用于保存 fluentd 的配置文件 etc 目錄和保存日志的 logs 目錄
$ mkdir -p etc logs
# 添加一個(gè)簡(jiǎn)單的配置文件
$ cat etc/fluentd_basic.conf
<source>
@type http
port 8888
bind 0.0.0.0
</source>
<match test.basic>
@type stdout
</match>
這里我們創(chuàng)建了一個(gè) fluentd 目錄用于測(cè)試,其中 etc 目錄用于保留配置文件、logs 目錄保存日志,首先添加了一個(gè)最基本的配置文件 etc/fluentd_basic.conf,其中:
source部分使用了 http 類型的輸入插件,在 8888 端口啟動(dòng)一個(gè)服務(wù)用于接收日志match部分定義了日志匹配test.basic標(biāo)簽,就將日志輸出到 stdout
使用下面的命令進(jìn)行 Fluentd 的啟動(dòng):
$ docker run -p 8888:8888 --rm -v $(pwd)/etc:/fluentd/etc -v $(pwd)/logs:/fluentd/logs fluent/fluentd:v1.14-1 -c /fluentd/etc/fluentd_basic.conf -v
這里我們將 etc 目錄和 logs 目錄掛載到容器中,然后通過 -c 參數(shù)指定 Fluentd 的配置文件,最后一個(gè) -v 參數(shù)是用于設(shè)置 Fluentd 開啟 verbose 模式,便于查看 Fluentd 的日志方便調(diào)試,正常會(huì)看到如下所示的輸出信息:
2022-06-11 07:23:48 +0000 [info]: fluent/log.rb:330:info: parsing config file is succeeded path="/fluentd/etc/fluentd_basic.conf"
2022-06-11 07:23:48 +0000 [info]: fluent/log.rb:330:info: gem 'fluentd' version '1.14.3'
2022-06-11 07:23:48 +0000 [debug]: fluent/log.rb:309:debug: No fluent logger for internal event
2022-06-11 07:23:48 +0000 [info]: fluent/log.rb:330:info: using configuration file: <ROOT>
<source>
@type http
port 8888
bind "0.0.0.0"
</source>
<match test.basic>
@type stdout
</match>
</ROOT>
2022-06-11 07:23:48 +0000 [info]: fluent/log.rb:330:info: starting fluentd-1.14.3 pid=7 ruby="2.7.5"
2022-06-11 07:23:48 +0000 [info]: fluent/log.rb:330:info: spawn command to main: cmdline=["/usr/bin/ruby", "-Eascii-8bit:ascii-8bit", "/usr/bin/fluentd", "-c", "/fluentd/etc/fluentd_basic.conf", "-v", "--plugin", "/fluentd/plugins", "--under-supervisor"]
2022-06-11 07:23:49 +0000 [info]: fluent/log.rb:330:info: adding match pattern="test.basic" type="stdout"
2022-06-11 07:23:49 +0000 [info]: fluent/log.rb:330:info: adding source type="http"
2022-06-11 07:23:49 +0000 [debug]: #0 fluent/log.rb:309:debug: No fluent logger for internal event
2022-06-11 07:23:49 +0000 [info]: #0 fluent/log.rb:330:info: starting fluentd worker pid=16 ppid=7 worker=0
2022-06-11 07:23:49 +0000 [debug]: #0 fluent/log.rb:309:debug: listening http bind="0.0.0.0" port=8888
2022-06-11 07:23:49 +0000 [info]: #0 fluent/log.rb:330:info: fluentd worker is now running worker=0
啟動(dòng)后我們同樣可以發(fā)送一條日志到 Fluentd 來(lái)驗(yàn)證我們的配置:
$ curl -X POST -d 'json={"action":"login","user":100}' http://localhost:8888/test.logs
發(fā)送后正常會(huì)在 Fluentd 中查看到如下所示的一條信息:
2022-06-11 07:34:29.925695338 +0000 test.logs: {"action":"login","user":100}
事件生命周期
Fluentd 是一個(gè)日志收集系統(tǒng),一條日志消息在 Fluentd 中被看成一個(gè) Event 事件,F(xiàn)luentd 的事件主要由下面三部分組成:
標(biāo)簽 tag:用于描述事件來(lái)源,可用于后面的事件路由 時(shí)間 time:事件發(fā)生的時(shí)間,時(shí)間格式為 Unix 時(shí)間戳 記錄 record:事件內(nèi)容本身,JSON 格式
所有的輸入插件都需要解析原始日志,生成滿足上面結(jié)構(gòu)的事件字段,比如一條 Apache 的訪問日志:
192.168.0.1 - - [28/Feb/2013:12:00:00 +0900] "GET / HTTP/1.1" 200 777
在通過 in_tail 輸入插件處理后,會(huì)得到如下所示的輸出結(jié)果:
tag: apache.access # 通過配置文件指定
time: 1362020400 # 28/Feb/2013:12:00:00 +0900
record: {"user": "-", "method": "GET", "code": 200, "size": 777, "host": "192.168.0.1", "path": "/"}
當(dāng) Fluentd 收到一條事件后會(huì)經(jīng)過一系列的處理流程:
修改事件的相關(guān)字段 過濾掉一些不需要的事件 路由事件輸出到不同的地方
過濾器 Filter
Filter 用于定義一個(gè)事件是該被接受或是被過濾掉,接下來(lái)我們創(chuàng)建一個(gè)新的配置文件新增過濾器。
$ cat etc/fluentd_filter.conf
<source>
@type http
port 8888
bind 0.0.0.0
</source>
<filter test.logs>
@type grep
<exclude>
key action
pattern ^logout$
</exclude>
</filter>
<match test.logs>
@type stdout
</match>
在該配置文件中我們新增了一個(gè) filter 模塊,使用 grep 插件,exclude 部分表示要過濾掉的日志配置,這里我們配置的是 action 這個(gè) key 匹配 ^logout$ 的時(shí)候進(jìn)行過濾,就是直接過濾掉 logout 日志事件。
使用新的配置文件,重新啟動(dòng) fluentd:
$ docker run -p 8888:8888 --rm -v $(pwd)/etc:/fluentd/etc -v $(pwd)/logs:/fluentd/logs fluent/fluentd:v1.14-1 -c /fluentd/etc/fluentd_filter.conf -v
然后重新向 Fluentd 提交兩條日志數(shù)據(jù):
$ curl -X POST -d 'json={"action":"login","user":2}' http://localhost:8888/test.logs
$ curl -X POST -d 'json={"action":"logout","user":2}' http://localhost:8888/test.logs
正常這個(gè)時(shí)候 Fluentd 只會(huì)收到第一條日志數(shù)據(jù),logout 這條事件被過濾掉了:
2022-06-11 07:52:45.555576216 +0000 test.logs: {"action":"login","user":2}
標(biāo)識(shí)符 Labels
Fluentd 的處理流程是根據(jù)我們?cè)谂渲梦募械亩x從上到下依次執(zhí)行的。但是假如我們?cè)谂渲梦募卸x了多個(gè)輸入源,不同的輸入源需要使用不同的 Filters 過濾器的時(shí)候,那么還按照順序執(zhí)行的方式話,配置文件就會(huì)變得非常復(fù)雜。為了解決這個(gè)問題,F(xiàn)luentd 中提供了一種標(biāo)識(shí)符 Labels 的方式,可以為不同的輸入源指定不同的處理流程。
如下所示創(chuàng)建一個(gè)新的配置文件 fluentd_labels.conf:
<source>
@type http
port 8888
bind 0.0.0.0
@label @TEST
</source>
<filter test.logs>
@type grep
<exclude>
key action
pattern ^login$
</exclude>
</filter>
<label @TEST>
<filter test.logs>
@type grep
<exclude>
key action
pattern ^logout$
</exclude>
</filter>
<match test.logs>
@type stdout
</match>
</label>
首先我們?cè)谳斎朐粗薪o日志源定義了一個(gè)標(biāo)簽 @label @TEST,然后先定義了一個(gè) filter 過濾掉 login 事件,然后在一個(gè) label 模塊里面過濾了 logout 事件。
現(xiàn)在我們使用該配置重新啟動(dòng) Fluentd:
$ docker run -p 8888:8888 --rm -v $(pwd)/etc:/fluentd/etc -v $(pwd)/logs:/fluentd/logs fluent/fluentd:v1.14-1 -c /fluentd/etc/fluentd_labels.conf -v
然后重新向 Fluentd 提交兩條日志數(shù)據(jù):
$ curl -X POST -d 'json={"action":"login","user":2}' http://localhost:8888/test.logs
$ curl -X POST -d 'json={"action":"logout","user":2}' http://localhost:8888/test.logs
正常 Fluentd 中會(huì)輸出一條日志記錄:
2022-06-11 08:06:41.977559894 +0000 test.logs: {"action":"login","user":2}
這是因?yàn)槲覀優(yōu)檩斎肴罩驹O(shè)置了 @TEST 的標(biāo)簽,因此跳過中間設(shè)置的一些過濾器,只運(yùn)行了 <label @TEST>...</lable> 標(biāo)簽塊里的過濾器,如果標(biāo)簽塊里面沒有定義過濾器則就不會(huì)過濾日志了。
配置文件
接下來(lái)我們?cè)賮?lái)詳細(xì)介紹下 Fluentd 的配置文件,在配置文件中可以使用指令包括:
source指令確定輸入源match指令確定輸出目的地filter指令確定事件處理管道system指令設(shè)置系統(tǒng)范圍的配置label指令對(duì)內(nèi)部路由的輸出和過濾器進(jìn)行分組@include指令包含其他文件
source
通過使用 source 指令選擇和配置所需的輸入插件來(lái)啟用 Fluentd 輸入源,F(xiàn)luentd 標(biāo)準(zhǔn)輸入插件包括 http 和 forward。http 提供了一個(gè) HTTP 端點(diǎn)來(lái)接受傳入的 HTTP 消息,而 forward 提供了一個(gè) TCP 端點(diǎn)來(lái)接受 TCP 數(shù)據(jù)包。當(dāng)然,也可以同時(shí)是兩者。例如:
# 在24224端口接受TCP事件
<source>
@type forward
port 24224
</source>
# http://<ip>:9880/myapp.access?json={"event":"data"}
<source>
@type http
port 9880
</source>
輸入源可以一次指定多個(gè),@type 參數(shù)用來(lái)指定輸入插件,輸入插件擴(kuò)展了 Fluentd,以檢索和提取來(lái)自外部的日志事件,一個(gè)輸入插件通常創(chuàng)建一個(gè)線程、套接字和一個(gè)監(jiān)聽套接字,它也可以被寫成定期從數(shù)據(jù)源中提取數(shù)據(jù)。Fluentd 支持非常多種輸入插件,包括:
in_tail in_forward in_udp in_tcp in_unix in_http in_syslog in_exec in_sample in_windows_eventlog
tail 插件應(yīng)該是平時(shí)我們使用得最多的輸入插件了,in_tail 輸入插件允許 Fluentd 從文本文件的尾部讀取事件,其行為類似于 tail -F 命令,比如下面的配置就定義了輸入插件為 tail,其中的 path 屬性指定了日志的源路徑:
<source>
@type tail
path /var/log/httpd-access.log
pos_file /var/log/td-agent/httpd-access.log.pos
tag apache.access
<parse>
@type apache2
</parse>
</source>當(dāng) Fluentd 第一次被配置為 in_tail 時(shí),它將從該日志的尾部開始讀取,而不是從開始,一旦日志被輪轉(zhuǎn),F(xiàn)luentd 就會(huì)從頭開始讀取新文件,它保持著對(duì)當(dāng)前 inode 號(hào)的跟蹤。如果 Fluentd 重新啟動(dòng),它會(huì)從重新啟動(dòng)前的最后一個(gè)位置繼續(xù)讀取,這個(gè)位置記錄在 `pos_file`` 參數(shù)指定的位置文件中。
match
match 用來(lái)指定日志的輸出目的地,例如:
# 將滿足 myapp.acccess 標(biāo)簽的事件全部輸出到
# /var/log/fluent/access.%Y-%m-%d
<match myapp.access>
@type file
path /var/log/fluent/access
</match>同樣輸出也可以一次指定多個(gè),@type 參數(shù)指定使用哪一個(gè)輸出插件。Fluentd 輸出插件具有三種緩沖和刷新模式:
非緩沖模式不對(duì)數(shù)據(jù)進(jìn)行緩沖,而是立即輸出結(jié)果 同步緩沖模式有一個(gè)暫存的 staged 的緩沖塊 chunks(一個(gè) chunk 是一個(gè)事件的集合)和一個(gè) chunk 隊(duì)列,其行為可以由 <buffer>部分控制異步緩沖模式也有暫存和隊(duì)列,但輸出插件不會(huì)在方法中同步提交寫塊,而是稍后提交

輸出插件可以支持所有的模式,但可能只支持其中一種模式,如果配置中沒有 <buffer> 部分,F(xiàn)luentd 會(huì)自動(dòng)選擇合適的模式。同樣 Fluentd 支持多種輸出插件, 比如:
out_copy out_null out_roundrobin out_stdout out_exec_filter out_forward out_mongo / out_mongo_replset out_exec out_file out_s3 out_webhdfs ......
比如我們使用 out_file 作為輸出目的地插件,out_file 輸出插件將事件寫入文件。當(dāng)滿足 timekey 條件時(shí),將創(chuàng)建該文件,要改變輸出頻率,需要修改 timekey 的值,如下所示:
<match pattern>
@type file
path /var/log/fluent/myapp
compress gzip
<buffer>
timekey 1d
timekey_use_utc true
timekey_wait 10m
</buffer>
</match>filter
使用 filter 可以指定事件的處理流程,多個(gè) filter 可以串聯(lián)起來(lái)使用:
Input -> filter 1 -> ... -> filter N -> Output
比如我們添加一個(gè)標(biāo)準(zhǔn)的 record_transformer 過濾器來(lái)匹配事件。
# http://this.host:9880/myapp.access?json={"event":"data"}
<source>
@type http
port 9880
</source>
<filter myapp.access>
@type record_transformer
<record>
host_param "#{Socket.gethostname}"
</record>
</filter>
<match myapp.access>
@type file
path /var/log/fluent/access
</match>
接收到的事件 {"event":"data"} 首先進(jìn)入 record_transformer 過濾器,該過濾器將 host_param 字段添加到事件中,然后過濾后的事件變?yōu)?{"event":"data","host_param":"webserver1"} 進(jìn)入 file 文件輸出插件。
system
系統(tǒng)范圍的配置由 system 指令設(shè)置,它們中的大多數(shù)也可以通過命令行選項(xiàng)獲得。例如可以使用以下配置:
log_level suppress_repeated_stacktrace emit_error_log_interval suppress_config_dump without_source process_name (僅在 system指令中可用,沒有 fluentd 選項(xiàng))
例如下面的配置:
<system>
# 等同于 -qq 選項(xiàng)
log_level error
# 等同于 --without-source 選項(xiàng)
without_source
# ...
</system>此外如果設(shè)置了 process_name 參數(shù),則 fluentd supervisor 和工作進(jìn)程名稱將更改。
<system>
process_name fluentd1
</system>使用此配置,ps 命令顯示以下結(jié)果:
% ps aux | grep fluentd1
foo 45673 0.4 0.2 2523252 38620 s001 S+ 7:04AM 0:00.44 worker:fluentd1
foo 45647 0.0 0.1 2481260 23700 s001 S+ 7:04AM 0:00.40 supervisor:fluentd1
label
label 指令可以對(duì)內(nèi)部路由的過濾器和輸出進(jìn)行分組,label 降低了 tag 處理的復(fù)雜性。label 參數(shù)是內(nèi)置插件參數(shù),因此需要 @ 前綴。例如下面的配置示例:
<source>
@type forward
</source>
<source>
@type tail
@label @SYSTEM
</source>
<filter access.**>
@type record_transformer
<record>
# ...
</record>
</filter>
<match **>
@type elasticsearch
# ...
</match>
<label @SYSTEM>
<filter var.log.middleware.**>
@type grep
# ...
</filter>
<match **>
@type s3
# ...
</match>
</label>
在該配置中,forward 事件被路由到 record_transformer 過濾器,然后輸出到 elasticsearch,而 in_tail 事件被路由到 @SYSTEM 標(biāo)簽內(nèi)的 grep 過濾器,然后輸出到 s3。
label 參數(shù)對(duì)于沒有 tag 前綴的事件流分離很有用。
@ERROR 標(biāo)簽是一個(gè)內(nèi)置標(biāo)簽,用于插件的 emit_error_event API 發(fā)出的錯(cuò)誤記錄。如果設(shè)置了 <label @ERROR>,則在發(fā)出相關(guān)錯(cuò)誤時(shí)將事件路由到此標(biāo)簽,例如緩沖區(qū)已滿或記錄無(wú)效。
@ROOT 標(biāo)簽也是一個(gè)內(nèi)置標(biāo)簽,用于通過插件的 event_emitter_router API 獲取根路由器。從 v1.14.0 開始引入此標(biāo)簽,以將標(biāo)簽分配回默認(rèn)路由,例如,由 concat 過濾器處理的超時(shí)事件記錄可以發(fā)送到默認(rèn)路由。
@include
使用 @include 指令可以導(dǎo)入單獨(dú)的配置文件中的指令,例如:
# 包含 ./config.d 目錄中的所有配置文件
@include config.d/*.conf
@include 指令支持常規(guī)文件路徑、glob 模式和 http URL 約定:
# 絕對(duì)路徑
@include /path/to/config.conf
# 如果使用相對(duì)路徑,指令將使用此配置文件的目錄名來(lái)擴(kuò)展路徑
@include extra.conf
# glob 匹配模式
@include config.d/*.conf
# http
@include http://example.com/fluent.conf
parse
一些 Fluentd 插件支持 <parse> 指令來(lái)指定如何解析原始日志數(shù)據(jù)。<parse> 指令可以在 <source>、<match> 或 <filter> 下配置,例如:
<source>
@type tail
path /path/to/input/file
<parse>
@type nginx
keep_time_key true
</parse>
</source><parse> 中通過 @type 參數(shù)來(lái)指定解析器插件的類型。Fluentd 內(nèi)置了一些有用的解析器插件,包括:
regexp apache2 apache_error nginx syslog csv tsv ltsv json multiline none
還有一些第三方的解析器插件:
grok:如果你熟悉 grok 模式,則 grok-parser 插件很有用 multi-format-parser:如果你需要在一個(gè)數(shù)據(jù)流中解析多種格式,那么可以使用這個(gè)解析器 protobuf:protocol buffers avro:Apache Avro
比如我們的日志事件中有包含多行日志的數(shù)據(jù),那么我們就可以使用 multiline 這個(gè)解析器來(lái)解決,該插件可以用來(lái)解析多行日志。這個(gè)插件是正則表達(dá)式解析器的多行版本。
多行解析器使用 formatN 和 format_firstline 參數(shù)解析日志,format_firstline 用于檢測(cè)多行日志的起始行。formatN,其中 N 的范圍是 [1..20],是多行日志的 Regexp 格式列表。
與其他解析器插件不同,此插件需要輸入插件中的特殊代碼,例如處理 format_firstline。目前,in_tail 插件適用于多行,但其他輸入插件不適用于它。
比如有一條如下所示的輸入日志:
Started GET "/users/123/" for 127.0.0.1 at 2013-06-14 12:00:11 +0900
Processing by UsersController#show as HTML
Parameters: {"user_id"=>"123"}
Rendered users/show.html.erb within layouts/application (0.3ms)
Completed 200 OK in 4ms (Views: 3.2ms | ActiveRecord: 0.0ms)
我們可以添加如下所示的配置來(lái)進(jìn)行解析:
<parse>
@type multiline
format_firstline /^Started/
format1 /Started (?<method>[^ ]+) "(?<path>[^"]+)" for (?<host>[^ ]+) at (?<time>[^ ]+ [^ ]+ [^ ]+)\n/
format2 /Processing by (?<controller>[^\u0023]+)\u0023(?<controller_method>[^ ]+) as (?<format>[^ ]+?)\n/
format3 /( Parameters: (?<parameters>[^ ]+)\n)?/
format4 / Rendered (?<template>[^ ]+) within (?<layout>.+) \([\d\.]+ms\)\n/
format5 /Completed (?<code>[^ ]+) [^ ]+ in (?<runtime>[\d\.]+)ms \(Views: (?<view_runtime>[\d\.]+)ms \| ActiveRecord: (?<ar_runtime>[\d\.]+)ms\)/
</parse>其中的 format_firstline /^Started/ 用來(lái)指定第一行日志的匹配規(guī)則,formatN 指定后面幾行的匹配規(guī)則,最后可以解析成如下所示的結(jié)果:
time:
1371178811 (2013-06-14 12:00:11 +0900)
record:
{
"method" :"GET",
"path" :"/users/123/",
"host" :"127.0.0.1",
"controller" :"UsersController",
"controller_method":"show",
"format" :"HTML",
"parameters" :"{ \"user_id\":\"123\"}",
...
}
同樣有如下所示的 JAVA 日志事件:
2013-3-03 14:27:33 [main] INFO Main - Start
2013-3-03 14:27:33 [main] ERROR Main - Exception
javax.management.RuntimeErrorException: null
at Main.main(Main.java:16) ~[bin/:na]
2013-3-03 14:27:33 [main] INFO Main - End
我們可以使用下面的配置來(lái)解析:
<parse>
@type multiline
format_firstline /\d{4}-\d{1,2}-\d{1,2}/
format1 /^(?<time>\d{4}-\d{1,2}-\d{1,2} \d{1,2}:\d{1,2}:\d{1,2}) \[(?<thread>.*)\] (?<level>[^\s]+)(?<message>.*)/
</parse>這樣就可以解析成如下所示的結(jié)果了:
time:
2013-03-03 14:27:33 +0900
record:
{
"thread" :"main",
"level" :"INFO",
"message":" Main - Start"
}
time:
2013-03-03 14:27:33 +0900
record:
{
"thread" :"main",
"level" :"ERROR",
"message":" Main - Exception\njavax.management.RuntimeErrorException: null\n at Main.main(Main.java:16) ~[bin/:na]"
}
time:
2013-03-03 14:27:33 +0900
record:
{
"thread" :"main",
"level" :"INFO",
"message":" Main - End"
}
模式匹配
如上所述,F(xiàn)luentd 允許你根據(jù)事件的 tag 來(lái)路由事件,我們可以明確指定需要處理的 tag,比如 <filter app.log> 來(lái)指定只處理 tag 為 app.log 的事件,我們也可以在 filter 和 match 中通過通配符,來(lái)處理同一類 tag 的事件。
tag 通常是一個(gè)字符串,由 . 分隔,比如 myapp.access:
*: 匹配滿足一個(gè) tag 部分的事件, 比如:a.*將匹配 a.b 這樣的 tag, 但是不會(huì)處理 a 或者 a.b.c 這類 tag**: 匹配滿足 0 個(gè)或多個(gè) tag 部分的事件,比如a.**將匹配 a、a.b、a.b.c 這三種 tag{X, Y, Z}: 匹配滿足 X、Y 或者 Z 的 tag,比如{a, b}將匹配 a 或者 b,但是不會(huì)匹配 c,這種格式也可以和通配符組合使用,比如a.{b.c}.*或a.{b.c}.**#{...}會(huì)把花括號(hào)內(nèi)的字符串當(dāng)做是 ruby 的表達(dá)式處理。比如
<match "app.#{ENV['FLUENTD_TAG']}">
@type stdout
</match>
如果設(shè)置了環(huán)境變量 FLUENTD_TAG 為 dev,那上面等價(jià)于 app.dev。
當(dāng)指定了多個(gè)模式時(shí)(使用一個(gè)或多個(gè)空格分開),只要滿足其中任意一個(gè)就行。比如: <match a b>匹配 a 和 b,<match a.** b.*>匹配 a, a.b, a.b.c, b.d 等
當(dāng)有多個(gè) match,需要注意一下它們的順序,如下面的例子,第二個(gè) match 永遠(yuǎn)也不會(huì)生效:
# ** 匹配所有的 tags. Bad :(
<match **>
@type blackhole_plugin
</match>
<match myapp.access>
@type file
path /var/log/fluent/access
</match>
正確的寫法應(yīng)該是將確定的 tag 盡量寫在前面,模糊匹配的寫在后面。
<match myapp.access>
@type file
path /var/log/fluent/access
</match>
# Capture all unmatched tags. Good :)
<match **>
@type blackhole_plugin
</match>另外需要注意順序的是 filter 和 match,如果將 filter 放在 match 之后,那么它也永遠(yuǎn)不會(huì)生效。
參考文檔
關(guān)于 Fluentd 的更多使用配置可以參考官方文檔了解更多信息 https://docs.fluentd.org 。
