LogstashでのGrokの始め方


# はじめに

## 本投稿の目的

- Elasticsearchでログ収集を行う際、ApacheやSyslog等の代表的なものであれば、既存のgrokパターンが存在しているが、たいていはオリジナルで作成が必要なケースがほとんど。
- また運用者の分析したい観点に合わせるため、既存のgrokパターンが在ったとしても、修正したいケースがある。
- このように、Logstashのgrokパターンの生成は、Elasticsearchでのログ収集にあたって、不可欠なスキルである。にもかかわらず、Logstashを利用する上での最難関のConfigであるため、一人でも多くこの難関を超えてほしいという思いから、入門用のチュートリアルを作成する。

## 本投稿のゴール

- grokパターンを自分で作成できるようになること

# ステップ１：取り組み方

## ログは頭から順番に分解していく。

- まずは簡単なログサンプルを見てみる。以下、Syslogのとある1行。

```
Apr 23 10:17:01 oya1ELK02 CRON[3732]: (root) CMD (   cd / && run-parts --report /etc/cron.hourly)
```

- 頭から見ていくと、最初に現れているのは、タイムスタンプが表示されていることがわかる。
- そうしたら、まずはタイムスタンプとそれ以外、という風に分解してみる。
- その際のgrokパターンは以下。

```
PATTERN %{SYSLOGTIMESTAMP:time} %{GREEDYDATA:restof}
```

- PATTERN
 - まずは、定義名を付けてあげる必要があるので。PATTERNという名前にした。この名前でLogstashのConfigから後で指定する。
- %{SYSLOGTIMESTAMP:time}
 - Syslogのタイムスタンプを示す正規表現は、「SYSLOGTIMESTAMP」という定義ですでに実装されているため、それにマッチする文字列を、「time」というタグを付けてあげる。
 - 正規表現の実装が無い場合は、自分で作ってあげる必要がある。例えば「yyyy年mm月dd日」の形式の場合だと、「(¥d{4})年(0[1-9]|1[0-2])月(0[1-9]|[12][0-9]|3[01])日」のようになる。詳細はあとで説明する。
- %{GREEDYDATA:restof}
 - あらゆる文字列を無差別にマッチさせる定義「GREEDYDATA」を利用し、それに「restof」というタグを付ける。

### 参考に、logstashのconfigファイルも掲載する。

```
input {
  stdin {}
}

filter {
  grok {
    patterns_dir => ["/home/tu-itou/patterns.d"]
    match => { "message" => "%{PATTERN}" }
  }
}

output {
  stdout { codec => rubydebug }
}
```

## 実行し結果を見てみる。

- LogstashのConfigで、入出力をそれぞれ標準入出力としているため、コマンドで実行する。

```
~$ cat sample.log | sudo /usr/share/logstash/bin/logstash -f sample.conf
{
    "@timestamp" => 2017-04-23T02:21:58.017Z,
        "restof" => "oya1ELK02 CRON[3732]: (root) CMD (   cd / && run-parts --report /etc/cron.hourly)",
      "@version" => "1",
          "host" => "oya1ELK02",
          "time" => "Apr 23 10:17:01",
       "message" => "Apr 23 10:17:01 oya1ELK02 CRON[3732]: (root) CMD (   cd / && run-parts --report /etc/cron.hourly)"
}
```

- 意図どおりに、「time」というタグと、「restof」というタグに分解されていることがわかる。

# ステップ2：分解の実績を積み上げていく

- 考え方は、先頭から順番にタグ付けし切り離していき、「restof」を徐々に縮めていくこととなる。

## Logstashの既存Grokパターン

- SYSLOGTIMESTAMPのように、Logstashにはいくつかの既存パターンが存在するので、それを積極的に活用しながらgrokパターンを自作することとなる。
- Logstashの既存パターンは公式情報を参照。
 - [Grok-patterns](https://github.com/logstash-plugins/logstash-patterns-core/blob/main/patterns/legacy/grok-patterns)

## 正規表現を自作する場合

- Logstashの既存Grokパターンに存在しない文字列パターンの場合は、正規表現を自作してマッチングする必要がある。
- 以下のサイトを利用すると、簡単に作成・検証ができる。
- [Regexper](https://regexper.com "Regexper")
 - 正規表現を貼りつけて、「Display」を押すと、その正規表現でマッチされる文字列パターンが視覚的にわかる。

### 自作した正規表現は定義づけしよう

- 例えば、「yyyy年mm月dd日」という形式を「JAPANDATE」として定義づけする場合

```
JAPANDATE (¥d{4})年(0[1-9]|1[0-2])月(0[1-9]|[12][0-9]|3[01])日
```

# ステップ3：grokフィルタのパフォーマンスを考慮する

## 共通部分は別で切り出す。

- システムのログなどは、ある部分までは共通で、それ以降で複数のパターンになる、というケースが多い。
- 例えばSyslogのある10行を抜き出してみると、

```
Apr 23 10:16:14 oya1ELK02 systemd[3555]: Reached target Timers.
Apr 23 10:16:14 oya1ELK02 systemd[3555]: Reached target Sockets.
Apr 23 10:16:14 oya1ELK02 systemd[3555]: Reached target Paths.
Apr 23 10:16:14 oya1ELK02 systemd[3555]: Reached target Basic System.
Apr 23 10:16:14 oya1ELK02 systemd[3555]: Reached target Default.
Apr 23 10:16:14 oya1ELK02 systemd[3555]: Startup finished in 9ms.
Apr 23 10:16:14 oya1ELK02 systemd[1]: Started User Manager for UID 1000.
Apr 23 10:17:01 oya1ELK02 CRON[3732]: (root) CMD (   cd / && run-parts --report /etc/cron.hourly)
Apr 23 10:29:21 oya1ELK02 systemd[1]: Started Session 1069 of user tu-itou.
Apr 23 11:17:01 oya1ELK02 CRON[6424]: (root) CMD (   cd / && run-parts --report /etc/cron.hourly)
```
- タイムスタンプ、ホスト名(oya1ELK02)まで共通で、それ以降で複数のパターンとなっている。
- これを、grokフィルタを2回に分けて実行し、grokが解析する範囲を小刻みにしてやる。

```
filter {
  grok {
    "match" => { "message" => '%{SYSLOGTIMESTAMP:time} %{WORD:hostname}
 %{GREEDYDATA:message}' },
    "overwrite" => "message"
  }
  grok {
    "match" => { "message" => "%{PATTERN}" }
  }
}
```

- 参考サイト：elastic公式ブログ
 - [Do you grok grok?](https://www.elastic.co/blog/do-you-grok-grok)