Amazon CloudWatch
Monitoring, logging, and observability for your applications
šµ Cost: CloudWatch has partial Free Tier (10 custom metrics, 10 alarms, 5GB logs ingestion/month for 12 months). Log ingestion costs $0.50/GB - high-traffic apps can incur significant costs. Set retention policies (7-30 days) to control storage costs. See our Costs & Cleanup Guide for cleanup steps.
What is CloudWatch?
Amazon CloudWatch is the central monitoring service for AWS. It collects logs, metrics, and events from your applications and infrastructure, letting you visualize performance, set alarms, and troubleshoot issues.
Essential for Production
CloudWatch Components
| Component | Purpose | Example |
|---|---|---|
| Logs | Store and search application logs | console.log output, errors |
| Metrics | Numerical data points over time | CPU usage, request count |
| Alarms | Trigger actions on metric thresholds | Alert when CPU exceeds 80% |
| Dashboards | Visualize metrics in graphs | Real-time performance view |
| Events/EventBridge | React to state changes | ECS task stopped notification |
CloudWatch Logs Structure
CloudWatch Logs
āāā Log Group: /ecs/my-app-frontend
ā āāā Log Stream: ecs/frontend/abc123
ā ā āāā Log Event: "Server started on port 3000"
ā ā āāā Log Event: "GET /api/health 200 5ms"
ā ā āāā Log Event: "Error: Connection refused..."
ā ā
ā āāā Log Stream: ecs/frontend/def456
ā āāā Log Event: "..."
ā
āāā Log Group: /ecs/my-app-worker
āāā Log Stream: ecs/worker/ghi789ECS Fargate automatically sends container stdout/stderr to CloudWatch. Just ensure your task definition includes the log configuration:
{
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/my-app",
"awslogs-region": "ap-southeast-1",
"awslogs-stream-prefix": "ecs",
"awslogs-create-group": "true"
}
}
}View Logs via CLI
2024-01-15T10:30:00 Server started on port 3000 2024-01-15T10:30:01 Connected to database 2024-01-15T10:30:05 GET /api/health 200 2ms 2024-01-15T10:30:10 GET /api/users 200 45ms
CloudWatch Logs Insights lets you query logs using a SQL-like syntax:
# Find all errors in the last hour
fields @timestamp, @message
| filter @message like /ERROR/
| sort @timestamp desc
| limit 100
# Count requests by status code
fields @message
| parse @message "* * * *" as method, path, status, duration
| stats count() by status
# Find slow requests (over 1 second)
fields @timestamp, @message
| parse @message "* * * *ms" as method, path, status, duration
| filter duration > 1000
| sort duration descimport {
CloudWatchLogsClient,
PutLogEventsCommand,
CreateLogStreamCommand
} from "@aws-sdk/client-cloudwatch-logs"
const client = new CloudWatchLogsClient({
region: process.env.AWS_REGION
})
const LOG_GROUP = process.env.CLOUDWATCH_LOG_GROUP || "/app/custom-logs"
export async function logEvent(
streamName: string,
message: string,
level: "INFO" | "WARN" | "ERROR" = "INFO"
) {
const logEntry = JSON.stringify({
level,
message,
timestamp: new Date().toISOString(),
service: "my-app"
})
const command = new PutLogEventsCommand({
logGroupName: LOG_GROUP,
logStreamName: streamName,
logEvents: [
{
timestamp: Date.now(),
message: logEntry
}
]
})
await client.send(command)
}
// Usage
await logEvent("api-requests", "User signed up", "INFO")
await logEvent("errors", "Payment failed: insufficient funds", "ERROR")Try the Demo
CloudWatch Pricing
Tiered & Region-Specific Pricing
| Component | Cost |
|---|---|
| Log ingestion | $0.50 per GB |
| Log storage | $0.03 per GB/month |
| Logs Insights queries | $0.005 per GB scanned |
| Metrics (first 10 custom) | Free |
| Alarms (first 10) | Free |
Log Volume Warning