Fluentd v0.12のリリース


https://rubygems.org/gems/fluentd

12月12日にFluentd v0.12をリリースしました．ここでは出たばかりのv0.12について書きます．v0.12はv1リリースのための準備マイナーバージョンアップの一つで，なるべく互換性を維持しつつ新機能や新しいAPIを実装しています．以下がv0.12で提供される主な新機能です．

* フィルタ
* ラベル
* ログ転送でのAt-least-once semantics
* 新しいParser/Formatterクラス

このうち，一番下の機能はv0.10にもバックポートされています．それぞれ説明していきます．

## フィルタ

Fluentdで一番待ち望まれていた機能です．Fluentdはロバストなログ転送にフォーカスして開発されているログコレクタで，貯めた後にHadoopでバッチを回したり，Prestoでアドホッククエリを投げるなどがよくある構成です．
ただ，ログを貯める前に速報値を出したいとか，ログ本体にはないデータを付加して転送したいなど，様々なユースケースが出てきました．Fluentd v0.10ではそのような場合に，`grep`，`record_reformer`，`geoip`など，Outputプラグインでフィルタ的な処理を書くプラグインが多数生まれました．しかしこれは毎回tagの書き換えを必要とし，少し分かりづらいという問題がありました．
v0.12からはもっと直感的に使えるFilterプラグインの仕組みを導入しました．

### データの流れ

v0.10では，間にフィルタ的な処理を挟む場合には，以下のような設定と処理の流れになっていました．fooというタグを使いたくてもタグの書き換えが必須なため，raw.fooなど少し工夫が必要です．

```conf
<source>
  @type tail
  tag raw.foo
</source>

<match raw.foo>
  @type filter
  remove_tag_prefix raw.
</match>

<match foo>
  @type mongo
</match>
```

    Source -> Input - raw.foo -> Output -> - foo -> Output -> Database
            |------------------ Fluentd ------------------|


v0.12のフィルタを使えば，fooというタグを使ってそのままフィルタ処理を適用して行けます．

```conf
<source>
  @type tail
  tag foo
</source>

<filter foo>
  @type filter
</match>

<match foo>
  @type mongo
</match>
```

    Source -> Input - foo -> Filter -> - foo -> Output -> Database
            |---------------- Fluentd ----------------|

フィルタでは，タグの書き換えができません．その場合には，引き続きOutputプラグインを使ってください．その代わり，タグにマッチするフィルタ群と最終的なアウトプットをまとめて一つのパイプラインにすることで，処理効率を高めています．これはフィルタ的な処理が多ければ多いほど，恩恵は増えると思います．

詳細に関しては，[Fluentd v0.12 Filter プラグインの使い方と作り方](http://qiita.com/sonots/items/25a354b5e8aa524faeef)を参照してください．気をつけることとして，フィルタはv0.12から入ったばかりでまだAPIが完全に固定されているわけではありません．フィードバックを受けてより良いAPIに変わる可能性があるので，プラグインを書く方は注意してください．

## ラベル

Fluentdでは，タグはイベントソースの識別子であり，これを使ってルーティングをします．単純な転送であれば問題にならないのですが，複数の入力があったり，タグの書き換えが発生すると，ルーティングが複雑化する傾向にありました．
そのため，ラベルという機能を導入し，ルーティングそのものはラベルで行うようにしました．これと上記のフィルタ機能を組み合わせると，よほどのことが無い限り，タグを書き換えずにイベントをルーティング出来るようになります．

```conf
<source>
  @type forward  
</source>

<source>
  @type forward
  @label @TEST
  port 24225
</source>

<match forward.**>
  @type tdlog
</match>

# labelの引数はパターンではなく，@で始まる名前
<label @TEST>
  <match forward.**>
    @type elasticsearch
  </match>
</label>
```

この例では，通常の`forward`への入力はTDに流れ，テスト用の`forward`への入力はESに流れるという感じになっています．`relabel`プラグインを使えば他のラベルに飛ぶことも出来ますし，タグの書き換えをすることなくルーティングが可能となりました．

ラベルを利用するにはプラグインにちょっとした変更が必要になるので，それらを含めての詳細は[Fluentd v0.12 ラベル機能の使い方とプラグインの改修方法](http://qiita.com/sonots/items/a01d2233210b7b059967)を参照してください．

## ログ転送でのAt-least-once semantics

Fluentdの`in_forward`/`out_forward`プラグインは今までat-most-onceしかサポートしていませんでしたが，v0.12からat-least-onceをサポートするようになりました．

```conf
<match forward.**>
  @type forward
  require_ack_response
</match>
```

`require_ack_response`を`out_forward`に設定すれば，`in_forward`がackを返すまでチェックし，タイムアウトやなんらかの理由でackが返ってこなければ，再度チャンクを再送します．
自分で試した限りだとこれといったパフォーマンス劣化は見られなかったのですが，それなりの流量がある所で試して見てもらいたい所です．

注意点として，`require_ack_response`を使うには，`out_forward`も`in_forward`もFluentd v0.12以上である必要があります．

##  新しいParser / Formatterクラス

v0.10の段階でもParser / Formatterはプラグイン化していたのですが，APIが整備されていなくてアドホックな感じになってました．それを整えて，他のInput / Outputプラグインなどと同じように使えるようにしました．
v0.10では以下のようにしてParserプラグインを実装してました．

```rb
# Parserプラグインの実装
module Fluent
  class TextParser
    class FooParser
      # 何も継承してないので，自分で基本モジュールをincludeする必要がある
      include Configurable

      # Overwrite initialize, configure and parse method

      # なぜかProc.newが必要
      TextParser.register_template('foo', Proc.new { FooParser.new })
    end
  end
end

# Parserプラグインを使う方法
parser = TextParser.new
parser.configure(conf)
```

v0.12では以下のように書けます．おまじない的なコードが減り，Inputプラグインなどと同じように書けます．

```rb
# Parserプラグインの実装
module Fluent
  class TextParser
    # Fluent::Parserクラスを継承
    class FooParser < Parser
      # 通常のプラグインと同じ登録方法
      Plugin.register_parser('foo', self)

      # Overwrite initialize, configure and parse method
    end
  end
end

# Parserプラグインを使う方法
parser = Plugin.new_parser('foo')
parser.configure(conf)
```

今回は省きますが，Formatterに関してもParserとほぼ同じように書けます．既存のAPIも残っているので今までのは使い続けられますが，新しいプラグインを作る時はこちらを推奨します．

また，この変更はv0.10.58にバックポートされているので，v0.10/v0.12両方で動くプラグインが書きやすくなっています．

## 最後に

v0.12で入った機能の中で大きめなやつを取り上げました．今回はルーティング部分などをリファクタリング含めかなり弄ったので，ユーザから見える所だけでなく，Engineの責務の分離など，内部的にも色々と改善されました．まだ互換性維持のためにいくつか無理している所もありますが，v1への過程でもっと綺麗にしていきたい所です．

v0.14ではプラグインのAPI改善やServerEngine対応，イベント時間のミリ秒以下のサポートなど，v1に向けてさらに加速していく予定ですので，来年にご期待ください！