CloudWatch Logs

List Log Groups

aws logs describe-log-groups | jq -r '.logGroups[].logGroupName'
/aws/lambda/user-signup
/aws/lambda/order-processor
/aws/events/billing-events

Filter Log Groups by Prefix

aws logs describe-log-groups --log-group-name-prefix "/aws/lambda/" | jq -r '.logGroups[].logGroupName'

List Log Streams (Newest First)

aws logs describe-log-streams \
    --log-group-name "/aws/lambda/user-signup" \
    --order-by LastEventTime \
    --descending \
    --max-items 5 \
    | jq -r '.logStreams[] | .logStreamName + " " + (.lastEventTimestamp|tostring)'
2026/04/13/[$LATEST]41b02a0430a74f8d9717f7921361e046 1744531200000
2026/04/12/[$LATEST]c049d71f34cd45a081d544840e6bed3b 1744444800000

Get Latest Log Stream Name

aws logs describe-log-streams \
    --log-group-name "/aws/lambda/user-signup" \
    --order-by LastEventTime \
    --descending \
    --max-items 1 \
    --query 'logStreams[0].logStreamName' \
    --output text

Tail Logs in Real Time

aws logs tail /aws/lambda/user-signup --follow

Tail Logs for Last N Minutes

aws logs tail /aws/lambda/user-signup --since 10m
aws logs tail /aws/lambda/user-signup --since 1h --follow

Get Events from a Log Stream

aws logs get-log-events \
    --log-group-name "/aws/lambda/user-signup" \
    --log-stream-name "2026/04/13/[\$LATEST]41b02a0430a74f8d9717f7921361e046" \
    --start-from-head \
    --query 'events[*].message' \
    --output text

Filter Log Events by Pattern

aws logs filter-log-events \
    --log-group-name "/aws/lambda/user-signup" \
    --filter-pattern "ERROR" \
    --query 'events[*].message' \
    --output text

Filter Log Events in a Time Range

💡 CloudWatch Logs expects --start-time and --end-time in epoch milliseconds. Compute them on the fly:

# last 10 minutes (macOS and Linux)
aws logs filter-log-events \
    --log-group-name "/aws/lambda/user-signup" \
    --filter-pattern "ERROR" \
    --start-time $(python3 -c "import time; print(int((time.time() - 600) * 1000))") \
    --query 'events[*].message' \
    --output text
# specific ISO timestamp window
aws logs filter-log-events \
    --log-group-name "/aws/lambda/user-signup" \
    --start-time $(python3 -c "from datetime import datetime; print(int(datetime.fromisoformat('2026-04-13T10:00:00').timestamp() * 1000))") \
    --end-time   $(python3 -c "from datetime import datetime; print(int(datetime.fromisoformat('2026-04-13T11:00:00').timestamp() * 1000))") \
    --query 'events[*].[timestamp,message]' \
    --output table

Start a CloudWatch Logs Insights Query

💡 start-query uses epoch seconds (not ms).

aws logs start-query \
    --log-group-name "aws-cloudtrail-logs-987654321-c59299d1" \
    --start-time $(date -u -v-60M +%s) \
    --end-time   $(date -u +%s) \
    --query-string 'fields @timestamp, eventName, errorCode, errorMessage, userIdentity.arn, sourceIPAddress
                    | filter (errorCode like "UnauthorizedOperation" or errorCode like "AccessDenied")
                    | sort @timestamp desc
                    | limit 50'
{
    "queryId": "32e95c24-c554-4a7f-a1fb-d95ae91e6556"
}

Fetch Insights Query Results

aws logs get-query-results --query-id "32e95c24-c554-4a7f-a1fb-d95ae91e6556"
# just the status
aws logs get-query-results --query-id "32e95c24-c554-4a7f-a1fb-d95ae91e6556" \
    --query 'status' --output text
# flatten field/value pairs
aws logs get-query-results --query-id "32e95c24-c554-4a7f-a1fb-d95ae91e6556" \
    --query 'results[*][*].[field,value]' --output text

Delete a Log Group

aws logs delete-log-group --log-group-name "/aws/lambda/user-signup"

Set Log Group Retention

aws logs put-retention-policy \
    --log-group-name "/aws/lambda/user-signup" \
    --retention-in-days 14