Filebeatで複数行を扱うMultiline設定まとめ

## 概要

Filebeatにて複数行のメッセージを1イベントとして取り扱うためのMultiline設定をまとめてみました。

公式ドキュメントは下記となります。
https://www.elastic.co/guide/en/beats/filebeat/current/configuration-filebeat-options.html#multiline

## 環境

Filebeat: v1.2.3

## 本題に入る前に

Multilineの話をする前に、まずはFilebeatのデフォルトの仕様について整理します。

Filebeatは指定したファイルを読込み、logstashやElasticsearchに情報（イベント）をアウトプットするShipperとなります。
通常、読込みファイルの1行につき1イベントがアウトプットされるため、
連携先のElasticsearchでは読込みファイルの1行が1ドキュメント（RDBでいうところのレコード）として登録されます。

下記のログを取り込んだ場合は合計3ドキュメントが登録される形になります。

```
[12/May/2014:21:14:56 +0900] "GET /index.html HTTP/1.1" 304 - "-" "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:29.0) Gecko/20100101 Firefox/29.0"
[12/May/2014:21:14:59 +0900] "GET /test.html HTTP/1.1" 304 - "-" "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:29.0) Gecko/20100101 Firefox/29.0"
[12/May/2014:21:14:51 +0900] "GET /sleep.php HTTP/1.1" 200 - "-" "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:29.0) Gecko/20100101 Firefox/29.0"
```

## Multilineのユースケース

通常は1行1イベントとして扱うことで何も問題がないのですが、
Javaのスタックトレースのようにメッセージが複数行に渡る場合に、複数行を1イベントとして扱いたい場合があります。
（下記のログメッセージは4ドキュメントではなく1ドキュメントとしてElasticsearchに取り込みたいという具合）

```
Exception in thread "main" java.lang.NullPointerException
        at com.example.myproject.Book.getTitle(Book.java:16)
        at com.example.myproject.Author.getBookTitles(Author.java:25)
        at com.example.myproject.Bootstrap.main(Bootstrap.java:14)
```

## 設定項目

Filebeatの設定ファイルである `filebeat.yml` の `multiline` セクションを編集します。

```yaml:filebeat.yml
      # Mutiline can be used for log messages spanning multiple lines. This is common
      # for Java Stack Traces or C-Line Continuation
      #multiline:

        # The regexp Pattern that has to be matched. The example pattern matches all lines starting with [
        #pattern: ^\[

        # Defines if the pattern set under pattern should be negated or not. Default is false.
        #negate: false

        # Match can be set to "after" or "before". It is used to define if lines should be append to a pattern
        # that was (not) matched before or after or as long as a pattern is not matched based on negate.
        # Note: After is the equivalent to previous and before is the equivalent to to next in Logstash
        #match: after

        # The maximum number of lines that are combined to one event.
        # In case there are more the max_lines the additional lines are discarded.
        # Default is 500
        #max_lines: 500

        # After the defined timeout, an multiline event is sent even if no new pattern was found to start a new event
        # Default is 5s.
        #timeout: 5s
```

設定項目は下記の通りとなります。

### pattern

正規表現パターン

### negate

正規表現にマッチするものを扱うか、マッチしないものを扱うか
`ture` もしくは `false` を指定（デフォルトは `false`）

### match

マッチする行をどのように扱うか
`before` もしくは `after` を指定

### maxlines

連続する行の最大行数（デフォルトは500）

### timeout

連続する行が見つらない場合のタイムアウト値（デフォルトは5s）  

## 具体例

なんとも直観的に分かりづらいですが、 `negate` と `match` の組み合わせで挙動が変わるので具体例を交えて説明します。

| No | negate | match  | 説明                                                                  |
|----|--------|--------|-----------------------------------------------------------------------|
| 1 | false  | after  | patternにマッチ**する**連続した行が、前のマッチ**しない**行の**後に**追加される |
| 2 | false  | before | patternにマッチ**する**連続した行が、次のマッチ**しない**行の**前に**追加される   |
| 3 | true   | after  | patternにマッチ**しない**連続した行が、**前の**マッチ**する**行の**後に**追加される |
| 4 | true   | before | patternにマッチ**しない**連続した行が、**次の**マッチ**する**行の**前に**追加される   |

例えば下記のようなスタックトレースの全文を1イベントとして扱いたい場合を考えてみましょう。

```
Exception in thread "main" java.lang.NullPointerException
        at com.example.myproject.Book.getTitle(Book.java:16)
        at com.example.myproject.Author.getBookTitles(Author.java:25)
        at com.example.myproject.Bootstrap.main(Bootstrap.java:14)
```

この場合はNo1の設定を適用することができます。

```yaml
multiline:
  pattern: '^[[:space:]]'
  negate: false
  match: after
```

これはpatternにマッチする（先頭がスペースで始まる）連続した行（2行目から4行目）が、前のマッチしない行（1行目）の後に追加され、1イベントとしてアウトプットされます。


もう1つ例として、下記のようにタイムスタンプをキーとしたまとまりを1イベントとして扱いたい場合を考えてみましょう。

```
[2015-08-24 11:49:14,389][INFO ][env                      ] [Letha] using [1] data paths, mounts [[/
(/dev/disk1)]], net usable_space [34.5gb], net total_space [118.9gb], types [hfs]
(/dev/disk1)]], net usable_space [34.5gb], net total_space [118.9gb], types [hfs]
[2015-08-24 11:49:15,567][INFO ][env                      ] [Letha] using [1] data paths, mounts [[/
(/dev/disk1)]], net usable_space [34.5gb], net total_space [118.9gb], types [hfs]
(/dev/disk1)]], net usable_space [34.5gb], net total_space [118.9gb], types [hfs]
```

この場合はNo3の設定を適用することができます。

```yaml
multiline:
  pattern: '^\[[0-9]{4}-[0-9]{2}-[0-9]{2}'
  negate: true
  match: after
```

これはpatternにマッチしない連続した行（2行目から3行目）が、前のマッチする行（1行目）の後に追加され、1イベントとしてアウトプットされます。