CloudWatch Logs Insights の新コマンド・関数を試してみた(parse logfmt、case、expand 他)
はじめに
2026年5月21日、CloudWatch Logs Insights に13個の新コマンド・関数が追加されました。全商用リージョンで利用可能です。
追加された機能の一覧は以下のとおりです。
| カテゴリ | コマンド/関数 | 用途 |
|---|---|---|
| 文字列・数値関数 | 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 による異常フィールドの特定も、日常的なログ調査を効率化してくれるはずです。
参考リンク
再現手順(テストデータ投入 + 主要クエリ・抜粋)
以下は主要機能の一部を試すための最小限のデータです。本文中の 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









