CloudWatch Logs Insights の新コマンド・関数を試してみた(parse logfmt、case、expand 他)

CloudWatch Logs Insights の新コマンド・関数を試してみた(parse logfmt、case、expand 他)

CloudWatch Logs Insights のクエリ言語に、文字列操作、エンコード/デコード、logfmt パースなどの新機能が追加されました。テストログを投入して主要な関数・コマンドの動作を確認しました。
2026.05.22

はじめに

2026年5月21日、CloudWatch Logs Insights に13個の新コマンド・関数が追加されました。全商用リージョンで利用可能です。

https://aws.amazon.com/jp/about-aws/whats-new/2026/06/amazon-cloudwatch-logs-insights/

追加された機能の一覧は以下のとおりです。

カテゴリ コマンド/関数 用途
文字列・数値関数 round 数値の丸め
文字列・数値関数 startsWith 文字列の前方一致フィルタ
文字列・数値関数 endsWith 文字列の後方一致フィルタ
文字列・数値関数 case 条件分岐
文字列・数値関数 regex_replace 正規表現による置換
文字列・数値関数 haversine 座標間の距離計算
エンコード/デコード urlencode URLエンコード
エンコード/デコード urldecode URLデコード
エンコード/デコード base64encode Base64エンコード
エンコード/デコード base64decode Base64デコード
パース・分析コマンド parse logfmt logfmt形式のログをフィールドにパース
パース・分析コマンド expand 配列フィールドを複数レコードに展開
パース・分析コマンド relevantfields 関連フィールドを自動表示

この記事では urlencode / base64encode を除く11機能を実際に試しています。この2つは今回は実機確認の対象外とし、一覧紹介に留めました。

検証環境

ap-northeast-1 にロググループ /test/insights-new-features を作成し、テストデータ計115件を投入して検証しました。再現手順は記事末尾の折りたたみに記載しています。

parse logfmt

logfmt 形式の構造化ログを直接パースできるようになりました。

元ログ:

level=info service=payment-service method=POST path=/api/payment duration=234ms status=200 user_id=usr_12345

クエリ:

parse @message logfmt as lf
| filter lf.service = 'payment-service'
| display lf.service, lf.method, lf.path, lf.duration, lf.status, lf.user_id

結果:

lf.service lf.method lf.path lf.duration lf.status lf.user_id
payment-service POST /api/payment 234ms 200 usr_12345

構文は parse @message logfmt as lf です。as で別名を付け、lf.key のドット記法で各フィールドにアクセスします。parse logfmt @message のように書くと構文エラーになるため注意してください。

startsWith / endsWith

文字列の前方一致・後方一致でフィルタできる関数です。従来の like 演算子(正規表現)でも同じ結果を得られますが、startsWith はエスケープ不要で可読性が高いのが利点です。

like で書く場合:

filter path like /^\/api\//

startsWith で書く場合:

filter startsWith(path, '/api/')
filter startsWith(path, '/api/')
| display path, urldecode(path)
path urldecode(path)
/api/users?name=%E7%94%B0%E4%B8%AD&page=1 /api/users?name=田中&page=1

startsWith / endsWith の返り値は boolean ではなく 数値(1 または 0) です。filter 内では暗黙的にブール判定されるため問題なく動作しますが、fields で使った場合は 1/0 が返ります。

fields path, startsWith(path, '/api/') as starts_api, endsWith(path, 'users') as ends_users
path starts_api ends_users
/api/users 1 1
/api/auth 1 0

regex_replace

正規表現による文字列置換です。RE2 構文を使用します。

parse @message logfmt as lf
| filter ispresent(lf.path) and endsWith(lf.path, 'payment')
| display lf.path, regex_replace(lf.duration, 'ms$', '') as duration_num
lf.path duration_num
/api/payment 234

単位文字列を除去し、数値部分だけを取り出しました。

base64decode / urldecode

エンコード済みの値をクエリ上でインラインにデコードできます。

base64decode

filter ispresent(payload_b64)
| display service, payload_b64, base64decode(payload_b64) as decoded
service payload_b64 decoded
data-pipeline SGVsbG8gV29ybGQgZnJvbSBDbG91ZFdhdGNoIExvZ3M= Hello World from CloudWatch Logs

urldecode

startsWith セクションで示したとおり、urldecode(path) でURLエンコード済みパラメータをインラインにデコードできます。

case

条件分岐関数です。構文は case(条件1, 値1, 条件2, 値2, ..., デフォルト値) で、条件を上から評価し最初に真になった値を返します。どの条件にも一致しない場合は末尾のデフォルト値が返されます。

filter ispresent(level)
| fields service, level,
  case(
    level = 'ERROR', 'critical',
    level = 'WARN', 'warning',
    'normal'
  ) as severity

結果(抜粋):

service level severity
auth-service ERROR critical
cdn-edge WARN warning
api-gateway INFO normal
geo-service INFO normal
data-pipeline DEBUG normal

末尾の 'normal' が条件なしのデフォルト値です。INFO や DEBUG など明示的に条件を書いていない level に対しても正しくデフォルト値が返されています。

round + haversine

round は数値の丸め、haversine は2つの座標間の距離(km)を計算する関数です。引数の順序は haversine(緯度1, 経度1, 緯度2, 経度2) です。

filter service = 'geo-service' or service = 'cdn-edge'
| display service, haversine(origin_lat, origin_lon, dest_lat, dest_lon) as distance_km
service distance_km
geo-service 402.7842
cdn-edge 5570.2222

geo-service(東京→大阪)が約403km、cdn-edge(ニューヨーク→ロンドン)が約5,570km と妥当な値でした。

round は小数点以下の桁数を指定して丸められます。

filter ispresent(response_time_ms)
| fields response_time_ms, round(response_time_ms, 0) as rounded
response_time_ms rounded
1523.7 1524

relevantfields

特定条件と相関の高いフィールドを自動的に特定するコマンドです。エラー発生時に特徴的なフィールドを探す初動調査に使えます。

100件のテストデータ(ERROR ログが特定の path / service に偏るよう設計)に対して実行しました。

relevantfields service, path where level = 'ERROR'
@fieldName @relevanceScore @topRelevanceContributors
path 0.404 /api/auth (frequencyChange: 0.61)
service 0.002 auth-service (frequencyChange: 0.67)

path のスコアが高く、ERROR 時に /api/auth に集中していることが読み取れます。具体的には、ERROR 条件下で /api/auth の出現率が 75%(通常時は 14%)と大きく偏っており、この差(frequencyChange)がスコアに反映されています。where 条件を付けたうえで、フィールドリストを省略して全フィールドを分析することも可能ですが、仮説がある場合はフィールド指定版のほうが効率的です。

なお、今回の検証では relevantfields 単独(where 句なし)では構文エラーになったため、relevantfields <フィールド> where <条件> の形で実行しました。

expand

配列フィールドを複数レコードに展開するコマンドです。

元ログ:

event_type=order items=["apple","banana","cherry"] user=alice

クエリ:

filter @message like /order/
| parse @message 'items=* user=*' as raw_items, user
| expand raw_items
| display raw_items, user

結果:

raw_items user
apple alice
banana alice
cherry alice

3レコードに展開されました。ポイントは parse の glob パターンで配列部分を文字列として切り出してから expand を適用している点です。

今回の検証では、JSON 配列の文字列表現を持つフィールドに対して expand を適用できました。ドキュメントには配列型フィールドに対して動作する旨が記載されていますが、jsonParse で生成した list 型では期待どおりに展開されず、parse の glob/regex で文字列として切り出した場合のみ動作を確認できました。自動 JSON パースでフラット化されたフィールド(items.0, items.1 のようにインデックス付きで展開された状態)でも、今回の検証では期待どおりに展開できませんでした。

注意事項

  • ログ投入後、Insights でクエリ結果に反映されるまで2〜3分のインジェスト遅延がある場合があります
  • regex_replace は RE2 構文です。後方参照やルックアラウンドなど、RE2 がサポートしない正規表現機能は利用できません

まとめ

今回のアップデートにより、Logs Insights 上でできる前処理が増え、ログを外部に出して加工する場面を減らせるようになりました。特に parse logfmt はアプリケーションログの解析で出番が多く、case による条件分岐は集計クエリの表現力を高めてくれます。regex_replace でのフィールド加工や relevantfields による異常フィールドの特定も、日常的なログ調査を効率化してくれるはずです。

参考リンク

https://aws.amazon.com/jp/about-aws/whats-new/2026/06/amazon-cloudwatch-logs-insights/

https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/CWL_AnalyzeLogData_LogsInsights.html

再現手順(テストデータ投入 + 主要クエリ・抜粋)

以下は主要機能の一部を試すための最小限のデータです。本文中の case、round、cdn-edge を含む haversine、relevantfields の結果を再現するには追加レコードが必要です。

以下のコマンドは AWS CloudShell または GNU coreutils 環境での実行を想定しています。

ロググループ・ストリーム作成

aws logs create-log-group --log-group-name /test/insights-new-features --region ap-northeast-1
aws logs create-log-stream --log-group-name /test/insights-new-features --log-stream-name test-stream-1 --region ap-northeast-1

テストデータ投入

NOW_MS=$(date +%s%3N)

cat << EOF > /tmp/log_events.json
[
  {"timestamp": $((NOW_MS - 5000)), "message": "{\\"level\\":\\"INFO\\",\\"service\\":\\"api-gateway\\",\\"path\\":\\"/api/users?name=%E7%94%B0%E4%B8%AD&page=1\\",\\"status\\":200,\\"lat\\":35.6812,\\"lon\\":139.7671}"},
  {"timestamp": $((NOW_MS - 4000)), "message": "level=info service=payment-service method=POST path=/api/payment duration=234ms status=200 user_id=usr_12345"},
  {"timestamp": $((NOW_MS - 3000)), "message": "{\\"level\\":\\"DEBUG\\",\\"service\\":\\"data-pipeline\\",\\"payload_b64\\":\\"SGVsbG8gV29ybGQgZnJvbSBDbG91ZFdhdGNoIExvZ3M=\\",\\"tags\\":[\\"prod\\",\\"critical\\",\\"us-east-1\\"]}"},
  {"timestamp": $((NOW_MS - 2000)), "message": "{\\"level\\":\\"INFO\\",\\"service\\":\\"geo-service\\",\\"origin_lat\\":35.6812,\\"origin_lon\\":139.7671,\\"dest_lat\\":34.6937,\\"dest_lon\\":135.5023}"},
  {"timestamp": $((NOW_MS - 1000)), "message": "event_type=order items=[\\"apple\\",\\"banana\\",\\"cherry\\"] user=alice"}
]
EOF

aws logs put-log-events \
  --log-group-name /test/insights-new-features \
  --log-stream-name test-stream-1 \
  --log-events file:///tmp/log_events.json \
  --region ap-northeast-1

クエリ実行例(parse logfmt)

START_TIME=$(( $(date +%s) - 300 ))
END_TIME=$(( $(date +%s) + 60 ))

QUERY_ID=$(aws logs start-query \
  --log-group-name /test/insights-new-features \
  --start-time $START_TIME --end-time $END_TIME \
  --query-string 'parse @message logfmt as lf | filter lf.service = "payment-service" | display lf.service, lf.method, lf.path, lf.duration' \
  --region ap-northeast-1 \
  --query queryId --output text)

sleep 5
aws logs get-query-results --query-id $QUERY_ID --region ap-northeast-1

クエリ実行例(expand)

QUERY_ID=$(aws logs start-query \
  --log-group-name /test/insights-new-features \
  --start-time $START_TIME --end-time $END_TIME \
  --query-string 'filter @message like /order/ | parse @message "items=* user=*" as raw_items, user | expand raw_items | display raw_items, user' \
  --region ap-northeast-1 \
  --query queryId --output text)

sleep 5
aws logs get-query-results --query-id $QUERY_ID --region ap-northeast-1

クリーンアップ

aws logs delete-log-group --log-group-name /test/insights-new-features --region ap-northeast-1

この記事をシェアする

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

関連記事