CloudWatchの新機能「Log Based Alarm」でLogs Insightsクエリ結果をアラーム化してみた

CloudWatchの新機能「Log Based Alarm」でLogs Insightsクエリ結果をアラーム化してみた

CloudWatchに追加された新しいアラームタイプ「Log Based Alarm」を使い、Logs Insightsクエリ結果をアラーム化してみました。PutLogAlarm APIでの作成手順、ログ行付き通知(ActionLogLineCount)、EventBridge連携に加え、`if`/`case` 関数と組み合わせた比率アラームの実現方法まで検証しています。
2026.06.30

はじめに

2026年6月29日、CloudWatchに新しいアラームタイプ「Log Based Alarm」が追加されました。AWS CLI v2のChangelogには以下の記載があります。

2.35.12

api-change:cloudwatch: This release adds the API (PutLogAlarm) to manage a new CloudWatch resource, Log Based Alarms. Log Based Alarms allows customers to alarm directly on CloudWatch Logs query results.

https://raw.githubusercontent.com/aws/aws-cli/v2/CHANGELOG.rst

PutLogAlarm のCLIリファレンスは以下で公開されています。

https://docs.aws.amazon.com/cli/latest/reference/cloudwatch/put-log-alarm.html

従来、CloudWatch Logsのログ内容に基づくアラームにはメトリクスフィルター経由のカスタムメトリクス作成が必要でした。Log Based Alarmではこれが不要になり、Logs Insightsクエリで抽出・加工した結果を AggregationExpression で集約し、その値を直接アラーム評価に利用できます。従来方式との主な違いは以下の通りです。

項目 従来(メトリクスフィルター + MetricAlarm) 新(Log Based Alarm)
設定手順 メトリクスフィルター作成 → メトリクス確認 → アラーム作成 PutLogAlarm 1 API コールで完結
クエリの柔軟性 固定パターンマッチのみ Logs Insights クエリによるフィルタ・フィールド加工
通知にログ行を含む 不可 ActionLogLineCount で最大50行
Logs Insights リンク なし 通知にクエリ結果の直リンクを含む
評価方式 EvaluationPeriods / DatapointsToAlarm QueryResultsToEvaluate / QueryResultsToAlarm
Chatbot 対応 ×(2026-06-30 時点)
コスト メトリクスフィルター: 無料 / カスタムメトリクス: $0.30/月 / アラーム: $0.10/月 アラーム: $0.10/月 + Logs Insights クエリ: $0.005/GB scanned

Log Based Alarm の仕組み

Log Based Alarmは「クエリ定期実行 → 結果集約 → 閾値評価」のパイプラインで動作します。

スケジュールドクエリ

PutLogAlarmを実行すると、CloudWatch LogsにScheduled Queryが自動作成されます。指定したスケジュール(例: rate(5 minutes))でLogs Insightsクエリが定期実行され、結果がアラーム評価に使われます。

クエリの対象時間範囲は StartTimeOffsetEndTimeOffset で制御します。

AggregationExpression

クエリ結果を単一の数値に集約する式です。count(*), sum(fieldName), avg(fieldName), min(fieldName), max(fieldName) のいずれかの関数を指定します。この集約結果が閾値と比較されます。

M-of-N 評価

QueryResultsToEvaluate(直近N回のクエリ結果を評価対象とする)と QueryResultsToAlarm(そのうちM回が閾値条件を満たすとALARM)で評価ロジックを制御します。

検証: アラーム作成

実際にPutLogAlarmでアラームを作成します。

IAM ロールの準備

Log Based Alarmにはスケジュールドクエリ実行用のIAMロールが必要です。logs.amazonaws.comcloudwatch.amazonaws.com の両方を信頼する必要があります。

aws iam create-role \
  --role-name "log-alarm-query-role" \
  --assume-role-policy-document '{
    "Version": "2012-10-17",
    "Statement": [
      {
        "Effect": "Allow",
        "Principal": {"Service": "logs.amazonaws.com"},
        "Action": "sts:AssumeRole"
      },
      {
        "Effect": "Allow",
        "Principal": {"Service": "cloudwatch.amazonaws.com"},
        "Action": "sts:AssumeRole"
      }
    ]
  }'

インラインポリシーで対象ロググループへのクエリ権限を付与します。

aws iam put-role-policy \
  --role-name "log-alarm-query-role" \
  --policy-name "LogAlarmQueryPermissions" \
  --policy-document '{
    "Version": "2012-10-17",
    "Statement": [
      {
        "Effect": "Allow",
        "Action": [
          "logs:StartQuery",
          "logs:GetQueryResults",
          "logs:StopQuery",
          "logs:FilterLogEvents",
          "logs:GetLogEvents"
        ],
        "Resource": "arn:aws:logs:ap-northeast-1:123456789012:log-group:/aws/ecs/your-app:*"
      }
    ]
  }'

PutLogAlarm の実行

aws cloudwatch put-log-alarm \
  --alarm-name "timeout-log-alarm" \
  --alarm-description "ECS timeout errors - 5min aggregation, threshold 20" \
  --scheduled-query-configuration '{
    "QueryString": "filter @message like /timeout request to/",
    "LogGroupIdentifiers": ["/aws/ecs/your-app"],
    "ScheduledQueryRoleARN": "arn:aws:iam::123456789012:role/log-alarm-query-role",
    "ScheduleConfiguration": {
      "ScheduleExpression": "rate(5 minutes)",
      "StartTimeOffset": 360,
      "EndTimeOffset": 60
    },
    "AggregationExpression": "count(*)"
  }' \
  --action-log-line-count 10 \
  --action-log-line-role-arn "arn:aws:iam::123456789012:role/log-alarm-query-role" \
  --actions-enabled \
  --alarm-actions "arn:aws:sns:ap-northeast-1:123456789012:your-alarm-topic" \
  --ok-actions "arn:aws:sns:ap-northeast-1:123456789012:your-alarm-topic" \
  --query-results-to-evaluate 1 \
  --query-results-to-alarm 1 \
  --threshold 20.0 \
  --comparison-operator "GreaterThanThreshold" \
  --treat-missing-data "notBreaching" \
  --region ap-northeast-1

主要なパラメータ:

  • QueryString: Logs Insightsクエリ。ログの絞り込みやフィールド定義を記述します(最終的な数値集約はAggregationExpressionで指定)
  • LogGroupIdentifiers: クエリ対象のロググループ
  • ScheduleExpression: クエリの実行間隔
  • StartTimeOffset / EndTimeOffset: 実行時刻からの相対秒数でクエリ対象時間範囲を指定。今回は 360 / 60 を指定しているため、実行時刻の6分前〜1分前の5分間が評価対象になります
  • AggregationExpression: クエリ結果の集約関数
  • ActionLogLineCount: アラーム通知に含めるログ行数(最大50)
  • QueryResultsToEvaluate / QueryResultsToAlarm: M-of-N評価のNとM

スケジュールドクエリの自動作成確認

作成後、describe-alarms でScheduled QueryのARNを確認できます。

aws cloudwatch describe-alarms \
  --alarm-names "timeout-log-alarm" \
  --region ap-northeast-1

:::detailsレスポンス(LogAlarmsセクション)

{
  "LogAlarms": [
    {
      "AlarmName": "timeout-log-alarm",
      "AlarmArn": "arn:aws:cloudwatch:ap-northeast-1:123456789012:alarm:timeout-log-alarm",
      "AlarmDescription": "ECS timeout errors - 5min aggregation, threshold 20",
      "ActionsEnabled": true,
      "OKActions": ["arn:aws:sns:ap-northeast-1:123456789012:your-alarm-topic"],
      "AlarmActions": ["arn:aws:sns:ap-northeast-1:123456789012:your-alarm-topic"],
      "InsufficientDataActions": [],
      "StateValue": "INSUFFICIENT_DATA",
      "ScheduledQueryConfiguration": {
        "QueryString": "filter @message like /timeout request to/",
        "LogGroupIdentifiers": ["/aws/ecs/your-app"],
        "QueryARN": "arn:aws:logs:ap-northeast-1:123456789012:scheduled-query:e026155f-ae01-4d43-bde5-024d4ea39f45",
        "ScheduledQueryRoleARN": "arn:aws:iam::123456789012:role/log-alarm-query-role",
        "ScheduleConfiguration": {
          "ScheduleExpression": "rate(5 minutes)",
          "StartTimeOffset": 360,
          "EndTimeOffset": 60
        },
        "AggregationExpression": "count(*)"
      },
      "QueryResultsToEvaluate": 1,
      "QueryResultsToAlarm": 1,
      "Threshold": 20.0,
      "ComparisonOperator": "GreaterThanThreshold",
      "TreatMissingData": "notBreaching",
      "ActionLogLineCount": 10,
      "ActionLogLineRoleArn": "arn:aws:iam::123456789012:role/log-alarm-query-role"
    }
  ]
}

:::

describe-alarms のレスポンスは従来の MetricAlarms とは別に、LogAlarms キーで返されます。

検証: アラーム発火と通知確認

スケジュールドクエリが実行されると、対象ロググループに対してLogs Insightsクエリが走ります。今回の検証では、対象ロググループに実際に存在するタイムアウトログが34件マッチしました。閾値20を超えたためALARMに遷移しています。

StateReason: "Threshold Crossed: 1 out of the last 1 query results [34.0 (30/06/26 11:43:54)]
was greater than the threshold (20.0)
(minimum 1 datapoint for OK -> ALARM transition)."

SNS 通知の内容

ALARM発火時のSNSメッセージには、従来のMetricAlarmにはない以下のフィールドが含まれます。

{
  "AlarmName": "timeout-log-alarm",
  "NewStateValue": "ALARM",
  "NewStateReason": "Threshold Crossed: 1 out of the last 1 query results [34.0 (30/06/26 11:43:54)] was greater than the threshold (20.0) ...",
  "LogGroups": ["/aws/ecs/your-app"],
  "QueryString": "filter @message like /timeout request to/",
  "AggregationExpression": "count(*)",
  "QueryExecutionId": "627920a2-af1e-4d79-a866-04488dbebd55"
}

従来のMetricAlarmメッセージにある Trigger フィールドは含まれず、代わりにLog Alarm固有のフィールドが追加されています。

ActionLogLineCount によるログ行の同梱

Email通知に含まれたログ行は以下です。

Log Lines:
2026-06-30 11:41:59.111 timeout request to https://example.cloudfront.net/spaces/.../entries?...
2026-06-30 11:38:44.108 timeout request to https://example.io/api/stats/user-30.json.gz
2026-06-30 11:38:44.008 timeout request to https://example.io/api/stats/user-29.json.gz
2026-06-30 11:38:43.908 timeout request to https://example.io/api/stats/user-28.json.gz
2026-06-30 11:38:43.808 timeout request to https://example.io/api/stats/user-27.json.gz
2026-06-30 11:38:43.708 timeout request to https://example.io/api/stats/user-26.json.gz
2026-06-30 11:38:43.608 timeout request to https://example.io/api/stats/user-25.json.gz
2026-06-30 11:38:43.508 timeout request to https://example.io/api/stats/user-24.json.gz
2026-06-30 11:38:43.408 timeout request to https://example.io/api/stats/user-23.json.gz
2026-06-30 11:38:43.308 timeout request to https://example.io/api/stats/user-22.json.gz

Email通知にはLogs Insightsクエリ結果への直リンクも含まれます。

View query results in Logs Insights:
https://ap-northeast-1.console.aws.amazon.com/cloudwatch/home?region=ap-northeast-1#logsV2:logs-insights$3FqueryId$3D627920a2-af1e-4d79-a866-04488dbebd55

検証: EventBridge 連携

Log Based Alarmの状態変更もEventBridgeでキャプチャできます。イベントパターンは通常のCloudWatch Alarmと同じ CloudWatch Alarm State Change です。

aws events put-rule \
  --name "log-alarm-state-change-capture" \
  --event-pattern '{
    "source": ["aws.cloudwatch"],
    "detail-type": ["CloudWatch Alarm State Change"],
    "resources": ["arn:aws:cloudwatch:ap-northeast-1:123456789012:alarm:timeout-log-alarm"]
  }' \
  --state ENABLED \
  --region ap-northeast-1

キャプチャしたイベントの構造は以下の通りです。

EventBridgeイベント(OK → ALARM遷移)
{
  "version": "0",
  "id": "a644916b-0f3e-7687-4a60-3e0b6b639f81",
  "detail-type": "CloudWatch Alarm State Change",
  "source": "aws.cloudwatch",
  "time": "2026-06-30T12:13:00Z",
  "region": "ap-northeast-1",
  "resources": [
    "arn:aws:cloudwatch:ap-northeast-1:123456789012:alarm:timeout-log-alarm"
  ],
  "detail": {
    "alarmName": "timeout-log-alarm",
    "state": {
      "value": "ALARM",
      "reason": "Threshold Crossed: 1 out of the last 1 query results [34.0] was greater than the threshold (20.0)...",
      "timestamp": "2026-06-30T12:13:00.795+0000"
    },
    "previousState": {
      "value": "OK",
      "reason": "...",
      "timestamp": "2026-06-30T12:12:51.478+0000"
    },
    "configuration": {
      "logGroupIdentifiers": ["/aws/ecs/your-app"],
      "queryString": "filter @message like /timeout request to/",
      "aggregationExpression": "count(*)",
      "scheduledQueryRoleARN": "arn:aws:iam::123456789012:role/log-alarm-query-role",
      "actionLogLineRoleArn": "arn:aws:iam::123456789012:role/log-alarm-query-role",
      "actionLogLineCount": 10,
      "schedule": {
        "expression": "rate(5 minutes)",
        "startTimeOffset": 360,
        "endTimeOffset": 60
      },
      "threshold": 20.0,
      "comparisonOperator": "GreaterThanThreshold",
      "treatMissingData": "notBreaching",
      "queryResultsToEvaluate": 1,
      "queryResultsToAlarm": 1
    }
  }
}

detail.configuration にLog Alarm固有のフィールド(logGroupIdentifiers, queryString, aggregationExpression, schedule 等)が含まれます。

応用: Logs Insights 関数との組み合わせ

Log Based Alarm の AggregationExpression は、QueryString 内で fieldsparse を使って定義した数値フィールドを sum(), avg(), min(), max() で参照できます。parse で文字列として抽出した値を数値集約する場合は toNumber() で変換します。2026年5〜6月に追加された Logs Insights の新関数(if, toNumber, isPrivateIP 等)と組み合わせると、単純なカウントだけでなく比率や重み付けスコアでのアラームが実現できます。

https://dev.classmethod.jp/articles/cloudwatch-logs-insights-new-commands-functions-2026/

https://dev.classmethod.jp/articles/cloudwatch-logs-insights-new-commands-functions-2026-june/

比率アラーム: タイムアウト率が閾値を超えたら発火

if 関数でマッチした行を 1、それ以外を 0 としてフラグ化し、AVG で比率を算出します。

aws cloudwatch put-log-alarm \
  --alarm-name "timeout-ratio-alarm" \
  --alarm-description "Timeout ratio exceeds 50% of all log lines" \
  --scheduled-query-configuration '{
    "QueryString": "fields if(@message like /timeout request to/, 1, 0) as is_timeout",
    "LogGroupIdentifiers": ["/aws/ecs/your-app"],
    "ScheduledQueryRoleARN": "arn:aws:iam::123456789012:role/log-alarm-query-role",
    "ScheduleConfiguration": {
      "ScheduleExpression": "rate(5 minutes)",
      "StartTimeOffset": 360,
      "EndTimeOffset": 60
    },
    "AggregationExpression": "avg(is_timeout)"
  }' \
  --actions-enabled \
  --query-results-to-evaluate 1 \
  --query-results-to-alarm 1 \
  --threshold 0.5 \
  --comparison-operator "GreaterThanThreshold" \
  --treat-missing-data "notBreaching" \
  --region ap-northeast-1

実際に作成したアラームの評価結果は以下でした。

StateReason: "Threshold Crossed: 1 out of the last 1 query results [0.03333333333333333 (30/06/26 12:58:25)]
was not greater than the threshold (0.5)
(minimum 1 datapoint for INSUFFICIENT_DATA -> OK transition)."

評価対象期間のクエリ結果行のうち、タイムアウトの比率が約3.3%と算出され、閾値50%を下回っているためOK状態です。avg(is_timeout) がクエリ結果の各行の is_timeout フィールド(0 or 1)を平均し、比率として評価していることが確認できました。

重み付けスコア: 重大度に応じたアラーム

case 関数(2026年5月追加)で多分岐のスコアリングができます。if のネストより可読性が高くなります。

aws cloudwatch put-log-alarm \
  --alarm-name "severity-score-alarm" \
  --alarm-description "Weighted severity score exceeds threshold" \
  --scheduled-query-configuration '{
    "QueryString": "fields case(@message like /timeout/, 2, @message like /error/, 1, 0) as severity_score",
    "LogGroupIdentifiers": ["/aws/ecs/your-app"],
    "ScheduledQueryRoleARN": "arn:aws:iam::123456789012:role/log-alarm-query-role",
    "ScheduleConfiguration": {
      "ScheduleExpression": "rate(5 minutes)",
      "StartTimeOffset": 360,
      "EndTimeOffset": 60
    },
    "AggregationExpression": "avg(severity_score)"
  }' \
  --actions-enabled \
  --query-results-to-evaluate 1 \
  --query-results-to-alarm 1 \
  --threshold 1.0 \
  --comparison-operator "GreaterThanThreshold" \
  --treat-missing-data "notBreaching" \
  --region ap-northeast-1

case(条件1, 値1, 条件2, 値2, ..., デフォルト値) の構文で、最大10分岐まで指定できます。

その他の組み合わせパターン

ユースケース QueryString AggregationExpression
レスポンスタイム平均 parse @message /took (?<ms>\d+)ms/ | fields toNumber(ms) as rt avg(rt)
遅いリクエストの比率 fields if(toNumber(response_time) > 1000, 1, 0) as is_slow avg(is_slow)
外部IPアクセス比率 fields if(isPrivateIP(src_ip), 0, 1) as is_external avg(is_external)
巨大ログ行の検知 fields if(strlen(@message) > 10000, 1, 0) as is_oversized sum(is_oversized)

ポイントは、QueryString で stats を使わず fields でフィールドを定義することです。stats で集約した結果フィールドは AggregationExpression から参照できません。

注意事項

AggregationExpression の制約

AggregationExpression には集約関数を直接指定します。使用可能な関数は count(*), sum(fieldName), avg(fieldName), min(fieldName), max(fieldName) です。

fieldsparse で定義したフィールドは参照可能ですが、QueryString 内で stats を使って集約したフィールドは参照できません。

# NG: QueryString に stats を含め、AggregationExpression でフィールド参照
QueryString: "filter @message like /error/ | stats count(*) as errorCount"
AggregationExpression: "errorCount"

# OK: QueryString はフィルタのみ、集約は AggregationExpression で
QueryString: "filter @message like /error/"
AggregationExpression: "count(*)"

# OK: fields で定義したフィールドを AggregationExpression で集約
QueryString: "fields if(@message like /error/, 1, 0) as is_error"
AggregationExpression: "avg(is_error)"

AWS Chatbot 未対応(2026-06-30 時点)

Log Based AlarmのSNSメッセージは従来のMetricAlarmと形式が異なるため(前述の通り Trigger フィールドがなく、Log Alarm固有のフィールドで構成)、AWS Chatbotが対応していない可能性があります。検証ではSNS Publishは成功しましたがSlackへの通知は表示されませんでした。

Chatbot経由でSlackに通知したい場合は、EventBridge → Lambdaでメッセージをカスタム通知フォーマットに加工してSNSにPublishする構成をお試しください。

https://dev.classmethod.jp/articles/sns-amazon-q-developer-aws-chatbot/

EventBridge ターゲットのリソースベースポリシー

EventBridgeのターゲットにCloudWatch Logsを指定する場合は、ロググループにリソースベースポリシーが必要です。events.amazonaws.com に対して logs:CreateLogStreamlogs:PutLogEvents を許可してください。

まとめ

Log Based Alarmにより、CloudWatch Logsの監視設定を大幅にシンプルにできるようになりました。メトリクスフィルターを作成せずに、Logs Insightsクエリによるフィルタやフィールド加工と AggregationExpression による集約を組み合わせて、ログ検索結果を直接アラーム条件として扱えます。

今回の検証では、単純なカウント閾値だけでなく、if / case / toNumber / isPrivateIP などの関数を使うことで、タイムアウト率、重大度スコア、レスポンスタイム平均といった比率や加重値ベースのアラームも表現できることを確認しました。従来はメトリクスフィルター、カスタムメトリクス、MetricAlarmを組み合わせたり、Lambdaで条件判定していた構成を、より少ない設定で実現できます。

通知面では、ActionLogLineCount によってマッチしたログ行を含められる点と、Logs Insightsクエリ結果への直リンクが含まれる点が便利です。EventBridge連携も従来のアラームと同じイベントパターンで利用できました。

現時点ではAWS Chatbotへの直接通知には制約があり、AggregationExpression の使い方にも注意点はありますが、ログ内容に基づくアラームをシンプルに作成したい場面では有力な選択肢になりそうです。

この記事をシェアする

AWSのお困り事はクラスメソッドへ

関連記事