COBOLレコードのElasticsearchへの取り込み - (3)実行結果/まとめ

# はじめに
COBOLハッカソン2020関連記事その3です。
先の記事に示した通り、ここまでで、主要な要素技術についての確認が済みましたので、ここからはいよいよ仮想のシナリオでバッチをいくつか流してCOBOLからElasticsearchまで一気通貫でデータを流してKibanaで結果を確認してみます。

##関連記事

[(1) コンセプト](https://qiita.com/teamcimkcs2020/private/cb3e8a020e414f7acad3)
[(2) 要素技術](https://qiita.com/teamcimkcs2020/private/5689e95b83052500a5fa)
[(3) 実行結果/まとめ](https://qiita.com/teamcimkcs2020/private/d3582ec41fb580ee3b72) <=当記事


#シナリオ(1) - 卸売業者の受注伝票の取込み

仮想の卸売業者の受注伝票を出力するようなダミーのアプリケーションを想定し、その受注情報をElasticsearchに取り込んで可視化する、というようなことをやってみます。

## 業務データ構造

取り扱うデータの構造(Copybook)としては以下のようなものを定義しました。

```xxx:order.cpy
           03 ORDER-ID              PIC X(18).
           03 ORDER-DATE            PIC X(10).
           03 ORDER-TIME            PIC X(8).
           03 ITEM.
             05 I-CODE              PIC X(7).
             05 I-NAME              PIC X(60).
             05 I-PRICE             PIC 9(8).
             05 I-VENDORID          PIC X(4).
           03 ORDER-QUANTITY        PIC 9(8).
           03 SHOP-INFO.
             05 S-ID                PIC X(6).
             05 S-NAME              PIC X(60).
             05 S-ZIP               PIC X(7).
             05 S-ADDRESS           PIC X(120).
             05 S-TEL               PIC X(12).
```

小売店(全国チェーンの薬局)から、商品(トイレットペーパー、ボックスティッシュなど)の注文を受けた時の注文データというイメージです(実際にはこれだと情報不足かと思いますが、ここでは簡素化のために上の情報に絞っています)。

生成されるデータは以下のようなイメージです。

```
OID20200113_2246242020/01/1322:46:24ITEM007トイレットペーパー１８ロール・ソフト・シングル              00000200V00100000067S01069一之江店
                  1320024東京都江戸川区一之江3-2-39                                                                                              03-5879-6098
OID20200130_1804262020/01/3018:04:26ITEM003トイレットペーパー１８ロール・シングル                      00000200V00100000097S00923五日市コイン通り店
                  7315128広島県広島市佐伯区五日市中央1-1-3                                                                                       082-943-8510
OID20200102_0708242020/01/0207:08:24ITEM001トイレットペーパー１２ロール・シングル                      00000180V00100000040S00312相馬店
                  9760013福島県相馬市小泉字根岸96番地1                                                                                           0244-37-3666
OID20200115_2045212020/01/1520:45:21ITEM003トイレットペーパー１８ロール・シングル                      00000200V00100000042S01770中土佐久礼店
                  7891301高知県高岡郡中土佐町久礼2256-5                                                                                          0889-52-3268
OID20200123_2141372020/01/2321:41:37ITEM010トイレットペーパー１２ロール・プレミアム・ダブル            00000300V00100000001S01727函館的場店
                  0400021函館市的場町10番3号                                                                                                     0138-30-1268
```

※補足
主に以下の情報を元にランダムにデータを生成させています(約10万レコード)。
店舗情報: 約2,000件
商品情報: 15アイテム
日付: 2020/01/01～2020/01/31
(最終的に地図を用いた可視化を行うため、住所は実際のものを使用する必要があったので、店舗情報は某ドラッグストアの情報をWebスクレイピングして取得しました。商品情報は適当に架空のものを作成しました。)

## データの取り込み

さて、COBOLバッチで出力された上のようなデータ構造のものをCopybookを元にJSON化して取り込むのですが、ここではさらに一工夫加えたいと思います。

取り扱う情報に住所の情報があるので、地図を使った可視化を行えるように、緯度/経度の情報を付与しつつデータの取り込みを行いたいと思います。

緯度/経度情報を取得するサービスをAPIで提供しているものがあったりするようですが、色々と情報を漁っていると、郵便番号と緯度/経度の情報の対応をcsv化してくれているのを見つけたので、ここではそれを流用させてもらうことにします(網羅性がどこまであるか未確認ですが、ここでは正確性は求めていないのでそのまま使います)。
参考:[郵便番号を緯度経度に変換する](https://renztech.wordpress.com/2018/02/28/%E9%83%B5%E4%BE%BF%E7%95%AA%E5%8F%B7%E3%82%92%E7%B7%AF%E5%BA%A6%E7%B5%8C%E5%BA%A6%E3%81%AB%E5%A4%89%E6%8F%9B%E3%81%99%E3%82%8B/)
ここで入手したcsvを加工して、郵便番号と緯度/経度情報のみを抽出して以下のようなymlファイルを作成します。

```yml:dfZip2Geoc.yml
"0600000": "43.05513,141.34103"
"0640941": "43.05513,141.34103"
"0600041": "43.05513,141.34103"
"0600042": "43.05513,141.34103"
"0640820": "43.05513,141.34103"
"0600031": "43.05513,141.34103"
...
```

緯度/経度情報は、Elasticsearch上のGeo-point datatypeにマッピングさせる想定ですので、その形式に合わせたデータとして作成しておきます。
参考 [Geo-point datatype](https://www.elastic.co/guide/en/elasticsearch/reference/current/geo-point.html)

Logstashでは、このような値のマッピング情報を元にして、値を変換してくれるtranslate filter pluginというものが提供されています。
参考: [translate filter plugin](https://www.elastic.co/guide/en/logstash/current/plugins-filters-translate.html)
このプラグインを使って、上のymlを辞書として使って、zipから緯度経度のフィールドを追加します。
logstashの構成ファイルのイメージとしては以下のようになります。

```ruby:logstash.conf
...
filter {
...
	translate {
		dictionary_path => "/etc/logstash/config_cobol01/zip2geoc.yml"
		field => "[SHOP-INFO][S-ZIP]"
		destination => "location"
		fallback => "Unknown"
	}
...
}
...
```

さらに、タイムスタンプもレコードの中に提供されている日付、時間をベースにしたいので、そのための設定も行います。

```ruby:logstash.conf
...
filter {
...
      mutate{ add_field => {
         "[@metadata][timestamp]" => "%{[ORDER-DATE]} %{[ORDER-TIME]}"
        }}

      date{ match => [
             "[@metadata][timestamp]", "yyyy/MM/dd HH:mm:ss"
        ]}
...
}
...
```

また、Elasticsearch側では、緯度/経度情報のフィールドを特定のデータタイプとして認識させる必要があるので、そのためのindex templateを作成しておきます。

```
PUT _template/order
{
  "index_patterns": ["order-*"],
  "settings": {
    "number_of_shards": 1,
    "number_of_replicas" : 0
  },
  "mappings": {
    "properties": {
      "location": {
        "type": "geo_point"

      }
    }
  }
}
```

## Kibanaによる可視化

上のような設定で取り込んだElasticsearch上のデータをKibanaで確認してみます。

### 受注データの確認

KibanaのDiscover画面の例です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/595911/befa52e8-acb7-6c1c-2d3c-d37fa2d4f9dd.png)



特定の期間の受注情報が確認できます。
フィールドごとにフィルターをかけたりソートをかけたり、参照したい時間帯のログを抽出したり、ということがGUIベースで容易に操作できます。
どのような注文がどの期間にどれだけ出ているか、といった集計も簡単に行えます。

### 緯度/経度情報を利用した可視化

データ取り込みの際に、発注元店舗の郵便番号から緯度/経度情報をマッピングして取り込んでいるので、その情報を利用した可視化を行ってみましょう。

KibanaのVisualizeでCoordinate Mapを使用すると地図情報と合わせた可視化が行えます。
例えば、特定の商品がどの地域からどのくらい受注されたかを以下のように可視化できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/595911/92e5c051-7daa-674a-c6c7-001ecb30cca0.png)


ズームして詳細化した様子
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/595911/0bffad0f-45bf-3740-d64b-52475b81b948.png)



#シナリオ(2) - アプリケーション・ログの取り込み
処理結果をファイルに出力する単純なバッチを用意し、5多重で実行します。
一連のバッチ処理を約30分バッチを流せるようにする。
この時出力される、レコードベースのアプリケーション・ログをElasticsearchに取り込んでみます。
アプリケーション・ログのフォーマットは、前述の[レコードベースのデータの取り込み(2)](https://qiita.com/teamcimkcs2020/private/5689e95b83052500a5fa#%E3%83%AC%E3%82%B3%E3%83%BC%E3%83%89%E3%83%99%E3%83%BC%E3%82%B9%E3%81%AE%E3%83%87%E3%83%BC%E3%82%BF%E3%81%AE%E5%8F%96%E3%82%8A%E8%BE%BC%E3%81%BF2)で示したフォーマットと同じものを使用します。

## データの取り込み

こちらも取り込み時に一工夫加えます。
今回取り込む予定のアプリケーションログには、バッチの開始と終了を表す以下のようなメッセージが含まれます。

```
2020/03/14-13:09:17.71 BATCH001 MSGID001: Begin Program
...
2020/03/14-13:09:25.85 BATCH001 MSGID002: End Program
```

これらの2つのメッセージのタイムスタンプの差分を取ることで、バッチの処理時間(Elapsed Time)を取得することができます。Logstashでは、このようなElapsed Timeを取得するためのプラグインが用意されているので、それを利用してElapsed Timeの情報を付与しつつ、データの取り込みを行います。
参考: [Elapsed filter plugin](https://www.elastic.co/guide/en/logstash/current/plugins-filters-elapsed.html)

Elapsed time pluginを利用してデータを取り込むために、Logstash構成ファイルに以下のような設定を追加します。

```ruby:logstash.conf
...
filter {
...
        if [msg_id] == "MSGID001" {
                mutate { add_tag => ["taskStarted"] }
        } else if [msg_id] == "MSGID002" {
                mutate { add_tag => ["taskTerminated"] }
        }

        elapsed {
                start_tag => "taskStarted"
                end_tag => "taskTerminated"
                unique_id_field => "[batch_id]"
                timeout => 10000
                new_event_on_match => false
        }
...
```

これでバッチごとのElapsed timeの情報が付与されるようになります。

## Kibanaによる可視化

### アプリケーション・ログ参照

KibanaのDiscover画面の例です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/595911/61c9bae8-a365-fdc4-e5a0-4d5ad3fb1d16.png)

アプリケーション・ログも同様にKibanaで見てみると、他のミドルウェアのログなどと同じように、時系列データとして確認できます。
フィールドごとにフィルターをかけたりソートをかけたり、参照したい時間帯のログを抽出したり、ということがGUIベースで容易に操作できます。
どのようなメッセージがどの時間帯にどれだけ出ているか、といった集計も簡単に行えます。
何か問題が発生した際の切り分けなどで、目的のメッセージに辿り着くのが容易になるかもしれません。

### メトリック情報可視化

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/595911/31bb98a3-e4ec-c53d-cb30-05d2eec5e5cd.png)

Visutalizeの機能を使ってバッチ処理の経過などを可視化することができます。
上の例は、バッチの完了件数、バッチ毎のElpased Timeの状況、単位時間当たりのレコード書き出し件数等をダッシュボードで表したものです。
バッチを5多重で実行していますが、BATCH001=>005まで起動するのにタイムラグがあるので、最初の立ち上がり時にはBATCH001の単位時間当たりの処理が多いことが見て取れます。並行度が上がるにつれて、各バッチ毎の単位時間当たりの処理件数は減りますが、全体としては平準化されている様子が分かります。
このようにグラフで可視化すると状況が直感的に判断できます。このような可視化をしておくことで、例えば、ある時点を境に全体的に処理量が落ちたり、特定のバッチだけ遅延する、という事象が発生した時にその特異な状況が把握しやすくなります。定常的に情報を蓄積していくことで傾向を把握したり、機械学習などと連携して早期の異常検知につなげるといったことにも応用できると思います。

# おわりに
End to Endで、COBOLから出力されたレコードベースのデータをElasticsearchへ取り込みKibanaで確認、というところまでを一通り実施してみました。
一度Elasticsearchにデータが取り込めれば、それを可視化/分析を行うのは一般的にも広く知られているElasticsearch/Kibanaの作法で実施することができます。ここでは、地図情報と合わせた可視化などを試してみましたが、実際の業務やデータ構造に合わせて他にも様々な情報や技術と組み合わせることでデータ活用の幅が広がると思います。
また、今回は実証実験的にELKスタックをメインで利用してみましたが、他のOSSベースのデータ分析基盤を利用するにしても、考え方は応用できると思います。
既存COBOLを活用しながらDigital Transformationをすすめる上での何らかのヒントになれば幸いです。

# 参考
今回の検証で利用したソース類は以下のGitHubに公開しています。
GitHub: [COBOLRecord2ELK](https://github.com/teamcimkcs2020/COBOLRecord2ELK)

------
[前の記事へ<=](https://qiita.com/teamcimkcs2020/private/5689e95b83052500a5fa)