LaravelのログをCloudWatchに流して、エラーの発生をトリガーとするアラームの作成とTips

# 最初にまとめ

CloudWatchにおいて、ログがない時もあるメトリクスのアラームを作成する場合、「欠落データの処理」を「欠落データを適正(しきい値を超えていない)として処理」とすると、状態を正常と異常の2択にできます :thumbsup: 

# 前提

EC2インスタンスでLaravelを実行

### 監視の流れ
Laravel(EC2) -> CloudWatchロググループ -> CloudWatchメトリクスフィルター -> CloudWatchアラーム -> SNS -> Chatbot -> Slack

この記事では、CloudWatchからSNS、Chatbot、Slackの流れは本題ではないので、そこを知りたい人は他を調べてください :pray:
あと、EC2インスタンスにはCloudWatchエージェントをインストール済みとします。

# CloudWatchでのLaravelログの取得

CloudWatchエージェントの設定ファイルにLaravelのログ設定を追加します。
JSON設定ファイルを見つけて、以下のような設定を追加します(もしくは `amazon-cloudwatch-agent-config-wizard` を実行)

```json
"logs": {
    "logs_collected": {
        "files": {
            "collect_list": [
                {
                    "file_path": "laravel_directory/storage/logs/laravel*log",
                    "log_group_class": "STANDARD",
                    "log_group_name": "laravel_log",
                    "log_stream_name": "{instance_id}_laravel",
                    "retention_in_days": -1
                }
            ]
        }
    }
},
```

設定ファイルを読み込んでCloudWatchエージェントを起動します。

これでCloudWatchの管理画面のロググループで、「laravel_log」というロググループが表示されると思います。
もし表示されないなら、CloudWatchエージェントを起動してからログが作られてないからです。
適当に `Log::info('hello');` などを実行してログを出力しましょう。
ロググループ「laravel_log」を選択して、ログストリームを選択すると、Laravelから送られてきたログが表示されるはずです。

# メトリクスフィルターの作成

ロググループは生のログみたいなもので、このままではメトリクスとして使えないので、メトリクスフィルターを使って加工します。

今回は、Larevelのログからエラーの数を数える `LaravelErrorCount` というメトリクスを作成します。

ロググループで該当のロググループを選択(ここではlaravel_log)、「メトリクスフィルターを作成」を実行します。
フィルターパターンでエラーを判別するための文字列を定義します。
Laravelはエラーが発生すると、「production.ERROR」だったり「local.ERROR」といった文字列がエラーの詳細と共にログに出力されます。
ログには他にも `Log::info()` だったり `Log::debug()` といったコマンドで様々なログレベルのログが出力される可能性があります。

今回はエラーをカウントしたいので、フィルターパターンは「ERROR」とします(入力しなくてもサジェストされますね)。
「メトリクスの割り当て」画面では、
「フィルター名」は「laravel_error_count」
「メトリクス名前空間」は「LaravelLog」
「メトリクス名」は「LaravelErrorCount」
「メトリクス値」は1
「デフォルト値」は0
「Unit」は「カウント」
とします。メトリクス値とデフォルト値は1と0で、それ以外は自由です。
送られてきたログ1行に対して、フィルターパターンと一致するとメトリクスに1が追加され、一致しないと0が追加されるという仕組みです。

![スクリーンショット 2024-01-26 18.10.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/123511/9625e3cb-24c2-7f1f-f312-4884c5a7ae74.png)


メトリクスフィルターを作成すると、メトリクスのカスタム名前空間に「LaravelLog」という項目が表示されるはずです。

![スクリーンショット 2024-01-28 10.35.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/123511/a4d5d1d3-1be1-66ac-127d-7cc2b419f266.png)


# アラームの作成

ここからアラームを作成します。
「メトリクスの選択」で「LaravelErrorCount」を選択します。
「統計」をデフォルトの「平均」から「合計」に変更します。
条件は「静的」「以上」「1」として、「その他の設定」の「欠落データの処理」は「欠落データを適正(しきい値を超えていない)として処理」として作成します。

![スクリーンショット 2024-01-26 18.16.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/123511/b909ab74-9ea2-7a4b-0913-4e0f0ac9b6f0.png)


これでLaravelでエラーが発生したら、CloudWatchでアラーム状態になるアラームができました。

# 本題であるアラーム作成の試行錯誤

ここまでが準備体操、ここからが本題!!
アラームを作る上で、試行錯誤した点が今回の記事の本題です。

### 欠落データの処理
自分が最初にアラームを作った時、アラームの「その他の設定」の「欠落データの処理」をデフォルトの「欠落データを見つかりませんとして処理」のままにしていました。

![スクリーンショット 2024-01-26 18.16.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/123511/f805efce-2a93-8c9c-48c5-efbb89ad5daf.png)

このアラームで実際にLaravelでエラーを発生させると、まずアラームが「アラーム状態」となります。
これは想定通り :thumbsup: 
そのまま放っておくと、「アラーム状態」から「データ不足」に変わります。
そこからさらに、例えば `Log::info()` などでログ出力すると、状態は「OK」に変わります。
よくよく調べたら、

* Laravelのログ出力がない -> データ不足
* Laravelのログ出力がある -> 
  * フィルターパターンに一致する -> アラーム状態
  * フィルターパターンに一致しない -> OK


自分が欲しいのは、何もログ出力がなかったら「データ不足」ではなく「OK」の状態になるアラームです。

### 解決方法

そこで変更するのは「欠落データの処理」の設定です。
「欠落データを見つかりませんとして処理」から「欠落データを適正(しきい値を超えていない)として処理」に変更します。

![スクリーンショット 2024-01-28 10.26.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/123511/af06024e-b2fc-2eaa-f4f1-e2c0a8cfe1ed.png)

これで、

* Laravelのログ出力がない -> OK
* Laravelのログ出力がある -> 
  * フィルターパターンに一致する -> アラーム状態
  * フィルターパターンに一致しない -> OK

OKです!!

# まとめ

CloudWatchにおいて、ログがない時もあるメトリクスのアラームを作成する場合、「欠落データの処理」を「欠落データを適正(しきい値を超えていない)として処理」とすると、状態を正常と異常の2択にできます :thumbsup: