Claude Media
Claude CodeでCloudWatch Logsの費用を下げる — 取り込み量の順位付けと保持期間

Claude CodeでCloudWatch Logsの費用を下げる — 取り込み量の順位付けと保持期間

IncomingBytesでロググループを取り込み量順に並べ、保持期間をTerraformで設定し、DEBUGログの出力元をClaude Codeに探させる手順です。

CloudWatch Logsの請求を下げる手は、保持期間を短くすることと、取り込むログを減らすことの2つです。効く場所が違います。保持期間は保存にかかる分、ログレベルの見直しは取り込みにかかる分を減らします。

この記事は、取り込み量が多いロググループをIncomingBytesで順位付けするところから始めます。続いて保持期間をTerraformで固定し、DEBUGログの出力元をClaude Codeにコードから探させる流れを示します。障害調査のクエリはClaude CodeでCloudWatch Logsを集計しエラーの急増を見つけるの守備範囲なので、ここでは費用の観点だけを扱います。

先に分かれ目を押さえる

保持期間を短くしても、取り込み料金は下がりません。取り込み済みのログに対する課金は、すでに発生しているからです。

くらべる

どちらの費用に効く手か

保存の費用

保持期間を短くする

古いログが消えるぶん、保存するデータ量が減ります。デプロイ済みのロググループにもその場で効きます。

取り込みの費用

ログレベルを上げる・出力を減らす

そもそも送らないので、取り込みと保存の両方が減ります。アプリケーション側の変更が要ります。

AWSのドキュメントによれば、ロググループの保持期間は既定で無期限です。設定しないまま放置されたロググループは、保存量が増え続けます。まず順位を見て、効く場所を決めます。

取り込み量の多いロググループを順位付けする

ロググループ別の取り込み量は、AWS/Logs名前空間のIncomingBytesメトリクスに入っています。ディメンションはLogGroupName、統計はSum、単位はバイトです。値は圧縮前のバイト数で、請求書の数字とそのまま一致する保証はありません。順位を付けるための目安として使います。

読み取りだけを許可する

Claude Codeに調べさせる前に、実行してよいコマンドを絞ります。ここで必要なのは、ロググループの一覧とメトリクスの取得だけです。

{
  "permissions": {
    "allow": [
      "Bash(aws logs describe-log-groups *)",
      "Bash(aws cloudwatch get-metric-statistics *)",
      "Bash(bash scripts/cwl-top-ingest.sh *)"
    ],
    "deny": [
      "Bash(aws logs put-retention-policy *)",
      "Bash(aws logs delete-log-group *)"
    ]
  }
}

denyにput-retention-policyを入れているのは、保持期間の変更をコマンドで直接やらせないためです。ルールはdeny、ask、allowの順に評価され、最初に一致したものが結果を決めます。保持期間は次の節のとおりTerraformで変えるので、CLIからの変更は止めておきます。

もう1つ、ループで複数のコマンドを並べると、複合コマンドとして扱われます。権限ドキュメントによると、区切り文字でつながったコマンドは、すべての部分が許可されていないと確認なしでは動きません。そこでループはシェルスクリプトに閉じ込め、スクリプト1本を許可します。

順位付けのスクリプト

次のスクリプトは、期間を引数で受け取り、ロググループごとのIncomingBytes合計を多い順に並べます。

#!/usr/bin/env bash
# 使い方: bash scripts/cwl-top-ingest.sh <開始 ISO8601> <終了 ISO8601>
set -euo pipefail
 
for g in $(aws logs describe-log-groups \
    --query 'logGroups[].logGroupName' --output text); do
  bytes=$(aws cloudwatch get-metric-statistics \
    --namespace AWS/Logs --metric-name IncomingBytes \
    --dimensions Name=LogGroupName,Value="$g" \
    --start-time "$1" --end-time "$2" \
    --period 86400 --statistics Sum \
    --query 'sum(Datapoints[].Sum)' --output text)
  printf '%s\t%s\n' "${bytes:-0}" "$g"
done | sort -rn | head -20

--periodは86400秒(1日)にしています。get-metric-statisticsは、開始時刻が15日より前だと期間に5分の倍数、63日より前だと1時間の倍数を求めます。1回の呼び出しで返るデータポイントは最大1,440件です。1日刻みなら、直近1か月を見ても30件で収まります。

実行はClaude Codeに任せられます。依頼文の例は次のとおりです。

scripts/cwl-top-ingest.shを過去30日の期間で実行して、上位10のロググループをバイト数のGB換算で表にして。

GB換算はClaude側で行わせ、元のバイト数も併記させます。換算を暗算に任せると桁を誤りやすいためです。

保持期間が未設定のグループも洗い出す

順位とは別に、保持期間が無期限のままのロググループを出します。describe-log-groupsのレスポンスにはretentionInDaysとstoredBytesがあり、無期限のグループではretentionInDaysが返りません。

aws logs describe-log-groups \
  --query 'logGroups[?retentionInDays==null].[logGroupName,storedBytes]' \
  --output text

取り込み量の上位と、無期限のグループが重なっているものから手を付けるのが近道です。

保持期間をTerraformで固定する

ロググループをaws_cloudwatch_log_groupで管理しているなら、retention_in_daysを足すだけです。

resource "aws_cloudwatch_log_group" "api" {
  name              = "/app/prod/api"
  retention_in_days = 30
}

指定できる値は、1、3、5、7、14、30、60、90、120、150、180、365、400、545、731、1096、1827、2192、2557、2922、3288、3653と、無期限を意味する0です。この一覧にない日数は指定できません。log_group_classをDELIVERYにした場合、この引数は無視され、保持期間は2日に固定されます。

複数のグループにまとめて設定する

上位5グループのように対象が複数あるなら、マップとfor_eachで1か所にまとめます。

locals {
  log_retention = {
    "/app/prod/api"    = 30
    "/app/prod/worker" = 14
    "/app/stg/api"     = 7
  }
}
 
resource "aws_cloudwatch_log_group" "managed" {
  for_each          = local.log_retention
  name              = each.key
  retention_in_days = each.value
}

環境ごとに日数を変えられるので、ステージングは短く、本番は長めに置けます。

既存のロググループはimportが先

nameを変えるとリソースが作り直しになります。すでに存在するロググループにそのままresourceを書いてapplyすると、同名の作成でぶつかります。先にimportして状態に取り込み、planの差分がretention_in_daysだけになることを確かめてから反映します。手順はClaude CodeでTerraformのimportを進めるにまとめています。

依頼文には次の条件を入れておくと、事故が減ります。

取り込み量の上位5グループについて、retention_in_daysを追加するHCLを書いて。planで差分が保持期間だけであることを示して。applyは実行しない。

短縮は取り消せない

落とし穴

保持期間を短くすると、古いログは戻らない

AWSのドキュメントでは、保持期間を過ぎたログイベントは削除対象としてマークされ、その後は保存料金の対象から外れます。実際の削除は通常72時間ほどかかり、まれに長引くとされています。短くした設定を後で長く戻しても、すでに削除されたログは復元できません。監査要件や調査で遡る期間がある場合は、短縮の前に必要な日数を決めておきます。

取り込み量そのものを減らす

保持期間で下がるのは保存の費用です。取り込みの費用を下げるには、送るログを減らします。

ログレベル別にどれだけ送っているか測る

DEBUGが大きな割合を占めているかを、Logs Insightsで測ります。クエリ中のparseでレベル名を取り出し、strlenとsumで文字数を合計します。

parse @message /(?<level>DEBUG|INFO|WARN|ERROR)/
| stats count(*) as events, sum(strlen(@message)) as chars by level
| sort chars desc

書式は、ログの形に合わせて直します。JSONログならlevelフィールドをそのままbyに使えます。実行にはaws logs start-queryとget-query-resultsの許可が必要で、権限の切り方は先の記事と同じです。スキャン量に課金がかかるので、期間は短く取ります。

DEBUGの出力元をコードで特定する

どのロググループが重いかが分かったら、そのサービスのソースを開き、ログレベルの決まり方を調べさせます。依頼は具体的に書くほど、探索が絞れます。

/app/prod/apiのログを出しているサービスはservices/apiです。ログレベルの設定箇所を洗い出して。環境変数、設定ファイル、コード内の初期化処理をそれぞれ挙げて。本番でDEBUGになりうる経路があれば、その条件も書いて。

見つけるべきものは、たとえば次のとおりです。

  • 本番の設定が環境変数の既定値に頼っていて、未設定だとDEBUGになる
  • ループの内側や、リクエストごとに全体を出力するdebug呼び出しが残っている
  • サードパーティのライブラリが、独自の設定でDEBUGを出している

CLAUDE.mdに調べ方の型を書いておくと、毎回の依頼が短くなります。

## ログ費用の調査ルール
- ログ量はaws cloudwatch get-metric-statisticsのIncomingBytesで確認する
- 保持期間の変更はTerraformのretention_in_daysで行い、CLIで直接変更しない
- ログレベルを変える提案には、本番のレベル、変更箇所、影響を受けるアラートを添える

ロググループの種類で下げる手もある

CloudWatch Logsには、標準クラスのほかにInfrequent Accessクラスがあります。AWSのドキュメントによれば、2つの違いは取り込み料金だけで、保存料金とLogs Insightsの料金は同じです。Infrequent Accessは、アドホックな検索や事後の調査に向いた、機能を絞ったクラスです。

ただし、ロググループを作成した後に、ログクラスを変更することはできません。変えるなら、新しいロググループを作って出力先を切り替えます。

機能差も大きく、Infrequent AccessではLive Tail、メトリクスフィルター、サブスクリプションフィルター、FilterLogEventsが使えません。アラームの元にしているログや、リアルタイムで追うログは標準クラスに残します。

効果を見る

変更後は、同じスクリプトを同じ期間の長さで再実行します。比較するのは2つです。

  1. 対象グループのIncomingBytesの合計が、ログレベルの変更後に減ったか
  2. 保持期間を短くしたグループのstoredBytesが減ったか

AWSのドキュメントには、削除対象としてマークされたログはstoredBytesの値に含まれないとあります。マークされた時点で数字は下がり始めます。実際にいくら下がったかは、費用の内訳を見ます。取り込み量と請求は完全には一致しないので、Claude CodeでInfracostを使い、PRのクラウド費用を下げる手順のように、変更前後を継続的に見る仕組みと組み合わせると確実です。

AWS CLIをMCP経由で動かす構成で権限を設計する場合は、aws-api-mcp-serverの権限設計が詳しい記事です。

まとめ

保持期間の見直しは、ロググループごとにTerraformで書けて、効果も早く出ます。ただし下がるのは保存の費用だけです。取り込みの費用を動かすには、DEBUGをはじめ、不要なログを出している箇所をコードから特定して、出力そのものを減らす必要があります。Claude Codeには読み取りだけを許可して順位と出力元を探させ、変更はTerraformのPRとしてplanの差分で確かめるのが、手戻りの少ない進め方です。

参照した一次ソース

この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。

  1. code.claude.com/docs/en/permissions
  2. docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch-Logs-Monitor…
  3. docs.aws.amazon.com/AmazonCloudWatch/latest/logs/Working-with-log-groups-and-s…
  4. docs.aws.amazon.com/AmazonCloudWatch/latest/logs/CloudWatch_Logs_Log_Classes.h…
  5. docs.aws.amazon.com/cli/latest/reference/cloudwatch/get-metric-statistics.html
  6. registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_l…
この記事を共有:XはてブLinkedIn