はじめに
Webサーバーからのログ収集などでfluentdを使うことがあって たまにfluentd設定ファイルを書くことがあるんですが、 たまにしか書かないので全然書き方が覚えられず苦労したりすることがあったので もうすこしどうにかならないかとツールを作ってみました。
難しさ
設定の書き方がわからないというのもあるんですが Webサーバーなど設置する環境にも不慣れなこともあって難しくなっているのかなと思います。
ログが正しく流れているのを確認するのはなかなか大変で
- サーバーをセットアップ
- fluentdやプラグインをサーバーにインストール
- ログを送信するサーバーアプリケーションをデプロイ
- アプリ操作などで実際にログを流す
- s3などにログが流れるのを確認する
上のような作業が必要になると思います。
さらに色々なフェーズで問題があって正しく送れないことがあります。
- fluentdの設定の問題
- サーバーアプリケーションの設定の問題
- s3やCloudWatchやBigQueryなどログ受信側の設定の問題
- WebサーバーのIAM権限などの問題
ということで環境に関わる問題と切り離せないと非常につらいので できれば環境と切り離して設定をテストできるとうれしいのではないかと思います。
--dry-runのオプションである程度はテストできるのですがもう少し色々テストできて欲しいです。
あるログが入力から流れてきたときにどの出力にどんなログが流れるかというのが見られると fluentdの設定を書くのがかなりやりやすくなると思います。
どうやるか
適当にログ出力部分のメソッドを書き換えれば試せるのではないかという方針です。
こちらの解説を見た感じInputプラグインとOutputプラグインというのがあって
という感じでしょうか。
Outputの生成にはFluent::Plugin.new_outputというメソッドが使われていたので この生成の段階でメソッド書き換えを行うことにしました。
Input側は特に書き換えなくてもemitというログを流すメソッドが呼び出せさえすればいいので 特に書き換えなくてもいいと思います。
問題
上の方針でやってみたところ一つ問題がありました。
Outputプラグインの種類によっては外部にログを投げない役割のものがありました。
例えばcopyやrelabelやforestなどです。
copyやrelabelの段階でメソッドを書き換えてしまうと その後まだログを外部に投げる段階まで来ていないところで終わってしまうので 有用な情報が得られません。
結局あまり良い方法が思いつかず copyやrelabelの場合はメソッドを書き換えないという適当な方法で対処しました。
作ったツール
https://github.com/bigsleep/logflowtester
こちらが作ったツールのソースです。
環境を切り離してテストできたほうがいいということでdockerコンテナで実行することにしました。
ruby全然知らないので色々と問題があるかもしれません。 設定ファイルやプラグインによっては上手く動かないこともあると思います。
テスト用設定
<source>
@type forward
port 24224
@label @raw
</source>
<source>
@type tail
path /var/log/httpd-access.log
pos_file /var/log/td-agent/httpd-access.log.pos
tag apache.access
format apache2
@label @logging
</source>
<label @raw>
<match app.**>
@type copy
<store>
@type relabel
@label @app
</store>
<store>
@type relabel
@label @logging
</store>
</match>
<match **>
@type relabel
@label @logging
</match>
</label>
<label @app>
<filter **>
@type record_transformer
<record>
tag ${tag}
</record>
</filter>
<match app.user.**>
@type kinesis_streams
region ap-northeast-1
stream_name log-stream-xxx
</match>
<match **>
@type file
path /var/log/fluentd/out_file_test
format json
buffer_type memory
compress gzip
symlink_path /path/to/symlink
append false
</match>
</label>
<label @logging>
<match **>
@type s3
s3_bucket YOUR_S3_BUCKET_NAME
path logs/${tag}/%Y/%m/%d/
s3_object_key_format %{path}%{time_slice}_%{index}.%{file_extension}
<buffer tag,time>
@type file
path /var/log/fluent/s3
timekey 3600 # 1 hour partition
timekey_wait 10m
timekey_use_utc true # use utc
</buffer>
</match>
</label>
ツール実行結果
$ docker run -v $(pwd):/data logflowtester /data/td-agent.conf '[{"tag":"app.user.action","source_index":0,"record":{"user":"xxx","action":"aaa"}},{"tag":"apache.access","source_index":1,"record":{"value":"aaa"}},{"tag":"error","source_index":0,"record":{"user":"user0","error":"fatal"}}]'
Result:
- output: #<Fluent::KinesisStreamsOutput:000000010d3198>
- tag: app.user.action
- es: #<Fluent::MultiEventStream:0x0000000000950380 @time_array=[#<Fluent::EventTime:0x0000000000955920 @sec=1518873206, @nsec=635850381>], @record_array=[{"user"=>"xxx", "action"=>"aaa", "tag"=>"app.user.action"}]>
Result:
- output: #<Fluent::Plugin::S3Output:000000019eb618>
- tag: app.user.action
- es: #<Fluent::OneEventStream:0x0000000000955150 @time=#<Fluent::EventTime:0x0000000000955920 @sec=1518873206, @nsec=635850381>, @record={"user"=>"xxx", "action"=>"aaa"}>
Result:
- output: #<Fluent::Plugin::S3Output:000000019eb618>
- tag: apache.access
- es: #<Fluent::OneEventStream:0x00000000008ad3b0 @time=#<Fluent::EventTime:0x00000000008ad428 @sec=1518873206, @nsec=636324323>, @record={"value"=>"aaa"}>
Result:
- output: #<Fluent::Plugin::S3Output:000000019eb618>
- tag: error
- es: #<Fluent::OneEventStream:0x00000000008ab9e8 @time=#<Fluent::EventTime:0x00000000008aba88 @sec=1518873206, @nsec=636373197>, @record={"user"=>"user0", "error"=>"fatal"}>
まとめ
fluentd設定テスト用のツールを書いてみました。 間に合せで作ったような状態なのでよりいいツールやWeb上でテストできるサービスなどがあるとうれしいです。