fluentdでIPアドレスから地理情報を付与

fluent-plugin-geoip というプラグインを使うとfluentdで流れてくるログに含まれるIPアドレスから国名などの地名や経緯度情報を付与できます。

こんな感じのログ

```json
{
    "ip":"XXX.XXX.XXX.XXX",
    "msg":"Hello",
}
```

を、こんな感じに変換できます。

```json
{
    "ip":"XXX.XXX.XXX.XXX",
    "msg":"Hello",
    "country_code":"JP",
    "country_name":"Japan",
    "latitude":35.69,
    "longitude":139.69
}
```

MaxMind社のGeoIPのデータベースを利用しているようです。


# インストール

インストール方法はGitHubレポジトリのREADMEに書いてあります。

[y-ken/fluent-plugin-geoip: Fluentd output plugin to geolocate with geoip.](https://github.com/y-ken/fluent-plugin-geoip)

gemでfluentdをインストールしてあるのであれば

```console
$ gem install -N fluent-plugin-geoip
```

※td-agentであれば `td-agent-gem` でインストールします。


# fluentdの設定ファイル

以下のような設定ファイルで試せます。

```conf
<source>
  @type tail
  path input.txt
  pos_file input.pos
  tag test
  <parse>
    @type csv
    keys ip,msg
  </parse>
</source>
<filter test>
  @type geoip
  geoip_lookup_keys ip
  backend_library geoip2_c
  skip_adding_null_record true
  <record>
    country_code    ${country.iso_code["ip"]}
    country_name    ${country.names.en["ip"]}
    latitude        ${location.latitude["ip"]}
    longitude       ${location.longitude["ip"]}
  </record>
</filter>
<match test>
  @type stdout
</match>
```

ファイルからのコンマ区切りの入力としています。


# 試してみる

この設定ファイルでfluentdを起動しておいて、別のターミナルなどで次のようにデータを送ります。 `XXX.XXX.XXX.XXX` のところはIPアドレスです。

```console
$ echo "XXX.XXX.XXX.XXX,Hello" >> input.txt
```

fluentdからは次のように出力されました。

```
2020-07-18 12:54:04.999460343 +0900 test: {"ip":"XXX.XXX.XXX.XXX","msg":"Hello","country_code":"JP","country_name":"Japan","latitude":35.69,"longitude":139.69}
```

※経緯度を見ても正確に指しているようには見えませんでしたが、国レベルでは合ってました。


# skip_adding_null_record

上記サンプルの設定ファイルでは `skip_adding_null_record true` という記述を入れているので、地理情報のないプライベートIPを入れてみると、地理情報のキーは付与されません。

```console
$ echo "192.168.0.1,Hello" >> input.txt
```

```
2020-07-18 12:58:01.055174554 +0900 test: {"ip":"192.168.0.1","msg":"Hello"}
```


設定ファイルから `skip_adding_null_record true` を省くと `null` として出力されます。

```
2020-07-18 12:59:55.737619793 +0900 test: {"ip":"192.168.0.1","msg":"Hello","country_code":null,"country_name":null,"latitude":null,"longitude":null}
```


# コマンドラインでGeoIPのデータベースに問い合わせてみる

fluent-plugin-geoipのソースの中に `utils/dump.rb` という名前でコマンドラインから簡単にGeoIPを試せるスクリプトがあります。

これを動かすには私の環境では以下のgemを手動でインストールすることが必要でした。

```console
$ gem install -N geoip2_compat
```

`utils/dump.rb` をrubyコマンドで直接呼び出します。

1つ目のパラメータはバックエンドのライブラリを指定するもので、 `geoip2`, `geoip2_compat`, `geoip` の3種類あります。

2つ目のパラメータはIPです。

```console
$ ruby ~/.rbenv/versions/2.7.1/lib/ruby/gems/2.7.0/gems/fluent-plugin-geoip-1.3.2/utils/dump.rb geoip2 XXX.XXX.XXX.XXX
{"continent"=>
  {"code"=>"AS",
   "geoname_id"=>6255147,
   "names"=>
    {"de"=>"Asien",
     "en"=>"Asia",
     "es"=>"Asia",
     "fr"=>"Asie",
     "ja"=>"アジア",
     "pt-BR"=>"Ásia",
     "ru"=>"Азия",
     "zh-CN"=>"亚洲"}},
 "country"=>
  {"geoname_id"=>1861060,
   "iso_code"=>"JP",
   "names"=>
    {"de"=>"Japan",
     "en"=>"Japan",
     "es"=>"Japón",
     "fr"=>"Japon",
     "ja"=>"日本",
     "pt-BR"=>"Japão",
     "ru"=>"Япония",
     "zh-CN"=>"日本"}},
 "location"=>
  {"accuracy_radius"=>500,
   "latitude"=>35.69,
   "longitude"=>139.69,
   "time_zone"=>"Asia/Tokyo"},
 "registered_country"=>
  {"geoname_id"=>1861060,
   "iso_code"=>"JP",
   "names"=>
    {"de"=>"Japan",
     "en"=>"Japan",
     "es"=>"Japón",
     "fr"=>"Japon",
     "ja"=>"日本",
     "pt-BR"=>"Japão",
     "ru"=>"Япония",
     "zh-CN"=>"日本"}}}
```

```console
$ ruby ~/.rbenv/versions/2.7.1/lib/ruby/gems/2.7.0/gems/fluent-plugin-geoip-1.3.2/utils/dump.rb geoip2_compat XXX.XXX.XXX.XXX
{:country_code=>"JP",
 :country_name=>"Japan",
 :latitude=>35.69,
 :longitude=>139.69}
```

```console
$ ruby ~/.rbenv/versions/2.7.1/lib/ruby/gems/2.7.0/gems/fluent-plugin-geoip-1.3.2/utils/dump.rb geoip XXX.XXX.XXX.XXX
{:country_code=>"JP",
 :country_code3=>"JPN",
 :country_name=>"Japan",
 :latitude=>35.689998626708984,
 :longitude=>139.69000244140625}
```