Re:Qの技術ブログにアクセスいただきありがとうございます!クラウド技術部のT.Aです。
AWS環境でEC2やRDSの運用を行っていると、インスタンス内部で発生したアラートをSNSで通知する構成をとることが多いですよね。
ただ、CloudWatch Alarmは「エラーが起きた」ことは通知してくれますが、「どんなエラーが起きたのか」は説明(description)に記載しておく必要があり、少しだけ利便性に欠けます。
そこで当社では以下のようなサービス構成をとって解決しています。
【結論】CloudWatch AlarmからLambdaを呼び出してエラー本文を検出し、SNSで通知を行う。

CloudWatch Alarmの課題
CloudWatch Logsを使ってログ監視を行う際、まず検討するのが「メトリクスフィルター + CloudWatch Alarm + SNS」の構成です。 しかし、この構成のままアラートメールを発報すると、「閾値を超えた」という通知は届くものの、ログ本文(エラーの詳細情報)が記載されないという問題が発生します。 特に夜間対応時にログの詳細がわからないと、一次切り分けに時間がかかってしまいますし、障害の規模も即座に把握できません。
サブスクリプションフィルターの限界
ログ本文を直接SNSに連携する手段として「サブスクリプションフィルター」の利用も検討しました。 ただ、サブスクリプションフィルターには1つのロググループに対して設定できるフィルター数に上限(デフォルトで最大2個まで)があるという制限があります。 すでに他のログ転送等でフィルターを使っている場合、拡張性に欠けるという懸念がありました。
また、この構成ですとメールの件名(アラーム名)をわかりやすいものにするには、StepFunctionなど別のサービスを組み合わせる必要があり、障害ポイントの増加にもつながります。

全体的な流れ
そこで採用したのが、先ほど記載した構成です。

- EC2にはCloudWatch Agentを導入し、EC2とRDSからCloudWatch Logsへログの転送を行う
- ロググループのメトリクスフィルターには、通知する/しない内容のフィルターを設定
- Alarmの呼び出し先は、ログの検索と抽出を行うLambdaを指定
- Lambdaの中からメール本文の整形・ログ追記、SNSの呼び出しを行う
この構成により、ログ本文が含まれた詳細なアラートメールを受け取ることができるようになりました。
ログ抽出の課題
上記の構成で解決したかと思っていたのですが、実際に運用を開始したところ「ログ検出のラグ」によるインシデントが発生しました。
きっかけは「OS内部でエラーログが発生しているのに、意図したメールが送付されてこない」という場面で発見しました。 原因を探ったところ、「メトリクスフィルターがログを検知してLambdaが起動した瞬間には、まだCloudWatch Logsの検索・取得を参照可能な状態になっていない」という微小なラグが存在することが判明しました。 そのため、Lambdaによる検出が早すぎても「該当ログなし」となってしまっていました。
最終的な解決策:Lambda側でのリトライ処理
この問題に対しては、Lambdaのプログラム内でSleep処理&Lambda自体のリトライ処理を組み込むことで対応しました。 1回目でログが取得できなくても、数秒待ってから再度取得を試みることで、ログが参照可能になるのを待つという方針です。 組み込んだ処理として少し工夫が必要なのは、
- UTC時間が文字列で記載されているため、エポックタイムに変更する必要があること
- ログの検出時間を、Alarmが検知した前後3分に拡張すること
です。
CloudWatch Logsからメトリクスフィルターを利用して検知しているのだから、対象のログはLogsで参照できるだろう、という思い込みにより発生した内容となります。
このリトライ処理を実装して以降、ログの取得漏れやエラーは一切発生しなくなり、現在は非常に安定して運用できています。
まとめ
CloudWatch Logsを活用したログ監視において、「詳細なログ本文の通知」と「検知直後のデータ取得ラグ」は見落としがちなポイントです。
同じように「アラートメールにログ本文を載せたい」「Lambdaでログを取得しようとしたらエラーになる」とお悩みの方は、ぜひLambda側でのリトライ処理を含めた実装を試してみてください!




