CloudWatch Logsに収集したアクセスログから応答時間をメトリクス化する

CloudWatch Logsの [メトリクスフィルタ](https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/logs/MonitoringPolicyExamples.html) 機能を使用すると、CloudWatch Logsに収集したアクセスログから応答時間などをメトリクス化できます。
CloudWatch エージェントで収集したログをそのまま活用できるため、 [カスタムメトリクス](https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/monitoring/publishingMetrics.html) を発行する方法と比べて簡単です。
<img width="720" style="border: 1px solid black;" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1045/11cb2b92-8d63-d5b4-956d-df02a3f5c6c0.png">

## 制約

- ディメンションや単位は設定できない
- アクセスログの出力フォーマットに手を入れる必要がある

## 手順

1. メトリクス化したい数値項目はスペース区切りで出力するよう変更する
2. メトリクスフィルタを作成する
 1. (例) 特定エンドポイントの応答時間をメトリクス化する
 2. (例) ステータスコード200以外の件数をメトリクス化する

※CloudWatch Logsへのアクセスログの収集は既に設定されている前提です。

### 1. メトリクス化したい数値項目はスペース区切りで出力するよう変更する

応答時間やバイト数などの数値を抽出したい場合は、抽出したい部分がスペース区切りで出力されている必要があります。

> メトリクスフィルターには、スペースで区切られたログイベントから数値を抽出する機能もあります。たとえば、ウェブリクエストのレイテンシーです。
>
> [フィルターとパターンの構文 - Amazon CloudWatch Logs](https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/logs/FilterAndPatternSyntax.html)

LTSV形式でログを出力している場合は、スペース区切りで出力する部分を無理矢理足すか、LTSV形式をやめるかしなければなりません。

```nginx:nginxでの設定例
log_format almostltsv "time:$time_local"
                      "\thost:$remote_addr"
                      "\tforwardedfor:$http_x_forwarded_for"
                      "\treq:$request"
                      "\tstatus:$status"
                      "\tsize:$body_bytes_sent"
                      "\treferer:$http_referer"
                      "\tua:$http_user_agent"
                      "\treqtime:$request_time"
                      "\tapptime:$upstream_response_time"
                      "\tvhost:$host"
                      " $request_time"; # ここ
```

### 2. メトリクスフィルタを作成する

公式のドキュメント [フィルターとパターンの構文 - Amazon CloudWatch Logs](https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/logs/FilterAndPatternSyntax.html) に目を通しておきます。以下がポイントです（強調は引用者による）。

> スペース区切りイベントを解析するメトリクスフィルターパターンを指定するには、メトリクスフィルターパターンが、**フィールドの名前をカンマで区切り、すべてのパターンを角括弧で囲んで指定する**必要があります。たとえば、[ip, user, username, timestamp, request, status_code, bytes] のようになります。
>
> **フィールド数が不明な場合は、省略符号 (…) を使用した省略通知**を使用できます。
> (略)
> また、フィールドに**条件を追加して、すべての条件に一致するログイベントのみがフィルターに一致する**ようにできます。
> 
> [フィルターとパターンの構文 - Amazon CloudWatch Logs](https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/logs/FilterAndPatternSyntax.html)

例えば、先述のnginxの設定例では、アクセスログが次のように出力されます。

`time:23/Sep/2019:11:37:10 +0900 host:127.0.0.1  forwardedfor:-  req:GET /app/web/ HTTP/1.1      status:200      size:18650      referer:-  ua:Xymon xymonnet/4.3.28 reqtime:0.409   apptime:0.409   vhost:www.p2pquake.net 0.409`

よって、フィルターパターンは次のように記述できます（ua部分はスペースの数が不明なため省略符号を用いています）。

`[time, timezone, host, forwardedfor, method, url, protocol, status, size, referer, ..., ltsvreqtime, apptime, vhost, reqtime]`

フィールド名|値
:--|:--
time|time:23/Sep/2019:11:37:10
timezone|+0900
host|host:127.0.0.1
forwardedfor|forwardedfor:-
method|req:GET
url|/app/web/
protocol|HTTP/1.1
status|status:200
size|size:18650
referer|referer:-
...|ua:Xymon xymonnet/4.3.28
ltsvreqtime|reqtime:0.409
apptime|apptime:0.409
vhost|vhost:www.p2pquake.net
reqtime|0.409

#### 2-1. (例) 特定エンドポイントの応答時間をメトリクス化する

`/app/web/` への `GET` リクエストの応答時間を抽出します。

1. CloudWatch Logsのロググループを選択し、「メトリクスフィルターの作成」を選択します。
2. フィルタパターンを次のようにします:<br>
```
[time, timezone, host, forwardedfor, method="*GET", url="/app/web/", protocol, status, size, ..., reqtime]
```
3. メトリクス値に、応答時間のフィールドである `$reqtime` を指定して設定完了です。

<img width="720" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1045/f67512b4-499c-ceca-45e7-fcef32eb9a2f.png">
<img width="720" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1045/ea0f2f7a-2847-0124-6515-61e3a5dfe083.png">


#### 2-2. (例) ステータスコード200以外の件数をメトリクス化する

`/v1/*` への `GET` リクエストのうち、ステータスコード200以外の件数をカウントします。

1. CloudWatch Logsのロググループを選択し、「メトリクスフィルターの作成」を選択します。
2. フィルタパターンを次のようにします:<br>
```
[time, timezone, host, forwardedfor, method="*GET", url="/v1/*", protocol, status!="status:200", ...]
```
3. 件数を数えるため、メトリクス値は `1` のままで設定完了です。

## 設定結果

<img width="720" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1045/330a2d65-d63d-87d6-8719-32d9efb85d33.png">
上記のようにメトリクス化できました。通常のメトリクスと同様に、CloudWatch Alarmも設定可能です。
「既にCloudWatch Logsにアクセスログを収集してある」「外部の監視サービスを追加で導入するほどでもない」という場面ではお手軽で良いです。

### 参考

- [ログデータの検索およびフィルタリング - Amazon CloudWatch Logs](https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/logs/MonitoringLogData.html)
- [Module ngx_http_core_module#var_request_time](https://nginx.org/en/docs/http/ngx_http_core_module.html#var_request_time)