Fluentd Config DSL と fluent-plugin-forest

久しぶりにいつか書こう、書こうと思って放置してたやつ。 DSLの書き方もすぐ忘れるので、メモ代わりにしつつ…

会社のログ管理としてFluentd: Open Source Log Managementを利用しています。

Fluentdはプラグインを追加したり、作成したりして自分の好きなようにログや、アプリからのデータ等を加工できます。

しかし、いろいろできてしまう為設定ファイルがとても長くなってしまうことも…

しかし最近ではFluentdもDSLに対応し、Rubyのコードで設定ファイルが書けるようになりました。

Fluentd界隈ではバイブルのtagomorisさんのブログより

Fluentd Config DSLについての話 - tagomorisのメモ置き場

以下に適当なサンプルを用意しました、本気をだすとどんどん長くなります。 *1

# fluentd.conf

<source>
  type forward
</source>

<match *.nginx>
  type file
  path /var/log/fluentd/nginx-*
  time_slice_format %Y%m%d-%H
  time_format %Y%m%dT%H%M%S%z
  compress gzip
</match>
<match *.app>
  type file
  path /var/log/fluentd/app-*
  time_slice_format %Y%m%d-%H
  time_format %Y%m%dT%H%M%S%z
  compress gzip
</match>
<match *.syslog>
  type file
  path /var/log/fluentd/syslog-*
  time_slice_format %Y%m%d-%H
  time_format %Y%m%dT%H%M%S%z
  compress gzip
</match>

これをFluentd Config DSLで書くとこんな感じ

# fluentd.rb

source {
  type :forward
}

['nginx','app','syslog'].each do|tag|
  match("*.#{tag}") {
    type :file
    path "/var/log/fluentd/#{tag}-*"
    time_slice_format '%Y%m%d-%H'
    time_format '%Y%m%dT%H%M%S%z'
    compress 'gzip'
  }
end

そこそこ減ったんじゃないでしょうか?

次のように実行してやると「$ fluentd -c fluentd.rb --log /tmp/fluentd.log」実行でき、 /tmp/fluentd.log の中に fluentd.rbが展開されfluentd.confと同じ内容が出力されてるのが確認できます。

Config DSLで書いていて、思った通りの設定になってるか確認したいときは実行してログなど見ると良いかも、

テストだけなら次を実行すると「$ fluentd -c fluentd.rb --dry-run」Fluentdは起動しないで、展開された設定が標準出力されます。

と、DSLの話をしてきましたがタイトルにはもうひとつ fluent-plugin-forest とあります。 このFluentdプラグインまたしてもtagomorisさんのブログを引用します。

fluent-plugin-forest released! - tagomorisのメモ置き場

私もいろんな場所で利用させて頂いています。

しかしDSLでこのプラグインを利用すると…

実行した設定ファイルはこれ

# fluentd.rb

source {
  type :forward
}

match("**") {
  type :forest
  subtype :file
  template {
     time_slice_format '%Y%m%d-%H'
     time_format '%Y%m%dT%H%M%S%z'
     compress 'gzip'
  }
  ['nginx','app','syslog'].each do|tag|
    cases("*.#{tag}.*") {
      path "/var/log/fluentd/${tag_parts[1]}/${tag}-*"
    }
  end
}

これを実行してみると

$ fluentd -c fluentd.rb --dry-run
2013-12-10 02:45:06 +0900 [info]: reading config file path="fluentd.rb"
/home/momin/.rbenv/versions/2.0.0-p353/lib/ruby/gems/2.0.0/gems/fluentd-0.10.41/lib/fluent/config_dsl.rb:28:in `instance_eval': ./fluentd.rb:16: syntax error, unexpected '{', expecting keyword_when (SyntaxError)
./fluentd.rb:19: syntax error, unexpected '}', expecting keyword_end
    from /home/momin/.rbenv/versions/2.0.0-p353/lib/ruby/gems/2.0.0/gems/fluentd-0.10.41/lib/fluent/config_dsl.rb:28:in `eval'
    from /home/momin/.rbenv/versions/2.0.0-p353/lib/ruby/gems/2.0.0/gems/fluentd-0.10.41/lib/fluent/config_dsl.rb:14:in `parse'
    from /home/momin/.rbenv/versions/2.0.0-p353/lib/ruby/gems/2.0.0/gems/fluentd-0.10.41/lib/fluent/engine.rb:72:in `parse_config'
    from /home/momin/.rbenv/versions/2.0.0-p353/lib/ruby/gems/2.0.0/gems/fluentd-0.10.41/lib/fluent/supervisor.rb:285:in `run_configure'
    from /home/momin/.rbenv/versions/2.0.0-p353/lib/ruby/gems/2.0.0/gems/fluentd-0.10.41/lib/fluent/supervisor.rb:108:in `dry_run'
    from /home/momin/.rbenv/versions/2.0.0-p353/lib/ruby/gems/2.0.0/gems/fluentd-0.10.41/lib/fluent/supervisor.rb:83:in `start'
    from /home/momin/.rbenv/versions/2.0.0-p353/lib/ruby/gems/2.0.0/gems/fluentd-0.10.41/lib/fluent/command/fluentd.rb:146:in `<top (required)>'
    from /home/momin/.rbenv/versions/2.0.0-p353/lib/ruby/2.0.0/rubygems/core_ext/kernel_require.rb:53:in `require'
    from /home/momin/.rbenv/versions/2.0.0-p353/lib/ruby/2.0.0/rubygems/core_ext/kernel_require.rb:53:in `require'
    from /home/momin/.rbenv/versions/2.0.0-p353/lib/ruby/gems/2.0.0/gems/fluentd-0.10.41/bin/fluentd:6:in `<top (required)>'
    from /home/momin/.rbenv/versions/2.0.0-p353/bin/fluentd:23:in `load'
    from /home/momin/.rbenv/versions/2.0.0-p353/bin/fluentd:23:in `<main>'

なんでかなーと思って、ちょこちょこ設定ファイル変更したりしてみても動かない…

よくよく考えてみると、fluent-plugin-forestの中には case が…はい、 つまり Fluentdの設定ではなくてRubyのコードして読み取られてたわけなんですよ… なのでちょこっとプラグインと設定ファイルを修正します

$ cd ~/.rbenv/versions/2.0.0-p353/lib/ruby/gems/2.0.0/gems/fluent-plugin-forest-0.2.2/lib/fluent/plugin
$ diff out_forest.rb out_forest.rb.bk 
44c44
<       when 'cases'
---
>       when 'case'
$ cd -
$ diff fluentd.rb fluentd.rb.bk
16c16
<     cases("*.#{tag}.*") {  
---
>     case("*.#{tag}.*") {   

特に何も考えないでcaseの後ろにsをつけてみました…

そして再度実行…

$ fluentd -c fluentd.rb --dry-run
2013-12-10 02:56:51 +0900 [info]: reading config file path="fluentd.rb"
2013-12-10 02:56:51 +0900 [info]: gem 'fluent-plugin-forest' version '0.2.2'
2013-12-10 02:56:51 +0900 [info]: gem 'fluentd' version '0.10.41'
2013-12-10 02:56:51 +0900 [info]: using configuration file: <ROOT>
  <source>
    type forward
  </source>
  <match **>
    type forest
    subtype file
    <template>
      time_slice_format %Y%m%d-%H
      time_format %Y%m%dT%H%M%S%z
      compress gzip
    </template>
    <cases *.nginx.*>
      path /var/log/fluentd/${tag_parts[1]}/${tag}-*
    </cases>
    <cases *.app.*>
      path /var/log/fluentd/${tag_parts[1]}/${tag}-*
    </cases>
    <cases *.syslog.*>
      path /var/log/fluentd/${tag_parts[1]}/${tag}-*
    </cases>
  </match>
</ROOT>
2013-12-10 02:56:51 +0900 [info]: adding source type="forward"
2013-12-10 02:56:51 +0900 [info]: adding match pattern="**" type="forest"

するとちゃんと動作してくれました。

実際会社ではDSL使ってないのでこんな修正はいらないのですが、 もしDSL使ってfluent-plugin-forest利用される場合は考慮いただけたらなと思います。

*1:ファイルを分けてincludeする手もありますけど…