Azure Log Analytics で KQL を用いて、サクッと時系列グラフを作り、アノマリー分析を行ってみる  (make-series & series_decompose_anomalies 演算子)

# 1.はじめに
Log Analytics ワークスペースや Microsoft Sentinel を用いて、時系列グラフを作成しようとしたところ、意外にこれといったドキュメントが無かったので、備忘録で残しておきます。
[前回](https://qiita.com/hisnakad/items/b4ece7b75121817a2efc)では、``summarize``演算子を用いた際に列分割を利用して時系列グラフを作成しましたが、今回は``make-series``演算子を用いて作成します。
``make-series`` を用いることで、アノマリー演算子である **``series_decompse_anomaies``** に入れて異常値予測分析を行うことが出来るようになります。

# 2.やりたいこと
Log Analytics ワークスペースから Kusto Query (KQL) を用いてサクッと時系列グラフを作りたい。
時系列に任意のキーを集計表示して比較したい。
**アノマリー（異常）分析などで異常値を判定したい [New]**
- X軸に時間
- Y軸に集計値（特定のキー毎に集計する）

こんなイメージ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653790/88fe106d-ed51-601d-2321-a4b24ca77333.png)

# 3.やってみる
``make-series``演算子を用いて、時系列軸の配列を作成します。
Log Analytics ワークスペースは Microsoft のデモ環境が提供されていますので、以下リンクから試してみてください。
[Azure Log Analytics デモ環境](https://ms.portal.azure.com/#view/Microsoft_Azure_Monitoring_Logs/DemoLogsBlade)

# 4.ポイント
## 4.1 make-series を用いて、時間軸(TimeGenerated)と対象グループ毎の集計配列を作成する
KQL だと以下の部分になります。option の step 宣言にて1時間毎の集計を行う際に HTTP ステータスコードのキー ``scStatus`` でグループ化しています。
```
| make-series Total=count() on TimeGenerated from datetime(2022-09-01) to datetime(2022-09-14) step 1h by scStatus
```
``make-series`` の使い方は公式 Docs も併せて参照下さい。

``make-series`` による集計を行うと、グループ化されたデータに基づいて配列型で結果を得ることが出来ます。

https://learn.microsoft.com/ja-JP/azure/data-explorer/kusto/query/make-seriesoperator

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653790/76da814c-8f18-4453-daeb-189e081d7a73.png)

## 4.2 series_decompose_anomalies を用いて、集計配列から異常値を検出する
KQL だと以下の部分になります。こちらは 6節で詳しく解説します。

```
| extend (anomalies, score, baseline) = series_decompose_anomalies(Total , 1.5, -1, 'linefit')
```
# 5.サンプル
# 5.サンプル例
## 5.1 W3C IIS ログで、ステータスコード毎に時系列チャートを作成する

```sql
W3CIISLog
| project TimeGenerated, sIP, csMethod, csUriStem, scStatus
| make-series Total=count() on TimeGenerated from datetime(2022-09-01) to datetime(2022-09-14) step 1h by scStatus
| render timechart with(title="W3C IIS Log group by HTTP Status", ytitle="Counts")
```
例
- ``make-series``宣言の中で、``scStatus``フィールドでグループ化したデータから``datetime``で期間を設定し、``step``で 1 時間単位で配列を作成します。
- 時間や配列間隔の値は ``let`` 宣言で変更・チューニングし易くするのがベストプラクティスです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653790/cffef72f-6995-2920-f93d-0bf1b784c03d.png)

# 6.応用例 - アノマリー分析をやってみる
``make-series``は動的な配列として作成されるため、分析手法では統計・異常分析として追加の分析演算子を用いて解析を行うことが出来るようになっています。
有名な演算子として``series-decompose-anomaliesfunction`` による異常検出が出来ます。

https://learn.microsoft.com/ja-jp/azure/data-explorer/kusto/query/series-decompose-anomaliesfunction

``series-decompose-anomaliesfunction``では、オプションを用いることで様々な分析をかけることが出来ます。
詳しくは上記の公式 Docs を参照して下さい。

|オプション|概要|
|:--|:--|
|しきい値|異常しきい値。既定値 1.5 (k 値) で、軽度またはそれ以上の異常を検出します。|
|季節性| 季節分析を制御する整数。次のいずれかが含まれます<BR>-1: 季節性の自動検出 (series_periods_detect を使用) [既定値]<BR>0: 季節性なし (つまり、このコンポーネントの抽出をスキップする)。<BR>period: 予想される期間をビン数単位で指定する正の整数。|
|傾向|傾向分析を制御する文字列。<BR>"avg": 傾向コンポーネントを系列の平均として定義する [既定値]<BR>"none": 傾向はありません。このコンポーネントの抽出をスキップします。<BR>"linefit": 線形回帰を使用して傾向コンポーネントを抽出します|

``make-series``による配列であれば、そのまま異常分析演算子である``series-decompose-anomaliesfunction``にかけることが出来ます。以下線形回帰``linefit``を用いて、季節性の自動検出を有効にして分析した例です。

```sql
W3CIISLog
| project TimeGenerated, sIP, csMethod, csUriStem, scStatus
| make-series Total=count() on TimeGenerated from datetime(2022-09-01) to datetime(2022-09-14) step 1h by scStatus
| extend (anomalies, score, baseline) = series_decompose_anomalies(Total , 1.5, -1, 'linefit')
```

このクエリー結果を見ると、グループ化で時系列に配列したデータに対して "anomalies", "score", "baseline" といった結果を得られます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653790/a8c953fe-7279-8bdc-47a0-8f248078f97c.png)

戻り値は、異常分析の結果（アップ/ダウン）、スコア値、予測値を返します。
この値を見ながら、実際の異常検知などを活用することができるようになります。

|オプション|概要|
|:--|:--|
|ad_flag:|それぞれアップ/ダウン/異常なしをマークする (+1、-1、0) を含む三項系列|
|ad_score:|異常スコア|
|baseline:|分解に従った、系列の予測値|

https://learn.microsoft.com/ja-jp/azure/data-explorer/kusto/query/series-decompose-anomaliesfunction#returns

このままだと、``ad_flag``や``ad_score``などの数値に対してフィルター条件がかけられないので、``mv-expand``演算子を用いて配列をレコードに変換します。

```sql
W3CIISLog
| project TimeGenerated, sIP, csMethod, csUriStem, scStatus
| make-series Total=count() on TimeGenerated from datetime(2022-09-01) to datetime(2022-09-14) step 1h by scStatus
| extend (anomalies, score, baseline) = series_decompose_anomalies(Total , 1.5, -1, 'linefit')
| mv-expand Total to typeof(double), TimeGenerated to typeof(datetime), anomalies to typeof(double), score to typeof(double), baseline to typeof(long)
```

すると、配列だったデータをレコード形式で表示することが出来ます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653790/cbbc45a1-4fc0-a04d-b781-5b61cd4f333d.png)

``ad_flag``の値(+1)でフィルタすると、異常値としてカウントアップしたタイミングにフィルタすることが出来ます。

```sql
W3CIISLog
| project TimeGenerated, sIP, csMethod, csUriStem, scStatus
| make-series Total=count() on TimeGenerated from datetime(2022-09-01) to datetime(2022-09-14) step 1h by scStatus
| extend (anomalies, score, baseline) = series_decompose_anomalies(Total , 1.5, -1, 'linefit')
| mv-expand Total to typeof(double), TimeGenerated to typeof(datetime), anomalies to typeof(double), score to typeof(double), baseline to typeof(long)
| where anomalies > 0
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653790/c850be91-9f2c-83b2-7f2a-20b9eea1057e.png)

Microsoft Sentinel でアラート通知を行う場合は、属性 (Entity) 情報を出した方が良いので、このタイミングでの送信元IPやUriなどを抽出すると良いのではと思います。

今回の IIS ログの例だと、以下のようなユースケースが考えられるのではないでしょうか。

- HTTP 200 のトレンドを越えた過剰な通信を検出する（リクエスト攻撃など）
- HTTP 4XX クライアントエラー数の異常性を検出する (404 などの特定コードに言及した異常値もあり)
- HTTP 5XX サーバー側エラー数の異常性を検出する


# 7. まとめ
Azure Monitor を用いてログを貯めている方は多いと思いますが、時系列チャートを作るテクニックがあまり紹介されていないので、簡単な手順としてまとめてみました。どなたかの参考になれば幸いです。

＊本稿は、個人の見解に基づいた内容であり、所属する会社の公式見解ではありません。また、いかなる保証を与えるものでもありません。正式な情報は、各製品の販売元にご確認ください。