I tried out the new commands and functions in CloudWatch Logs Insights (parse logfmt, case, expand, and more)

I tried out the new commands and functions in CloudWatch Logs Insights (parse logfmt, case, expand, and more)

New features such as string manipulation, encoding/decoding, and logformat parsing have been added to the CloudWatch Logs Insights query language. We injected test logs and verified the behavior of the main functions and commands.
2026.05.22

This page has been translated by machine translation. View original

Introduction

On May 21, 2026, 13 new commands and functions were added to CloudWatch Logs Insights. They are available in all commercial regions.

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

The list of added features is as follows.

Category Command/Function Purpose
String/Numeric Functions round Rounding numbers
String/Numeric Functions startsWith Prefix match filter for strings
String/Numeric Functions endsWith Suffix match filter for strings
String/Numeric Functions case Conditional branching
String/Numeric Functions regex_replace Replacement using regular expressions
String/Numeric Functions haversine Distance calculation between coordinates
Encode/Decode urlencode URL encoding
Encode/Decode urldecode URL decoding
Encode/Decode base64encode Base64 encoding
Encode/Decode base64decode Base64 decoding
Parse/Analysis Commands parse logfmt Parse logfmt-formatted logs into fields
Parse/Analysis Commands expand Expand array fields into multiple records
Parse/Analysis Commands relevantfields Automatically display related fields

This article covers 11 of the features in practice, excluding urlencode / base64encode. These two were not tested hands-on this time and are only introduced in the list.

Verification Environment

A log group /test/insights-new-features was created in ap-northeast-1, and 115 test data entries were loaded for verification. The steps to reproduce are described in the collapsible section at the end of the article.

parse logfmt

It is now possible to directly parse structured logs in logfmt format.

Original log:

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

Query:

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

Result:

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

The syntax is parse @message logfmt as lf. Use as to assign an alias, and access each field using dot notation like lf.key. Note that writing it as parse logfmt @message will result in a syntax error.

startsWith / endsWith

These are functions that filter by prefix match or suffix match on strings. The same result can be achieved with the traditional like operator (regular expressions), but startsWith has the advantage of not requiring escaping and being more readable.

Written with like:

filter path like /^\/api\//

Written with 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

The return value of startsWith / endsWith is not a boolean but a numeric value (1 or 0). It works fine within filter since it is implicitly evaluated as a boolean, but when used with fields, it returns 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

This is string replacement using regular expressions. It uses RE2 syntax.

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

The unit string was removed to extract only the numeric portion.

base64decode / urldecode

Encoded values can be decoded inline directly in the query.

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

As shown in the startsWith section, URL-encoded parameters can be decoded inline with urldecode(path).

case

This is a conditional branching function. The syntax is case(condition1, value1, condition2, value2, ..., default_value), which evaluates conditions from top to bottom and returns the value of the first condition that is true. If no condition matches, the default value at the end is returned.

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

Result (excerpt):

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

The 'normal' at the end is the default value with no condition. The default value is correctly returned even for levels like INFO and DEBUG that are not explicitly specified as conditions.

round + haversine

round is a function for rounding numbers, and haversine is a function that calculates the distance (in km) between two coordinates. The argument order is haversine(latitude1, longitude1, latitude2, longitude2).

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

The values were reasonable: geo-service (Tokyo → Osaka) at approximately 403 km, and cdn-edge (New York → London) at approximately 5,570 km.

round can be used to round to a specified number of decimal places.

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

relevantfields

This is a command that automatically identifies fields highly correlated with a specific condition. It can be used for initial investigation to find fields that are characteristic when errors occur.

It was executed against 100 test data entries (designed so that ERROR logs are concentrated on specific paths / services).

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)

The score for path is high, indicating that errors are concentrated on /api/auth. Specifically, the occurrence rate of /api/auth under ERROR conditions is 75% (compared to 14% under normal conditions), and this difference (frequencyChange) is reflected in the score. It is also possible to omit the field list and analyze all fields with the where condition, but when you have a hypothesis, specifying fields is more efficient.

Note that in this verification, using relevantfields alone (without a where clause) resulted in a syntax error, so it was executed in the form relevantfields <fields> where <condition>.

expand

This is a command that expands array fields into multiple records.

Original log:

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

Query:

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

Result:

raw_items user
apple alice
banana alice
cherry alice

It was expanded into 3 records. The key point is that expand is applied after extracting the array portion as a string using the glob pattern in parse.

In this verification, expand could be applied to fields that have a string representation of a JSON array. The documentation states that it operates on array-type fields, but it did not expand as expected with list types generated by jsonParse, and operation was confirmed only when extracting as a string using glob/regex in parse. Even with fields that were flattened by automatic JSON parsing (expanded with indexes like items.0, items.1), expansion did not work as expected in this verification.

Notes

  • After ingesting logs, there may be an ingestion delay of 2–3 minutes before the results are reflected in Insights queries
  • regex_replace uses RE2 syntax. Regular expression features not supported by RE2, such as backreferences and lookarounds, cannot be used

Summary

This update increases the amount of preprocessing that can be done within Logs Insights, reducing the need to export logs externally for processing. In particular, parse logfmt will be frequently used for application log analysis, and case-based conditional branching enhances the expressiveness of aggregation queries. Field processing with regex_replace and anomalous field identification with relevantfields should also help streamline day-to-day log investigations.

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

Reproduction Steps (Test Data Ingestion + Key Queries, Excerpt)

The following is the minimum data needed to try some of the key features. Additional records are needed to reproduce the results for case, round, haversine including cdn-edge, and relevantfields described in the main text.

The following commands are intended to be run in AWS CloudShell or a GNU coreutils environment.

Create Log Group and Stream

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

Ingest Test Data

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

Query Execution Example (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

Query Execution Example (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

Cleanup

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

Share this article

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