โšก Onwuachi Control Plane

CloudWatch Logs and Metric Filters

Overview

CloudWatch Logs is a managed log aggregation service. It stores log events in Log Groups and Log Streams, applies retention policies, and most importantly โ€” converts log patterns into CloudWatch metrics via Metric Filters. This is the bridge between raw log data and actionable alerts.

Why It Matters

Logs contain signal that metrics don’t capture โ€” specific error messages, user IDs, request paths, stack traces. Metric Filters extract that signal into quantifiable metrics without requiring a separate log analytics platform. Combined with CloudWatch Alarms, they enable alerting on application-level behavior from raw log data.

Where It Fits

DOP-C02 Domain 4 โ€” Monitoring and Logging

Application log line (“503 GET /api/health”) | v CloudWatch Log Stream (instance-id) | v CloudWatch Log Group (/platform/haproxy) | v Metric Filter (pattern: “503” โ†’ HAProxy5xxErrors +1) | v CloudWatch Metric (DopLab/HAProxy5xxErrors) | v CloudWatch Alarm โ†’ SNS โ†’ email


The Big Picture

Your platform today AWS Equivalent

/var/log/haproxy.log โ†’ CloudWatch Log Stream HAProxy server logs โ†’ Log Group (/platform/haproxy) grep “503” logs | wc -l โ†’ Metric Filter โ†’ custom metric Prometheus alert rule โ†’ CloudWatch Alarm on that metric


Core Concepts

Log Group โ€” the container for related log streams. Has a retention policy. One per application/service.

Log Stream โ€” one per source: one per EC2 instance, one per Lambda invocation, one per container. Named by instance ID for EC2.

Log Events โ€” individual log lines with millisecond timestamps.

Retention policies โ€” how long logs are kept:

1 day, 3 days, 7 days, 14 days, 30 days, 60 days, 90 days, 120 days, 150 days, 180 days, 1 year, 2 years, 5 years, 10 years, Never expire

Default is Never Expire โ€” always set a retention policy to control cost.

Metric Filters โ€” the exam-critical feature: Converts log patterns to CloudWatch metrics:

Pattern โ†’ Metric increment “503” โ†’ +1 to HAProxy5xxErrors “ERROR” โ†’ +1 to ApplicationErrors “FATAL” โ†’ +1 to FatalErrors

Filter pattern syntax:

“ERROR” โ€” contains the word ERROR “ERROR” - “TIMEOUT” โ€” contains ERROR but not TIMEOUT “[ip, id, status=5*]” โ€” structured: status starts with 5 { $.level = “ERROR” } โ€” JSON logs: field matching { $.httpStatusCode = 5* } โ€” JSON: status code starts with 5

defaultValue on metric transformation:

Use defaultValue: 0 for error rate metrics so treat-missing-data: notBreaching works correctly.


Real-World Example

Live lab in devopslab, account 046685909731:

  1. Created log group /dop-lab/haproxy with 7-day retention
  2. Created log stream test-instance-001
  3. Pushed 16 fake HAProxy 503 log events across three batches using put-log-events
  4. Created metric filter haproxy-503-errors โ€” pattern "503", namespace DopLab, metric HAProxy5xxErrors, defaultValue 0
  5. Queried metric after ~3 minutes โ€” confirmed Sum: 15.0 in GetMetricStatistics
  6. Deleted log group after lab

Key observation: Metric Filter propagation takes 2-3 minutes after log events are ingested. Query with a wide enough time window and period.

Lab runbook:

# Create log group with retention
aws logs create-log-group --log-group-name /dop-lab/haproxy
aws logs put-retention-policy --log-group-name /dop-lab/haproxy --retention-in-days 7

# Create log stream
aws logs create-log-stream \
  --log-group-name /dop-lab/haproxy \
  --log-stream-name test-instance-001

# Push log event
aws logs put-log-events \
  --log-group-name /dop-lab/haproxy \
  --log-stream-name test-instance-001\
  --log-events "[{\"timestamp\": $(date +%s%3N), \"message\": \"haproxy 503 GET /api/health\"}]"

# Create metric filter
aws logs put-metric-filter \
  --log-group-name /dop-lab/haproxy \
  --filter-name haproxy-503-errors \
  --filter-pattern "503" \
  --metric-transformations \
    metricName=HAProxy5xxErrors,metricNamespace=DopLab,metricValue=1,defaultValue=0

# Query metric (wait 2-3 min)
aws cloudwatch get-metric-statistics \
  --namespace DopLab \
  --metric-name HAProxy5xxErrors \
  --start-time $(date -u -d '15 minutes ago' +%Y-%m-%dT%H:%M:%SZ) \
  --end-time $(date -u +%Y-%m-%dT%H:%M:%SZ) \
  --period 900 \
  --statistics Sum \
  --output table

# Cleanup
aws logs delete-log-group --log-group-name /dop-lab/haproxy

Engineering Analogy

Metric Filters are the equivalent of grep "503" /var/log/haproxy.log | wc -l run continuously against a streaming log, publishing the count as a metric every minute. The difference: it’s serverless, always-on, and feeds directly into CloudWatch Alarms without any cron job or script.


Best Practices


Common Mistakes


Pro Tip

aws logs filter-log-events --log-group-name <group> --filter-pattern "503" lets you search log events directly via CLI without a metric filter โ€” useful for ad-hoc incident investigation. It’s the CLI equivalent of CloudWatch Logs Insights for simple pattern searches.


Key Takeaways


Related Articles


References

System Context

โ† Back to Kb