【AWS】CloudWatch LogsからSlackへのエラー詳細通知。実装と運用上の注意点まとめ
はじめに
AWSの運用において、エラーの可視化は避けては通れないテーマです。
本記事で紹介するエラー通知の構成では、CloudWatchのサブスクリプションフィルターを用いて、ログの中身を抽出して通知へと繋げます。これにより、エンジニアがわざわざコンソールを開いてログを探しに行く手間を省くことが可能になります。
今回の構成
今回使用するサービスは以下の内容です。
Lambda → CloudWatch → SNS → Chatbot → Slack
本記事では、CloudWatchのサブスクリプションフィルターを用いた構成を採用します。
メトリクスフィルターとサブスクリプションフィルターの違いについて
具体的な実装に入る前に、CloudWatchで利用できる2つのフィルター機能の役割を説明します。
ログのグループには「メトリクスフィルター」と「サブスクリプションフィルター」が存在し、それぞれ以下のような役割を持っています。
メトリクスフィルター
ログを数値に変換して、グラフやアラームで監視する仕組み。
特定のキーワード(例:ERROR)が含まれるログが届いた際、そのログを「1件」としてカウントし、数値データ(メトリクス)として蓄積します。その数値が、あらかじめ設定したしきい値(例:5分間に3回以上)を超えると、CloudWatchアラームが作動して通知を送ります。
1つのロググループあたり100個作成できます。
メトリクスフィルターを採用するケース
「▽分間に〇回以上のエラーが出たら通知」といった、回数やしきい値に基づいたアラート運用をしたいなどエラー率や発生頻度を監視したい場合
「特定のログがどれくらい出力されているか」をダッシュボードで可視化し、長期的な推移を追いたい場合
メトリクスフィルターはLambda無しでアラート運用が可能であるため、運用コストを抑えたい場合
サブスクリプションフィルター
特定の条件に合うログを、そのまま別の場所へ転送する仕組み。
ログの中に特定のキーワードを見つけると、そのログの内容(メッセージ、タイムスタンプ、ログストリーム名など)を丸ごとパッケージ化し、リアルタイムで配信先(LambdaやKinesisなど)へ受け渡します。
1つのロググループあたり2個作成できます。
サブスクリプションフィルターを採用するケース
エラーメッセージやスタックトレースを通知に直接含めることで、コンソールを開く前に原因を特定し、障害対応の初動を早めたい場合
メトリクスの集計(数分単位の間隔)を待たず、ログが出力された瞬間に転送・通知を行いたい場合
特定のアクションログをリアルタイムで抽出し、ログデータを長期保存やログ集約、分析に用いたい場合
どちらを採用すべきか
どちらの手法も一長一短があるため、目的によって使い分けるのが正解です。エラー通知の観点では以下の通りです。
メトリクスフィルター:エラーの「発生頻度」を重視し、手軽に監視を始めたい場合。
サブスクリプションフィルター:エラーの「中身」を重視し、デバッグや調査を効率化したい場合。
本記事では、「エラー内容が即座にわかる通知」を実現するため、後者のサブスクリプションフィルターを用いた構築手順を解説していきます。エラーが発生したこと自体を把握できれば良い場合は、メトリクスフィルタを使用することを推奨します。
通知の導線を作成する(SNSトピック作成)
CloudWatchのフィルターの概要を説明が完了したので、早速説明に移ります。
まずは、Amazon SNS(Simple Notification Service)からChatBotに通知するためのSNSトピックを作成しましょう。
Slackと連携する
Amazon Q Developer in chat applications (旧称: AWS Chatbot)を使用し、チャットクライアントで該当のSlackのワークスペースを選択してください。
[新しいチャネルを設定]から通知したいSlackチャンネルを選択し、適切なロールを設定してください。
※今回は解説のため、ロールはデフォルトのままで進めています。[通知 - オプション]では、先ほど作成したトピックのリージョンとトピックを選択してください。
Slackのほうに移り、通知するチャンネルでAmazon Q(AWSアプリ)をチャンネルに追加しましょう。
AWSで設定したAmazon Qのチャネルから「テストメッセージ」を設定し、連絡が来れば連携は完了です。
Slack通知用のLambdaを作成
CloudWatchから転送されるログデータをSlackメッセージ用に変換して、SNSトピック宛てに通知します。
今回は、エラー通知用として作成していますが、後ほど実施するサブスクリプションフィルターから受け取るものをそのまま転送するので、メッセージの内容は好みに合わせて設定してください。
import { SNSClient, PublishCommand } from '@aws-sdk/client-sns';
import { gunzipSync } from 'zlib';
const snsClient = new SNSClient({});
const SNS_TOPIC_ARN = process.env.SNS_TOPIC_ARN;
export const handler = async (event) => {
const MAX_MESSAGE_LENGTH = 2500;
const region = process.env.AWS_REGION;
try {
// CloudWatch Logsデータの解凍
const payload = Buffer.from(event.awslogs.data, 'base64');
const decompressed = gunzipSync(payload);
const logData = JSON.parse(decompressed.toString('utf-8'));
// ロググループ名とログストリーム名をURLエンコード
const encodedLogGroup = logData.logGroup.replace(/\//g, '$252F');
const encodedLogStream = logData.logStream.replace(/\//g, '$252F');
for (const logEvent of logData.logEvents) {
const rawMessage = logEvent.message;
const logMessage =
rawMessage.length > MAX_MESSAGE_LENGTH
? rawMessage.substring(0, MAX_MESSAGE_LENGTH) + '...'
: rawMessage;
// 発生時刻の1分前の時間範囲を設定
const eventTimestamp = logEvent.timestamp;
const startTime = eventTimestamp - 1 * 60 * 1000;
// CloudWatch LogsのログストリームURLを構成
const logStreamUrl = `https://${region}.console.aws.amazon.com/cloudwatch/home?region=${region}#logsV2:log-groups/log-group/${encodedLogGroup}/log-events/${encodedLogStream}$3Fstart$3D${startTime}$26end$3D${eventTimestamp}`;
// Chatbot カスタム通知スキーマの組み立て
const customNotification = {
version: '1.0',
source: 'custom',
content: {
title: `:warning:エラー検出通知:warning:`,
description: `*エラー発生箇所*\n${logData.logStream}\n*エラー内容*\n${logMessage}\n\n<${logStreamUrl}|エラー内容はこちらをクリック>`,
},
};
// Slackに通知するSNSへ送信
await snsClient.send(
new PublishCommand({
TopicArn: SNS_TOPIC_ARN,
Message: JSON.stringify(customNotification),
Subject: 'エラー検出通知',
}),
);
}
} catch (error) {
// 整形に失敗した場合のエラー通知(内部でエラーを再スローする)
await sendErrorNotification(error);
}
};
const sendErrorNotification = async (error) => {
const errorNotification = {
version: '1.0',
source: 'custom',
content: {
title: `:warning:エラーアラート処理失敗:warning:`,
description: `*エラーアラートの処理中にエラーが発生しました*\n\n*エラー内容*\n${error.message}\n\n*エラースタック*\n${error.stack || 'スタック情報なし'}`,
},
};
try {
await snsClient.send(
new PublishCommand({
TopicArn: SNS_TOPIC_ARN,
Message: JSON.stringify(errorNotification),
Subject: 'エラーアラート処理失敗',
}),
);
// エラー通知が成功した場合は、Lambdaを成功として扱う
} catch (snsError) {
// SNS送信自体が失敗した場合はコンソールに出力し、Lambdaを失敗として扱う
console.error('SNS送信に失敗しました:', snsError);
console.error('元のエラー:', error);
throw error;
}
};Lambdaのロールにsnsへのpublishポリシーを追加しましょう。
{
"Effect": "Allow",
"Action": "sns:Publish",
"Resource": "該当SNSトピックのARN"
}テストでは「Cloudwatch Logs」のテストイベントを使用することで、通知確認が可能です。
※ChatBot宛てに送るメッセージは、ChatBotとSlackのメッセージのフォーマットに合わせるようにする必要があります。それぞれの制約については、本記事の最後の参考文献をご確認ください。
ログエラーの内容を流す
今回は紹介のために「ERROR」という文字列のエラーを発火させるLambdaを作成します。
プロジェクトで扱うログメッセージがロググループに既に存在する場合は、こちらはスキップしていただいて構いません。
export const handler = async (event) => {
// Errorオブジェクトのメッセージとして "ERROR" を渡す
throw new Error("ERROR:テスト用のエラー");
};エラーを検出する
通知の導線が整ったら、最後にCloudWatch Logsでエラーを検知するための設定を行います。
サブスクリプションフィルターの作成
まず、AWSコンソールの CloudWatch > ロググループ から、エラー通知を行いたい対象のロググループを選択します。
[サブスクリプションフィルター] セクションに移動します。
[Lambda サブスクリプションフィルターを作成] を選択します。
[転送先 Lambda 関数] として、先ほど作成した「Slack通知用Lambda」を指定します。
ログパターンの定義
次に、どのログを通知対象とするかを決定する「ログイベント形式とフィルターパターン」を設定します。今回はテストとして ERROR という文字列を指定します。
運用上の注意点
実運用に投入する前に、以下の2点を必ず確認してください。
無限ループの防止
通知用Lambda自身のロググループに対して、同じ通知用Lambdaへ転送するフィルターを設定しないでください。「通知のエラーがログに出力され、それを検知してまた通知を送る」という無限ループが発生し、予期せぬ高額請求を招く恐れがあります。コストと流量の管理
サブスクリプションフィルターはログが出力されるたびにLambdaを起動します。あまりに広範なキーワード(例:単なる空白や頻発する文字列)を設定すると、Lambdaの実行費用が膨らむ可能性があります。まずは具体的なエラーコードなどで絞り込み、予算アラートを設定した上で運用を開始することを推奨します。
おわりに
今回は、CloudWatchのサブスクリプションフィルターを活用して、Lambdaのエラー内容をSlackへ即座に通知する仕組みについて解説しました。
今回紹介した構成を導入することで、わざわざAWSコンソールにログインしてログを探しに行く手間が省け、Slack上での迅速な一次解析が可能になります。
もちろん、通知の自動化にはさまざまなアプローチがあります。通知の頻度、コスト、必要な情報の粒度など、プロジェクトの要件に応じて最適な手法を柔軟に選択していきましょう。本記事の内容が、皆さんの効率的な運用体制づくりの一助となれば幸いです。
参考文献
AWS Budgets によるコストの管理 - AWS コスト管理
ログイベントの送信を検証 - Amazon CloudWatch Logs
Amazon Q Developer in chat applications
Slack Developer Docs
関連記事
- FinOps段階導入の実践ガイド
橋本 真志
- DevSecOpsを加速、OWASP ZAP×DVWAで学ぶ...
Tsai Yalin
What is BEMA!?
Be Engineer, More Agile


