はじめに
AWSで運用しているシステムのログ、毎日確認するの大変ですよね。
Lambda関数のエラーログ、ECSコンテナの実行ログ、API Gatewayのアクセスログ...。
CloudWatch Logsに蓄積されるログを毎日チェックして、エラーが出てないか、パフォーマンスに問題はないか確認するのは時間もかかるし、見落としも心配です。
そこで、Amazon Bedrockを使ってCloudWatch Logsを自動で分析し、日本語のレポートを生成するシステムを作ってみました。
作ったもの
毎日決まった時間に、前日のログを自動で分析して、こんなレポートをメールで送ってくれるシステムです。
# システム運用レポート 2026-XX-XX
## システム健全性評価
- 全体評価: 🟢 優良
- エラー率は低い水準で安定稼働しています
## ユーザー利用状況
- 朝の時間帯に利用が集中しています
- 複数部署からのアクセスを確認
## 注意すべき事項
- 特定時間帯でAPI制限に到達する事象を観察
- システム全体への影響は限定的です
## 運用推奨事項
- API制限値の見直しを検討してください
- エラー集中時間帯の利用パターンを分析することを推奨します
技術的な知識がなくても読めるレポートになっています。
システム構成
今回の実装では、AWS Amplifyで動作しているWebアプリケーションのログを分析しています。
AmplifyアプリケーションはCloudWatch Logsにログを自動出力するため、
そのログをLambda関数で取得し、Bedrockで分析する構成です。
(アプリケーションの規模や特徴については後述で詳しく説明します)
構成としては比較的シンプルに作成しています。
CloudWatch Logsに出力されるログであれば、どのAWSサービスでも分析対象にできます。
EventBridge (毎日12:00 JST)
↓
Lambda (Python 3.12, 512MB, 5分タイムアウト)
↓
CloudWatch Logs (ログ取得、最大60,000件/日)
↓
Lambda (統計計算・サンプリング)
↓
Bedrock (Claude Sonnet 4, APAC Inference Profile)
↓
S3保存 + SNS通知
使用している主要技術:
- Lambda関数: Python 3.12、メモリ512MB、タイムアウト120秒
- AIモデル: Claude Sonnet 4 (
apac.anthropic.claude-sonnet-4-20250514-v1:0) - スケジュール: EventBridge (cron式:
0 3 * * ? *= 毎日12:00 JST) - ログ取得: CloudWatch Logs API (最大60,000件/日)
実装のポイント
全部のログをBedrockに突っ込むことができれば一番楽ですが、当然ながら上限があります。
そのため、必要な情報をレポートに出力するために以下の3つを実施しました。
1. 全ログから統計を計算
Bedrockにはトークン制限があるので、全ログを対象とするのは難しいです。
そのため重要なログだけをサンプリングして送る必要がありますが、サンプリングすると全体の統計が不正確になってしまいます。
エラー率やアクセスユーザー数などの統計情報は正確な数値が欲しいですよね。
そこで、サンプリングする前にあらかじめLambdaで全ログから統計を計算しておきます。
# CloudWatch Logsから前日分のログを全件取得
logs = get_all_logs(log_group_name, start_time, end_time)
# 統計情報を計算
stats = {
'total_logs': len(logs),
'error_count': sum(1 for log in logs if log['level'] == 'ERROR'),
'error_rate': (error_count / len(logs)) * 100,
'unique_users': len(set(log['user_id'] for log in logs)),
'avg_response_time': sum(log['response_time'] for log in logs) / len(logs)
}
こうしておくことで、全体としての運用状況をレポートに反映することができます。
2. 重要なログをサンプリング
統計情報は計算できましたが、Bedrockに実際のログを解析してもらうのは必須です。
ログが全部で1,000件未満なら全部解析してもらうこともできますが、そんなシステムは珍しいかと思います。
※ CRITICALだけ出しているとかなら別ですが...
動作確認で使った社内アプリでは全レベル合わせて約60,000件/日のログ数を出しているので、このまま全てBedrockに送るとトークン制限(今回のモデルのClaudeの場合200Kトークン)を超えてしまいます。
そこで、重要なログだけを抽出します。
# エラーログを優先(ログレベルで判定)
error_logs = [log for log in logs if log['level'] in ['ERROR', 'CRITICAL']]
# 営業時間帯のログを優先(タイムスタンプで判定)
business_hours_logs = [log for log in logs
if log not in error_logs
and 9 <= log['timestamp'].hour <= 18]
# トークン数を見ながら段階的に追加
sampled_logs = []
estimated_tokens = 0
MAX_TOKENS = 180000 # Claude Sonnet 4のコンテキストウィンドウから余裕を見た値
for log in error_logs + business_hours_logs:
log_text = f"{log['timestamp']} [{log['level']}] {log['message']}"
estimated_tokens += len(log_text) // 4 # 簡易推定(英語基準)
if estimated_tokens > MAX_TOKENS:
break
sampled_logs.append(log_text)
サンプリングのポイント:
- エラーログは全て含める(最優先)
- 通常ログは営業時間帯を優先
- トークン上限を超えないように制御
この結果、大体1,500件程度にサンプリングされるようになりました。
優先的にエラーログを渡しているので、仮にエラーログが1,500件程度出た場合以外は通常ログも対象にできます。
3. Bedrockでレポート生成
先ほどまでの内容でレポート作成に必要な統計情報と分析してほしいログ情報を整理したので、これらの内容をBedrockに送ります。
この際にBedrockへの指示として以下のプロンプトを送信します。
prompt = f"""
あなたはシステム運用の専門家です。
以下のログを分析して、日本語でレポートを作成してください。
## 統計情報(全ログから計算)
- 総ログ数: {stats['total_logs']}件
- エラー率: {stats['error_rate']}%
- ユニークユーザー数: {stats['unique_users']}人
- 平均レスポンス時間: {stats['avg_response_time']}ms
## ログサンプル
{sampled_logs}
## レポート項目
1. システム健全性評価
2. ユーザー利用状況
3. システムパフォーマンス
4. 注意すべき事項
5. 運用推奨事項
"""
# Bedrock APIを呼び出し
response = bedrock_client.invoke_model(
modelId="apac.anthropic.claude-sonnet-4-20250514-v1:0",
body=json.dumps({
"anthropic_version": "bedrock-2023-05-31",
"max_tokens": 4000,
"messages": [{"role": "user", "content": prompt}]
})
)
実装時にハマったポイント
実装中に2つの大きな壁にぶつかりました。
トークン上限の見積もりミス
Claude Sonnet 4は200Kトークンのコンテキストウィンドウがあります。
最初は「200K使えるなら色々できるんじゃないか?」と思って実装したのですが、実際に動かすとリクエストエラーが頻発しました。
調べてみると:
- 出力用に4Kトークンが必要
- APIのオーバーヘッドも考慮が必要
- 実際に入力に使えるのは約180Kトークン程度
200Kフルに使えると思って実装すると「リクエストが大きすぎる」とエラーになってしまうので、トークンについては余裕を見ておいたほうが良いです。
日本語ログのトークン数計算
180Kトークンを上限として設定し、「4文字=1トークン」で計算、さらにマージンを1,000トークン確保していたのですが、それでもリクエストエラーが出続けました。
マージンを調整しても変化がなかったので原因を調べたところ、今回のアプリではログに日本語が入っており、英語のみの場合とトークンの扱いが違うことが判明しました。
英語基準の「4文字=1トークン」は日本語では不正確でした。
そのため、以下の調整を実施:
- 日本語混在の場合は消費トークンを多めに見積もる
- 安全マージンを10%程度確保する
- 実用的な送信上限を設定
最終的に、動作確認で使った社内アプリの1日あたりのエラーログ数と、分析に必要な通常ログ数を考慮し、Bedrockに送信するログの上限を25Kトークンとして設定しました。
この設定により、安定してAPI呼び出しができるようになり、現在まで一度もエラーは発生していません。
日本語ログを扱う場合は、英語基準の簡易推定式に頼らず、対象システムのログ特性を踏まえて余裕を持った上限設定をすることをお勧めします。
クロスアカウント構成でのセキュリティ
今回は本番環境のログを分析するため、セキュリティを考慮してクロスアカウント構成を採用しました。
ターゲットとなるアプリ実行アカウントは分析アカウントに対してログ読み取りのみ許可を出すことでログ本体には手出しさせないようにしています。
構成
アプリ実行アカウント(ログソース)
↓ AssumeRole(読み取り専用)
分析実行アカウント(Lambda + Bedrock)
アプリ実行アカウント側の設定
IAMロール: [ログ読み取りロール]
Trust Policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::[分析アカウントID]:role/[Lambda実行ロール]"
},
"Action": "sts:AssumeRole"
}
]
}
Permissions Policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:FilterLogEvents",
"logs:DescribeLogStreams"
],
"Resource": "arn:aws:logs:ap-northeast-1:[アカウントID]:log-group:/aws/amplify/[APP_ID]:*"
}
]
}
分析実行アカウント側の設定
IAMロール: [Lambda実行ロール]
主要な権限:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Resource": "arn:aws:iam::[アプリアカウントID]:role/[ログ読み取りロール]"
},
{
"Effect": "Allow",
"Action": "bedrock:InvokeModel",
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObject"
],
"Resource": "arn:aws:s3:::[レポートバケット]/*"
},
{
"Effect": "Allow",
"Action": "sns:Publish",
"Resource": "arn:aws:sns:ap-northeast-1:[分析アカウントID]:*"
}
]
}
工夫点
- ログソース側はCloudWatch Logs読み取り専用、分析側は必要な権限のみ付与
- 特定のLambdaロールからのみAssumeRole可能
- AssumeRoleで一時的な認証情報を取得、自動的に期限切れ(デフォルト1時間)
- 分析システムの障害が本番環境に影響しない
Amplifyのアプリに使ってみた
今回は、社内で使用しているAmplify Webアプリケーション(利用ユーザー数約50人の小規模システム)を分析対象として実装しました。
システムの特徴
- 1日あたり約60,000件のログ
- 日本語メッセージを含むログが混在
- エラーログ、アクセスログ、パフォーマンスログが混在
運用結果
前提条件:
- ログ量: 約60,000件/日
- 実行頻度: 1日1回(毎日12:00 JST)
- リージョン: ap-northeast-1(東京)
実績:
- 実行時間: 平均37.4秒
- 安定性: 稼働率100%
コスト(月額30日換算、1ドル=150円):
- Bedrock API料金: 408円/月
- 入力: 606Kトークン × \$3.00/Mトークン = $1.82
- 出力: 60Kトークン × \$15.00/Mトークン = $0.90
- 小計: $2.72
- Lambda実行料金: 2円/月(実行時間: 1,200秒/月 × 512MB)
- CloudWatch Logs: 23円/月(ログ出力: 30回 × 10MB)
- その他(S3、SNS、EventBridge): 1円未満
- 合計: 月額432円($2.88)
生成されたレポート
このログを分析した結果、以下のようなレポートが毎日メールで届くようになりました。
レポートの内容:
- システム健全性評価(エラー率、稼働状況)
- ユーザー利用状況(アクセスパターン、ピーク時間帯)
- システムパフォーマンス(レスポンス時間、ボトルネック)
- 注意すべき事項(異常検知、警告)
- 運用推奨事項(改善提案)
他のサービスへの展開
このシステムは、CloudWatch Logsをデータソースとしているので、
今回の実装範囲だけでもCloudWatch Logsにログを出力するAWSサービスであれば、どれでも分析対象にできます。
対応可能なサービス例:
- AWS Lambda → 関数の実行ログ
- Amazon ECS/Fargate → コンテナログ
- Amazon API Gateway → アクセスログ
- AWS Amplify → アプリケーションログ
- AWS App Runner → Webアプリログ
- Amazon RDS → データベースログ
- Amazon EC2 → アプリケーションログ
Lambda関数の環境変数を変えるだけで対応可能です。
環境変数の設定例:
# Lambda用
LOG_GROUP_NAME = "/aws/lambda/my-function"
# ECS用
LOG_GROUP_NAME = "/ecs/my-cluster/my-task"
# API Gateway用
LOG_GROUP_NAME = "API-Gateway-Execution-Logs_{rest-api-id}/{stage}"
# Amplify用
LOG_GROUP_NAME = f"/aws/amplify/{AMPLIFY_APP_ID}"
まとめ
Amazon Bedrockを使って、CloudWatch Logsの自動分析システムを作りました。
実装できたポイント:
- 毎日のログ分析とレポート作成を自動化できた
- 重要なエラーを見つけやすくなった
- 報告してほしい内容に調整しやすくなった
- 低コストで運用できる(月額500円程度)
- CloudWatch Logsを使う任意のAWSサービスに適用できる
技術的なポイント:
- 全ログから統計を計算(正確性)
- 重要なログをサンプリング(トークン制限対応)
- 日本語混在のログは消費トークンを多めに見積もる(注意点)
Bedrockを使った実用的なログ分析を思ったより簡単にできました。
Lambda、ECS、API Gatewayなど使っていて手軽に分析したいと考えている方はやってみてください!
また、re:Invent2025で発表されたBedrock AgentCoreの機能を使ったらもっと良くなるのかなと思っています。
Memoryで過去のレポートを踏まえた運用提案をしたりとか、Evaluateで精度的にどうなのか、他に分析に適したモデルがないかなど。。。
そのあたりも追々試せたらいいなと思っています

