Edited at

fluentdでteeっぽいことをやりたくなった時はlabelを使うと簡単

More than 3 years have passed since last update.


最初のシチュエーション(前提)

AWS上のインスタンスで動かしているプログラムのログやsyslog、インスタンスが止まったら消えてしまうのでS3にアップロードしたい、という事はよくあります。

そんな時に書く設定はだいたいこんな感じでしょうか。

<source>

@type tail
path /var/log/aaa.log
pos_file /var/log/td-agent/pos/aaa.pos
tag server.log.aaa
format none
</source>
<source>
@type tail
path /var/log/bbb.log
pos_file /var/log/td-agent/pos/bbb.pos
tag server.log.bbb
format none
</source>
<source>
@type tail
path /var/log/ccc.log
pos_file /var/log/td-agent/pos/ccc.pos
tag server.log.ccc
format none
</source>

<filter server.log.**>
@type record_transformer
<record>
hostname "#{Socket.gethostname}"
</record>
</filter>

<match server.log.**>
@type forest
subtype s3
remove_prefix server.log
<template>
aws_key_id {{aws_key}}
aws_sec_key {{aws_secret}}
s3_bucket server_log
s3_region ap-northeast-1
format single_value
message_key message
store_as gzip
path ${tag}/
s3_object_key_format %{path}%{time_slice}-%{hostname}_%{index}.%{file_extension}
buffer_path /var/log/td-agent/buffer/${tag}
time_slice_format %Y%m%d-%H
time_slice_wait 5m
utc
</template>
</match>

forestを使って記述をまとめていますが、まあフツーですね。


次なる要望(本題)

運用部門から、「bbb.logが出る時は障害なので、slackにログの内容を通知して欲しい」と言われました。


NGパターンその1

まともに動かないパターン

(前略)

<match server.log.bbb>
@type slack
webhook_url {{webhook_url}}
channel {{alert_channel}}
username Server_Alert
icon_emoji :no_entry_sign:
flush_interval 1m
</match>

<match server.log.**>
@type forest
subtype s3
remove_prefix server.log
<template>
aws_key_id {{aws_key}}
aws_sec_key {{aws_secret}}
s3_bucket server_log
s3_region ap-northeast-1
format single_value
message_key message
store_as gzip
path ${tag}/
s3_object_key_format %{path}%{time_slice}-%{hostname}_%{index}.%{file_extension}
buffer_path /var/log/td-agent/buffer/${tag}
time_slice_format %Y%m%d-%H
time_slice_wait 5m
utc
</template>
</match>

よくあるミス(訳:よくあるということにしてこんな恥ずかしいミスをするのは自分だけではない事にしたい)、 server.log.bbbserver.log.** にマッチする前に処理が終わってしまうので、これではbbb.logがS3に上がらなくなってしまいます。


NGパターンその2

NGではない(想定どおりの動きはする)けどなんかしょっぱいパターン

(前略)

<match server.log.bbb>
@type copy
<store>
type slack
webhook_url {{webhook_url}}
channel {{alert_channel}}
username Server_Alert
icon_emoji :no_entry_sign:
flush_interval 1m
</store>
<store>
type s3
aws_key_id {{aws_key}}
aws_sec_key {{aws_secret}}
s3_bucket server_log
s3_region ap-northeast-1
format single_value
message_key message
store_as gzip
path bbb/
s3_object_key_format %{path}%{time_slice}-%{hostname}_%{index}.%{file_extension}
buffer_path /var/log/td-agent/buffer/${tag}
time_slice_format %Y%m%d-%H
time_slice_wait 5m
utc
</store>
</match>

<match server.log.**>
@type forest
subtype s3
remove_prefix server.log
<template>
aws_key_id {{aws_key}}
aws_sec_key {{aws_secret}}
s3_bucket server_log
s3_region ap-northeast-1
format single_value
message_key message
store_as gzip
path ${tag}/
s3_object_key_format %{path}%{time_slice}-%{hostname}_%{index}.%{file_extension}
buffer_path /var/log/td-agent/buffer/${tag}
time_slice_format %Y%m%d-%H
time_slice_wait 5m
utc
</template>
</match>

せっかくforestで設定を分けたのに、コピペが発生して美しくないです。


じゃあどうするの

こんな時こそlabelの出番!

Fluentd v0.12 is Released - Fluentd Blog

Fluentd v0.12 ラベル機能の使い方とプラグインの改修方法

Fluentd v0.12でのFilterとLabel - Go ahead!

<source>

@type tail
path /var/log/aaa.log
pos_file /var/log/td-agent/pos/aaa.pos
tag server.log.aaa
format none
</source>
<source>
@type tail
path /var/log/bbb.log
pos_file /var/log/td-agent/pos/bbb.pos
tag server.log.bbb
format none
@label @tee_slack # <- A
</source>
<source>
@type tail
path /var/log/ccc.log
pos_file /var/log/td-agent/pos/ccc.pos
tag server.log.ccc
format none
</source>

<label @tee_slack> # <- B
<match server.log.bbb>
@type copy
<store>
type slack
webhook_url {{webhook_url}}
channel {{alert_channel}}
username Server_Alert
icon_emoji :no_entry_sign:
flush_interval 1m
</store>
<store>
type relabel
@label @to_s3
</store>
</match>
</label>

<label @to_s3> # <- D
<filter server.log.**>
@type record_transformer
<record>
hostname "#{Socket.gethostname}"
</record>
</filter>

<match server.log.**>
@type forest
subtype s3
remove_prefix server.log
<template>
aws_key_id {{aws_key}}
aws_sec_key {{aws_secret}}
s3_bucket server_log
s3_region ap-northeast-1
format single_value
message_key message
store_as gzip
path ${tag}/
s3_object_key_format %{path}%{time_slice}-%{hostname}_%{index}.%{file_extension}
buffer_path /var/log/td-agent/buffer/${tag}
time_slice_format %Y%m%d-%H
time_slice_wait 5m
utc
</template>
</match>
</label>

<match server.log.**> # <- C
@type relabel
@label @to_s3
</match>


設定内容の解説


  1. slackに流したいログファイルのsourceに @label @tee_slack をつける (上記A


  2. @tee_slack のラベルがついたログはまずBに到達、slackにログの内容を投げつけつつ、relabelプラグインによりlabelが @to_s3 に変化

  3. ラベルがついていないログはCに到達、relabelプラグインにより @to_s3 のラベルが付与される


  4. @to_s3 のラベルがついたログはDに到達、タグに応じたKeyによってS3に投げられる

順番に追っていけば簡単ですね。


嬉しいこと

もちろんrewrite-tag-filterプラグイン等を使えば、タグの付け替えで同じことはできます。

labelを使う方法が嬉しいのは、 既存のmatchルールについては <label></label> で囲むだけで良く、書き換えの必要はほとんどないという点です。特にタグの命名規則に変更がないので、タグに依存する設定(この例ではforest)に影響が出ないというのは大きな利点だと思います