fluentdで不要なログを取り除くfilterプラグインを作った

# <i class="fa fa-cubes" style="font-size:1em;"></i> 概要
Fluentd v0.12から利用できるFilterの機能を利用して、ログから正規表現でマッチしたものを取り除く[fluent-plugin-ignore-filter](https://github.com/bungoume/fluent-plugin-ignore-filter)を作りました。


# <i class="fa fa-question-circle" style="font-size:1em;"></i> grep-filterと何が違うのか

Fluentd v0.12以降には [grep Filter Plugin](http://docs.fluentd.org/articles/filter_grep) という @sonots さんの
[fluent-plugin-grep](https://github.com/sonots/fluent-plugin-grep) を取り込んだフィルタが組み込まれています。

こちらはログから正規表現で「マッチしたものを抽出する」プラグインです。

grepフィルタにはexcludeオプションがあるため、こちらを利用することで不要なログを取り除くことは一応可能です。

しかし、こちらのプラグインは「マッチしたログを通す」ためのフィルタのため、
マッチしたものを除外する目的で利用しようとすると、パース済みのログに対しand条件で除外できないという問題がありました。

例えば、ELBからのヘルスチェックだけ取り除きたいときに、`path:/healthcheck.txt`かつ `client_ip:10.0.0.0/16` のログのみを除外するといった記述ができません。


```xml+erb
<filter access.nginx.**>
  @type grep
  exclude1 path ^/healthcheck\.txt$
  exclude2 client_ip ^10\.0\.
</filter>
```

と記述するとexcludeがor条件になってしまうため、`path:/healthcheck.txt` すべてと `client_ip:10.0.0.0/16` すべてが取り除かれてしまいます。

私が作成したignoreフィルタは「マッチしたものを取り除く」ので、grepフィルタとは逆の動作になります。

grep-pluginはもともとmatch向けのため、必要なものを通すという記法が理解しやすかったのだと思います。
しかし、filterプラグインは何重にも掛けることが出来るため、「必要なものを通す」を1つ書くよりは「不要なものを除く」記述を複数書くほうが理解しやすくなると思い、作成しました。


# <i class="fa fa-cube" style="font-size:1em;"></i> 使い方

## インストール
```bash
# for fluentd
$ gem install fluent-plugin-ignore-filter

# for td-agent2
$ sudo td-agent-gem install fluent-plugin-ignore-filter
```

## 記法
記法はgrepフィルタとほぼ同じです。
`regexpN <key> <pattern>` 全てにマッチしたもののうち、
`excludeN <key> <pattern>` 全てにマッチしないもの
を除外します。

# <i class="fa fa-flask" style="font-size:1em;"></i> 具体例

### ELBからのヘルスチェックだけ取り除きたい
ELBはVPCの中なのでclient_ipがprivate_ipなになる。
（client_ipにはx_forwarded_forの最後の値か、なければremote_ipが入っている設定）

```xml
<filter access.nginx.**>
  @type ignore
  regexp1 path ^/healthcheck\.txt$
  regexp2 client_ip ^10\.0\.
</filter>
```

### syslogから特定のメッセージ以外のinfo|noticeを除外したい
corosync使ってるとcrmdがnoticeレベルで結構重要な情報を出すので
`level:notice` でも`ident:crmd`かつ `message:process_lrm_event〜` は通したいとき。

```xml
<filter alert.messages.**>
  @type ignore
  regexp1 level info
</filter>
<filter alert.messages.**>
  @type ignore
  regexp1 level notice
  exclude1 ident crmd
</filter>
<filter alert.messages.**>
  @type ignore
  regexp1 level notice
  regexp2 ident crmd
  exclude1 message ^process_lrm_event
</filter>
```

Filterは何重にも書けられるので便利ですね！

# <i class="fa fa-cube" style="font-size:1em;"></i> 補足
* Fluentdの[filter_grep.rb](https://github.com/fluent/fluentd/blob/master/lib/fluent/plugin/filter_grep.rb) をかなり参考にさせてもらっています。 @sonots さんありがとうございます。
    * grep Filterも併用してわかりやすい記述にするのが良さそうです。
* Rubyは初めて書いたので変なところがあるかもしれません。Pull-request歓迎です！
    * <i class="fa fa-arrow-right" style="font-size:1em;"></i> <i class="fa fa-github-square" style="font-size:1em;"></i> [fluent-plugin-ignore-filter](https://github.com/bungoume/fluent-plugin-ignore-filter) 


# <i class="fa fa-cube" style="font-size:1em;"></i> その他
* 同様のプラグインはあるかもしれません。
    * 名称をexclude-filterにしようとしたら[既に存在](https://github.com/yu-yamada/fluent-plugin-exclude-filter)していました。(ただしfilter pluginではないようです。）
* Filterプラグインはまだ数が少ないので、ネーミングルールが分かりませんでした。
    * 名称がFilterでもout pluginが多いですね。
* ignoreのexcludeって分かりにくい気はしています。
    * 仕方ないですかね...