システムログから重要キーワードの抽出を試してみる(Azure Text Analytics API & 「言選Web」termextractで実践)

システムのログデータの中で、そのログデータの特徴的な部分の情報をピックアップして、「こういう特徴のログが出た時」には「こういう対処を行う」といったヒモ付ができると調査作業時間の短縮に繋がるのではないかといろいろと試行錯誤しています。
まずはログデータの特徴を重要キーワードとしてピックアップする方式をいくつか試してみたので記録しておきます。
もっと良い方法があるよとかコメントあれば歓迎です。

## 試したこと

今回試してみたのは2つ。  
1つ目は、Azureのサービスの一つで、Cognitiveサービスの中のText Analytics APIです。  
もう1つは、termextractのPythonモジュールです。

Azureの[Text Analytics API](https://azure.microsoft.com/ja-jp/services/cognitive-services/text-analytics/)は、自然言語テキスト情報に対して、言語がなんかを特定したり、センチメント分析(肯定的か否定的かをスコア化)したり、キーフレーズを抽出したりできます。  
[termextract](http://gensen.dl.itc.u-tokyo.ac.jp/)は、東京大学や横浜国立大学の研究室で開発されているもののようで、文章中から専門用語を自動抽出することができます。

以下、試した記録です。

## Azure Text Analytics API
### Azureのサービス登録

- Azureにサインイン
- [すべてのサービス]→[AI+MachineLearning]カテゴリの[Cognitive Services]にテキスト分析のサービスを追加
    - お試しなので、F0(月5000リクエストまで無料のプラン)を選択して作成。場所は東日本で実施。
- 作成されたサービスのページでAPIキーを確認して記録します(後ほどのプログラム実行の部分で利用のため)

### 分析にかけるログデータを準備

今回は、試しにLinuxサーバのsyslogデータをいくつか流し込んでみます。
引き渡すログのテキストデータは、APIの仕様上、以下のように分析にかけたい単位でナンバリングした上で、JSON型で準備します。今回のログは英文なのでlanguage:enを指定です。日本語もいけるようです。

```json
{'documents': [
    {'id': '1', 'language': 'en', 'text': 'Mar 25 21:35:10 docker-host-alpine kern.alert kernel: [8140400.248002] grsec: From xx.xx.xx.xxx: Segmentation fault occurred at 0000000000000008 in /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java[java:23636] uid/euid:999/999 gid/egid:999/999, parent /usr/bin/docker-containerd-shim['},
    {'id': '2', 'language': 'en', 'text': 'Mar 25 21:35:10 docker-host-alpine kern.alert kernel: [8140400.284656] grsec: From xx.xx.xx.xxx: denied resource overstep by requesting 4096 for RLIMIT_CORE against limit 0 for /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java[java:23636] uid/euid:999/999 gid/egid:999/999, parent /us'}
]}
```

### PythonのプログラムからAPIを呼び出してキーワード抽出を試す

上記で準備したログデータに対して、API実行します。
実行するPythonのプログラムは以下。

```python
#!/bin/env python
import requests
from pprint import pprint

class LogAnalyze:
    def __init__(self):
        subscription_key = '確認したAPIキーを記述'
        self.text_analytics_base_url = 'https://japaneast.api.cognitive.microsoft.com/text/analytics/v2.0/' #今回は東日本リージョンでの利用のためURLはjapaneastですが、違うリージョンの場合はエンドポイントURLが異なるのでそれに合わせて変更
        self.headers   = {"Ocp-Apim-Subscription-Key": subscription_key}
    def check_key_phrases(self, documents):
        key_phrase_api_url = self.text_analytics_base_url + "keyPhrases"
        response  = requests.post(key_phrase_api_url, headers=self.headers, json=documents)
        key_phrases = response.json()
        pprint(key_phrases)

if __name__ == "__main__":
    documents = {'documents': [
        {'id': '1', 'language': 'en', 'text': 'Mar 25 21:35:10 docker-host-alpine kern.alert kernel: [8140400.248002] grsec: From xx.xx.xx.xxx: Segmentation fault occurred at 0000000000000008 in /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java[java:23636] uid/euid:999/999 gid/egid:999/999, parent /usr/bin/docker-containerd-shim['},
        {'id': '2', 'language': 'en', 'text': 'Mar 25 21:35:10 docker-host-alpine kern.alert kernel: [8140400.284656] grsec: From xx.xx.xx.xxx: denied resource overstep by requesting 4096 for RLIMIT_CORE against limit 0 for /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java[java:23636] uid/euid:999/999 gid/egid:999/999, parent /us'}
    ]}
    LogAnalyze().check_key_phrases()
```

実行すると以下のようになります。

```json
{'documents': [{'id': '1',
                'keyPhrases': ['java-',
                               'bin',
                               'usr',
                               'docker-host',
                               'docker-containerd',
                               'alert kernel',
                               'alpine kern',
                               'parent',
                               'penjdk-amd64',
                               'jre']},
               {'id': '2',
                'keyPhrases': ['java-',
                               'alert kernel',
                               'alpine kern',
                               'usr',
                               'penjdk-amd64',
                               'jre',
                               'bin',
                               'docker-host',
                               'euid',
                               'lib']}],
 'errors': []}
```

人間が見て重要そうなキーワードとしては、1つ目のログの場合は、Segmentation faultだったり、2つ目のログの場合は、denied resource overstepとかRLIMIT_COREだったりが期待されるところかなと思うのでこのままだと少しいまいちな感じ。

ログデータは前処理した上で登録させないと厳しそうです。

例えば、時刻情報や出力プログラム情報、facility情報、severity情報等を除去した本文だけでやってみるとどうでしょう。
登録するログを以下に書き換えます。

```json
{'documents': [
    {'id': '1', 'language': 'en', 'text': 'From xx.xx.xx.xxx: Segmentation fault occurred at 0000000000000008 in /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java[java:23636] uid/euid:999/999 gid/egid:999/999, parent /usr/bin/docker-containerd-shim['},
    {'id': '2', 'language': 'en', 'text': 'From xx.xx.xx.xxx: denied resource overstep by requesting 4096 for RLIMIT_CORE against limit 0 for /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java[java:23636] uid/euid:999/999 gid/egid:999/999, parent /us'}
]}
```
結果は以下。

```json
{'documents': [{'id': '1',
                'keyPhrases': ['java-',
                               'bin',
                               'usr',
                               'parent',
                               'penjdk-amd64',
                               'jre',
                               'docker-containerd',
                               'egid',
                               'euid',
                               'jvm']},
               {'id': '2',
                'keyPhrases': ['java-',
                               'usr',
                               'penjdk-amd64',
                               'jre',
                               'bin',
                               'euid',
                               'lib',
                               'jvm',
                               'RLIMIT',
                               'egid']}],
 'errors': []}
```

多少変わりましたがまだ微妙です。パス情報の部分とかが過敏に検出されてしまっている模様。

Text Analytics APIは、ある程度長い文面が期待されているのと、ログデータのような見方が特殊なものよりは会話やWeb記事とかの情報に使うイメージな気もします。

## 「言選Web」&termextractで試してみる

次に、termextractを試してみました。termextractをWeb上で実行できる「言選Web」というサービスがあるので、まずはここでさくっと試します。

[言選Web](http://gensen.dl.itc.u-tokyo.ac.jp/gensenweb.html)


1つ目のログ

```
21:35:10 docker-host-alpine kern.alert kernel                            1.68
segmentation fault                                                       1.41
parent                                                                   1.00
xx.xx.xx.xxx                                                             1.00
grsec                                                                    1.00
8140400.248002                                                           1.00
java:23636                                                               1.00
uid/euid:999/999                                                         1.00
gid/egid:999/999                                                         1.00
Mar                                                                      1.00
```

2つ目のログ

```
21:35:10 docker-host-alpine kern.alert kernel                            1.68
resource overstep                                                        1.41
parent                                                                   1.00
xx.xx.xx.xxx                                                             1.00
8140400.284656                                                           1.00
grsec                                                                    1.00
java:23636                                                               1.00
uid/euid:999/999                                                         1.00
gid/egid:999/999                                                         1.00
Mar                                                                      1.00
limit                                                                    1.00
RLIMIT_CORE                                                              1.00
```

こちらの結果の方が人がチェックしているキーワードに近い情報が取れてきているように見えます。右端の数値は重要度スコアのようです。

python版のライブラリがあるので、プログラムから処理させてみます。

### termextractインストール

```
$ wget http://gensen.dl.itc.u-tokyo.ac.jp/soft/pytermextract-0_01.zip 
$ unzip pytermextract-0_01.zip
$ cd pytermextract-0_01
$ python setup.py install
高精度の分析(英文)をさせたいので以下をインストール
$ pip install nltk
```

### ログデータのテキストファイルを用意

先程のログデータをテキストファイルとして置いておきます。

```log_sample.txt
Mar 25 21:35:10 docker-host-alpine kern.alert kernel: [8140400.248002] grsec: From xx.xx.xx.xxx: Segmentation fault occurred at 0000000000000008 in /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java[java:23636] uid/euid:999/999 gid/egid:999/999, parent /usr/bin/docker-containerd-shim[
```

### サンプル提供されているtermex_eng.pyスクリプトを使って分析

取得して展開したフォルダ内のpytermex配下にtermextractを使って分析処理して結果を吐き出すサンプルプログラムが用意されています。
今回は、英文のログデータに対して処理かけたいので、termex_eng.pyというのを利用します。

```
$ python pytermex/termex_eng.py log_sample.txt
```

実行すると、実行したカレントディレクトリ直下に結果が出力されます。

```eng_extracted.txt
21:35:10 docker-host-alpine kern.alert kernel   1.6817928305074292
Segmentation fault      1.414213562373095
uid/euid:999/999 gid/egid:999/999       1.414213562373095
Mar     1.0
8140400.248002  1.0
grsec   1.0
xx.xx.xx.xxx    1.0
/usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java  1.0
java:23636      1.0
parent  1.0
/usr/bin/docker-containerd-shim 1.0
```

Web上で確認した結果と似たような結果が返されているのがわかります。

# まとめ

特定のログのみでの試行なのでほんとにこのやり方で使えるかはまだまだわからないですが、多少人がログを見て調査する作業に役立つきっかけにはなるような気がするのでもう少し試してみたいと思います。