Observability: CloudWatch & Logs
Verified against aws-cli/2.33.6, flags verified via `aws <cmd> help`, 2026-08-20 · official docs
CloudWatch metrics and alarms, and CloudWatch Logs — the commands for reading what's actually happening in a running system and reacting to it.
Reading metrics#
aws cloudwatch get-metric-statistics \ --namespace AWS/EC2 --metric-name CPUUtilization \ --dimensions Name=InstanceId,Value=i-0123456789abcdef0 \ --start-time 2026-08-19T00:00:00Z --end-time 2026-08-20T00:00:00Z \ --period 300 --statistics Average aws cloudwatch get-metric-data \ --metric-data-queries '[{"Id":"cpu","MetricStat":{"Metric":{"Namespace":"AWS/EC2","MetricName":"CPUUtilization","Dimensions":[{"Name":"InstanceId","Value":"i-0123456789abcdef0"}]},"Period":300,"Stat":"Average"},"ReturnData":true}]' \ --start-time 2026-08-19T00:00:00Z --end-time 2026-08-20T00:00:00Z
get-metric-statistics is the simple single-metric query; get-metric-data is the newer, batched form (query up to 500 metrics in one call, supports metric math) — reach for get-metric-data for anything beyond a quick one-off check.
Publishing custom metrics#
aws cloudwatch put-metric-data \ --namespace MyApp \ --metric-name QueueDepth \ --value 42 --unit Count \ --dimensions Environment=production
Alarms#
aws cloudwatch put-metric-alarm \ --alarm-name high-cpu \ --namespace AWS/EC2 --metric-name CPUUtilization \ --dimensions Name=InstanceId,Value=i-0123456789abcdef0 \ --statistic Average --period 300 --evaluation-periods 3 \ --threshold 80 --comparison-operator GreaterThanThreshold \ --alarm-actions arn:aws:sns:us-east-1:111122223333:my-alerts-topic aws cloudwatch describe-alarms --state-value ALARM # only alarms currently firing aws cloudwatch describe-alarms --alarm-name-prefix high- # filter by name prefix
Log groups and streams#
aws logs describe-log-groups --log-group-name-prefix /aws/lambda/ aws logs describe-log-streams --log-group-name /aws/lambda/my-function --order-by LastEventTime --descending aws logs put-retention-policy --log-group-name /aws/lambda/my-function --retention-in-days 30
Log groups default to never expiring unless you set a retention policy — a common, quietly expensive default left over from first-time Lambda/ECS setups. Worth auditing with describe-log-groups periodically.
Reading log events#
aws logs get-log-events --log-group-name /aws/lambda/my-function --log-stream-name <stream-name> --start-from-head aws logs filter-log-events --log-group-name /aws/lambda/my-function --filter-pattern "ERROR" --start-time 1755648000000 aws logs tail /aws/lambda/my-function --follow --since 1h --filter-pattern "ERROR"
aws logs tail is the closest thing to kubectl logs -f for CloudWatch — it's a CLI-only convenience command (not a direct API wrapper), it accepts human-readable --since values like 1h/30m, and --follow streams new events live instead of returning a fixed page.
filter-log-events searches across all streams in a log group at once; get-log-events reads one specific stream. Use filter-log-events when you don't already know which stream/task/container instance produced the log line you're looking for.