{
"$type": "site.standard.document",
"bskyPostRef": {
"cid": "bafyreicp4gkuw2av5yugxnkwp63kqnwo7vvkpjrh2sm5pdsiqcezmegsge",
"uri": "at://did:plc:ctnrfotecqqhvo7kl5cgtpwt/app.bsky.feed.post/3miasdbfk5n72"
},
"coverImage": {
"$type": "blob",
"ref": {
"$link": "bafkreih2neo5vcziuzon2jgaejt4pclo33zlkqhuzyplzbt6ohp522xtxy"
},
"mimeType": "image/png",
"size": 759187
},
"path": "/entry/2026/03/30/100000",
"publishedAt": "2026-03-30T01:00:00.000Z",
"site": "https://toranoana-lab.hatenablog.com",
"tags": [
"https://api.slack.com/apps",
"toranoana-lab.co.jp"
],
"textContent": "こんにちは!虎の穴ラボの鷺山です。\n\nAWSでアプリケーションを運用すると、ログをCloudWatch Logsに集約・管理できて便利です。\n\nただ、定期的なログ監視を人力で行うのは大変です。AWSのコンソールに都度アクセスしてチェックするのも手間がかかります。とはいえ、エラーログや不穏なログにはすぐに気付けるようにしておきたいです。\n\nそこで今回は、**CloudWatchのエラーログをSlackに転送する仕組み** の作り方をご紹介します。\n\n普段使っているSlackなどのビジネスチャットツールにログを流すことで、負担なくログ監視ができるようになります。\n\n## 前提環境\n\n項目 | バージョン・値\n---|---\nPython | 3.14\nAWSリージョン | `ap-northeast-1`\n\n## SlackのWebhook URLの作成\n\nSlackにログを転送する上で必要なWebhook URLを作成します。\n\n 1. Slackアプリの管理ページ https://api.slack.com/apps に移動します。\n 2. **Create New App** または**Create an App** を押し、**From scratch** を選択します。\n 3. **App Name** にアプリ名を入力し、**Pick a workspace to develop your app in:** に作成先のワークスペースを選択します。\n * 今回は**ERRORログ通知** というアプリ名とします。\n * **Create App** を押すとアプリが作成されます。\n 4. アプリが作成されたら**Incoming Webhooks** メニューを開き、**Activate Incoming Webhooks** を**On** にします。\n 5. 次に**App Home** メニューを開き、**Your App's Presence in Slack** の**App Display Name** を設定します。\n * **Display Name (Bot Name):** ボットの表示名を入力します。\n * **Default username:** ボットのユーザー名を入力します。\n 6. もう一度**Incoming Webhooks** メニューを開き、**Webhook URLs for Your Workspace** の**Add New Webhook** を押します。\n 7. アクセス権の設定画面で投稿先のチャンネルを選択して**許可する** を押します。これでWebhook URLが生成されます。\n\n\n\n「ERRORログ用」と「WARNログ用」でApp (Webhook URL)を分けて作成すると、Slack上でログの種別が分かりやすくなるのでおすすめです。\n\n## Lambda関数の作成\n\nCloudWatchのログをSlackに転送するLambda関数を作成します。\nSlackでの視認性を高めるため、ログを見やすい形式に整形して転送します。\n\n今回は**sendLogsToSlack** という関数名にしました。ランタイムは**Python** を使用します。\n\nコードは以下のようになります。\n\n\n import base64 import gzip import json import os from urllib.parse import quote from urllib.request import Request, urlopen # 環境変数からSlack Webhook URLを取得 SLACK_WEBHOOK_URL_FOR_ERROR = os.environ[\"SLACK_WEBHOOK_URL_FOR_ERROR\"] SLACK_WEBHOOK_URL_FOR_WARN = os.environ[\"SLACK_WEBHOOK_URL_FOR_WARN\"] def lambda_handler(event, context): # Base64からデコードし、gzipを解凍 raw_data = event.get(\"awslogs\", {}).get(\"data\") decoded_data = base64.b64decode(raw_data) unzipped_data = gzip.decompress(decoded_data) # JSON形式のデータをロードし、ログメッセージを抽出 json_data = json.loads(unzipped_data.decode(\"utf-8\")) log_group = json_data.get(\"logGroup\") log_stream = json_data.get(\"logStream\") log_events = json_data.get(\"logEvents\") messages = [log_event[\"message\"] for log_event in log_events] print(f\"Incoming data: {json.dumps(json_data)}\") webhook_url = get_webhook_url(messages) # 各ログメッセージをSlackに送信 for i, message in enumerate(messages): header = build_header(log_group, log_stream) if i == 0 else \"\" # 長いメッセージはカットし、改行を復元 message = message[:300].replace(\"\\\\n\", \"\\n\").rstrip() message = f\"{header}```\\n{message}\\n```\" # Slackへのリクエストを作成し送信 payload = json.dumps({\"text\": message}).encode(\"utf-8\") req = Request(webhook_url, payload) urlopen(req).read() return {\"statusCode\": 200} def get_webhook_url(messages): message = messages[0] if \" ERROR \" in message.upper(): # 👈 ログフォーマットに応じて変更してください return SLACK_WEBHOOK_URL_FOR_ERROR else: return SLACK_WEBHOOK_URL_FOR_WARN def build_header(log_group, log_stream): app_name = log_group.split(\"/\")[-1] cloudwatch_url = build_cloudwatch_url(log_group, log_stream) return f\"• <{cloudwatch_url}|`{app_name}`>:\\n\" def build_cloudwatch_url(log_group, log_stream): escaped_log_group = quote(log_group, safe=\"\").replace(\"%2F\", \"%252F\") escaped_log_stream = quote(log_stream, safe=\"\").replace(\"%2F\", \"%252F\") return ( f\"https://ap-northeast-1.console.aws.amazon.com\" f\"/cloudwatch/home?region=ap-northeast-1\" f\"#logsV2:log-groups/log-group/{escaped_log_group}\" f\"/log-events/{escaped_log_stream}\" )\n\n### 💡ポイント\n\n上記のコードの各ポイントを解説します。\n\n\n # 環境変数からSlack Webhook URLを取得 SLACK_WEBHOOK_URL_FOR_ERROR = os.environ[\"SLACK_WEBHOOK_URL_FOR_ERROR\"] SLACK_WEBHOOK_URL_FOR_WARN = os.environ[\"SLACK_WEBHOOK_URL_FOR_WARN\"]\n\nこのLambda関数で使用する環境変数です。\n\nキー | 値\n---|---\n**SLACK_WEBHOOK_URL_FOR_ERROR** | 前のセクションで生成したERRORログ用のWebhook URL\n**SLACK_WEBHOOK_URL_FOR_WARN** | 前のセクションで生成したWARNログ用のWebhook URL\n\n「設定」タブから事前に設定してください。\n\n\n # Base64からデコードし、gzipを解凍 raw_data = event.get(\"awslogs\", {}).get(\"data\") decoded_data = base64.b64decode(raw_data) unzipped_data = gzip.decompress(decoded_data)\n\nCloudWatchから転送されるデータにはBase64エンコードとgzip圧縮が施されているため、デコードと解凍を行っています。\n\n\n webhook_url = get_webhook_url(messages)\n\n\n def get_webhook_url(messages): message = messages[0] if \" ERROR \" in message.upper(): # 👈 ログフォーマットに応じて変更してください return SLACK_WEBHOOK_URL_FOR_ERROR else: return SLACK_WEBHOOK_URL_FOR_WARN\n\nログのメッセージに含まれる文字列からエラーレベルを判定し、ERRORとWARNのどちらのWebhook URLに転送するかを判定しています。\nアプリケーションのログフォーマットに応じて変更してください。\nここでは、前後に半角スペースを含む**「 ERROR 」** という文字列が含まれる場合にERRORレベルと判定しています。\n\n\n # 各ログメッセージをSlackに送信 for i, message in enumerate(messages): header = build_header(log_group, log_stream) if i == 0 else \"\" ...\n\n\n def build_header(log_group, log_stream): ... def build_cloudwatch_url(log_group, log_stream): ...\n\nロググループ・ログストリームの情報から、ヘッダー文字列を作成しています。\n出力元のログストリーム名を含めることで「どのシステムからの出力か」を分かりやすくしています。\nさらにCloudWatchのURLを含めることで、AWSのコンソールに移動しやすくしています。\n\n\n # 長いメッセージはカットし、改行を復元 message = message[:300].replace(\"\\\\n\", \"\\n\").rstrip() message = f\"{header}```\\n{message}\\n```\" # Slackへのリクエストを作成し送信 payload = json.dumps({\"text\": message}).encode(\"utf-8\") req = Request(webhook_url, payload) urlopen(req).read()\n\n最後に、長いメッセージを300文字で切り出したり、改行を復元 (`\\\\n` → `\\n`) などの加工をした後、Slackへメッセージを送信しています。\n\n## サブスクリプションフィルターの作成\n\n上記で作成したLambda関数に向けて、CloudWatchからログが転送されるように設定します。\n\n対象のロググループの画面から**Lambda サブスクリプションフィルターを作成** を選択します。\n\n上記で作成したLambda関数 (`sendLogsToSlack`) を選択します。\n\nログ形式に**その他** を選択し、フィルターパターン、フィルター名を入力します。\n\n例えば、前後に半角スペースを含む**「 ERROR 」** や**「 WARN 」** という文字列が含まれていた場合にログを転送させるには、以下のようにフィルターパターンを設定します。\n\n\n ?\" ERROR \" ?\" WARN \"\n\n`\"...\"`で条件のグルーピング、`?`でOR条件を表現することができます。\n\n## 実行結果\n\n以上の設定で、ERRORやWARNのログメッセージがSlackに転送されるようになります。\n\n▼ERRORログ\n\n▼WARNログ\n\n各メッセージのヘッダーには対象のログストリームへのリンクも含んでいるため、AWSのコンソールに移動してログの全容をすぐに確認できます。\n\n## まとめ\n\nCloudWatchのエラーログを、Slack WebhookとLambdaを組み合わせてSlackに自動転送する仕組みをご紹介しました。\nサブスクリプションフィルターでERRORやWARNを絞り込むことで、Slack上で必要なログだけを手軽に確認できます。\nAWSでアプリケーションを運用している方はぜひ試してみてください!\n\n# 採用情報\n\n虎の穴ラボでは一緒に働く仲間を募集中です!\nこの記事を読んで、興味を持っていただけた方はぜひ弊社の採用情報をご覧ください。\ntoranoana-lab.co.jp",
"title": "CloudWatchのエラーログをSlackにリアルタイム転送する方法",
"updatedAt": "2026-03-30T01:00:01.000Z"
}