fluentdのラベルの挙動

## TL;DR

ラベルを指定した場合のエミット先はプラグインの実装によって異なる。

* ラベルに対応していないプラグインの場合
    * ```@label```の有無に関わらずトップにエミットする
* ラベルに対応しているプラグインの場合
    * ```@label```を設定している場合、対応するlabelタグにエミットする
    * ```@label```を設定していない場合、自分のいるlabelタグに再エミットする

　
```<label>```タグ内で```<match>``` タグとパターンがマッチしなかったイベントについては、トップでマッチしなかった場合と同様警告がでる。

## 環境

動作は以下のバージョンで確認した。

* td-agent (2.3.5-1)
    * fluentd (0.12.36)

* fluent-plugin-rewrite-tag-filter (1.5.1, 1.5.2)
* fluent-plugin-flowcounter (0.3.0)

## ラベルに対応していないプラグインの挙動

ラベルが実装されたのがfluentd 0.12なので、まだラベルに対応していないプラグインも多い。

未対応のプラグインでは以下の記事で説明されているように```Engine.emit```でエミットが行われている（対応しているプラグインでは```Engine.emit```の代わりに```router.emit```が使われている）。

[Fluentd v0.12 ラベル機能の使い方とプラグインの改修方法 - Qiita](http://qiita.com/sonots/items/a01d2233210b7b059967)

ラベルに対応していないプラグインは、```@label```の有無に関わらず常にトップにエミットする。

例として[rewrite_tag_filter (1.5.1)](http://docs.fluentd.org/articles/out_rewrite_tag_filter)の動きを見てみる。**（rewrite_tag_filterは1.5.2でラベルに対応しています。以下のrewrite_tag_filterの挙動は例としてお読みください。）**

動作確認用に次のような設定ファイルを用意する。

rewrite_tag_filterでラベルを```@labeled```と指定し、ラベルが```@labeled```になっていれば```<label @labeled>```にエミットされ```labeled true```がレコードに追加されて出力される。ラベルが無視されトップにエミットされている場合には、```labeled false```がレコードに追加されて出力される。

```rewrite_tag_filter.conf
<source>
  @type forward
  @label @test
</source>

<label @test>
  <filter>
    @type stdout
  </filter>
  <match>
    @type rewrite_tag_filter
    @label @labeled
    rewriterule1 value .* rewritten
  </match>
</label>

<label @labeled>
  <filter>
    @type record_transformer
    <record>
      labeled true
    </record>
  </filter>
  <match>
    type stdout
  </match>
</label>

<filter>
  @type record_transformer
  <record>
    labeled false
  </record>
</filter>
<match>
  type stdout
</match>
```

上記の設定ファイルでfluentdを起動し、fluent-catでテスト用のデータを読み込ませる。

```bash
$ fluentd -c rewrite_tag_filter.conf -vv --use-v1-config &
$ echo "{\"value\": \"test\"}" | fluent-cat test
```

結果は以下のように```"labeled":"false"```になっており、ラベル```@labeled```は無視されていることがわかる。（出力は見易さのためタイムスタンプを消すなど加工しています。）

```
rewritten: {"value":"test","labeled":"false"}
```

**1.5.2以降ではラベルが有効で以下のように`"labeled":"true"`となります。**

```
rewritten: {"value":"test","labeled":"true"}
```

## ラベルに対応しているプラグインの挙動

ラベルに対応しているプラグインでは当然でラベルを指定すれば対応するラベルへ飛ぶ。
ラベルを指定していない場合には、自分のいるラベルへ再エミットされる。

### relabel

まず、例として[relabel](http://docs.fluentd.org/articles/out_relabel)プラグインの挙動を見てみる。

```relabel.conf
<source>
  @type forward
  @label @test
</source>

<label @test>
  <filter>
    @type stdout
  </filter>
  <match>
    @type relabel
  </match>
</label>

<label @labeled>
  <filter>
    @type record_transformer
    <record>
      labeled true
    </record>
  </filter>
  <match>
    type stdout
  </match>
</label>

<filter>
  @type record_transformer
  <record>
    labeled false
  </record>
</filter>
<match>
  type stdout
</match>
```

fluentdを起動しテスト用のデータを読み込ませる。

```bash
$ fluentd -c relabel.conf -vv --use-v1-config &
$ echo "{\"value\": \"test\"}" | fluent-cat test
```

結果は次のような出力が延々と繰り返される。これはrelabelの出力が再度```<label @test>```にエミットされループしているためである。

```
test: {"value":"test"}
test: {"value":"test"}
test: {"value":"test"}
test: {"value":"test"}
...
```

### flowcounter

[flowcounter](https://github.com/tagomoris/fluent-plugin-flowcounter)もラベルを指定しなかった場合には自分のいるラベルへ再エミットされる。

まず正しい動作を確認するためにラベルを指定した場合の動きを見てみる。

```flowcounter.conf
<source>
  @type forward
  @label @test
</source>

<label @test>
  <filter>
    @type stdout
  </filter>
  <match>
    @type flowcounter
    @label @labeled                                                                
    count_keys *                                                                   
    unit second 
  </match>
</label>

<label @labeled>
  <filter>
    @type record_transformer
    <record>
      labeled true
    </record>
  </filter>
  <match>
    type stdout
  </match>
</label>

<filter>
  @type record_transformer
  <record>
    labeled false
  </record>
</filter>
<match>
  type stdout
</match>
```

ラベルを指定している場合にはfluentdを起動すると、`flowcount: {"labeled":"true"}`が1秒毎に出力される。```"labeled":"true"```が付いていることから```<label @labeled>```内から出力されていることがわかる。

```bash
$ fluentd -c flowcounter.conf -vv --use-v1-config &
```

```
flowcount: {"labeled":"true"}
flowcount: {"labeled":"true"}
flowcount: {"labeled":"true"}
...
```

この状態でデータを入力するとカウントされる。testタグをつけているのでキーの頭に```test_``` が付けられている。

```
$ echo "{\"value\": \"test\"}" | fluent-cat test
```

```
flowcount: {"test_count":1,"test_bytes":12,"test_count_rate":1.0,"test_bytes_rate":12.0,"labeled":"true"}
flowcount: {"test_count":0,"test_bytes":0,"test_count_rate":0.0,"test_bytes_rate":0.0,"labeled":"true"}
flowcount: {"test_count":0,"test_bytes":0,"test_count_rate":0.0,"test_bytes_rate":0.0,"labeled":"true"}
flowcount: {"test_count":0,"test_bytes":0,"test_count_rate":0.0,"test_bytes_rate":0.0,"labeled":"true"}
...
```

次に、ラベルの指定をコメントアウトしてfluentdを再起動してみる。

```
<label @test>
  <filter>
    @type stdout
  </filter>
  <match>
    @type flowcounter
    # @label @labeled                                                                
    count_keys *                                                                   
    unit second 
  </match>
</label>
```

再起動すると何も入力しなくても次のようにカウント結果が表示される。キー名の頭が```flowcount_``` になっていることからflowcounterの出力が```<label @test>```に再エミットされ自分自身の出力をカウントしていることがわかる。

1回データを入力しただけで大量の出力が発生するrelabelのケースと異なり、1秒毎にしか出力されないので、結果をログで見ていてもループしていることに気づきにくい。

```
flowcount: {}
flowcount: {"flowcount_count":1,"flowcount_bytes":1,"flowcount_count_rate":1.0,"flowcount_bytes_rate":1.0}
flowcount: {"flowcount_count":1,"flowcount_bytes":95,"flowcount_count_rate":1.0,"flowcount_bytes_rate":95.0}
flowcount: {"flowcount_count":1,"flowcount_bytes":95,"flowcount_count_rate":1.0,"flowcount_bytes_rate":95.0}
flowcount: {"flowcount_count":1,"flowcount_bytes":95,"flowcount_count_rate":1.0,"flowcount_bytes_rate":95.0}
flowcount: {"flowcount_count":1,"flowcount_bytes":95,"flowcount_count_rate":1.0,"flowcount_bytes_rate":95.0}
...
```

この状態でデータを入力すると、以下のように入力されたデータについてがカウントされる。

```
$ echo "{\"value\": \"test\"}" | fluent-cat test
```

```
flowcount: {"flowcount_count":1,"flowcount_bytes":95,"test_count":1,"test_bytes":12,"flowcount_count_rate":1.0,"flowcount_bytes_rate":95.0,"test_count_rate":1.0,"test_bytes_rate":12.0}
flowcount: {"flowcount_count":1,"flowcount_bytes":169,"test_count":0,"test_bytes":0,"flowcount_count_rate":1.0,"flowcount_bytes_rate":169.0,"test_count_rate":0.0,"test_bytes_rate":0.0}
flowcount: {"flowcount_count":1,"flowcount_bytes":170,"test_count":0,"test_bytes":0,"flowcount_count_rate":1.0,"flowcount_bytes_rate":170.0,"test_count_rate":0.0,"test_bytes_rate":0.0}
flowcount: {"flowcount_count":1,"flowcount_bytes":170,"test_count":0,"test_bytes":0,"flowcount_count_rate":1.0,"flowcount_bytes_rate":170.0,"test_count_rate":0.0,"test_bytes_rate":0.0}
...
```

## ```<label>```タグ内で```<match>```とパターンがマッチしなかった場合

```<label @test>```から```<match>```タグを削除し、パターンがマッチしなかった場合の挙動をみる。

```no_match.conf
<source>
  @type forward
  @label @test
</source>

<label @test>
  <filter>
    @type stdout
  </filter>
</label>

<label @labeled>
  <filter>
    @type record_transformer
    <record>
      labeled true
    </record>
  </filter>
  <match>
    type stdout
  </match>
</label>

<filter>
  @type record_transformer
  <record>
    labeled false
  </record>
</filter>
<match>
  type stdout
</match>

<label @ERROR>
  <filter>
    @type record_transformer
    <record>
      error true
    </record>
  </filter>
  <match>
    type stdout
  </match>
</label>
```

fluentdを起動しデータを入力すると、```no patterns matched tag="test"```という警告がでる。fluentタグの出力に```"labeled":"false"```が付いていることからfluentタグはラベル内ではなくトップに出力されていることがわかる。

```bash
$ fluentd -c no_match.conf -vv --use-v1-config &
$ echo "{\"value\": \"test\"}" | fluent-cat test
```

```
[trace]: plugin/in_forward.rb:194:initialize: accepted fluent socket from '127.0.0.1:53776': object_id=70322129696080
test: {"value":"test"}
[warn]: fluent/agent.rb:164:emit: no patterns matched tag="test"
[trace]: plugin/in_forward.rb:262:on_close: closed socket
fluent.trace: {"message":"accepted fluent socket from '127.0.0.1:53776': object_id=70322129696080","labeled":"false"}
fluent.warn: {"tag":"test","message":"no patterns matched tag=\"test\"","labeled":"false"}
fluent.trace: {"message":"closed socket","labeled":"false"}
```



# 参考資料

* [Fluentd v0.12 ラベル機能の使い方とプラグインの改修方法 - Qiita](http://qiita.com/sonots/items/a01d2233210b7b059967)