19.2
CloudWatch Logs, Agents and Subscriptions
Log groups and streams, the unified agent, Logs Insights, export vs subscriptions, cross-account aggregation and Live Tail.
26 min Reading + animations
Tip: tap or hover any dotted-underlined term to see what it means.
By the end of this lesson you will be able to
- Explain how CloudWatch Logs stores logs: log events, log streams and log groups, and which settings live on the group (retention, KMS key, log class, access).
- Name the sources that send logs to CloudWatch Logs, and explain why nothing leaves an EC2 instance until an agent sends it.
- Turn log lines into a metric with a metric filter, and build the exam’s chain logs → metric filter → metric → alarm → SNS.
- Query stored logs with Logs Insights (and know its three query languages and its price), and watch new logs as they arrive with Live Tail.
- Choose between an export task to S3 (batch, up to 12 hours behind) and subscription filters (real time) to Kinesis Data Streams, Data Firehose, Lambda or OpenSearch.
- Collect the logs of many accounts and Regions in one log-archive account with destinations, access policies and IAM roles.
- Choose the unified CloudWatch agent over the old CloudWatch Logs agent, and give it credentials on EC2 and on premises.
#Why CloudWatch Logs: every log in one place
Lesson 19.1 was about numbers: CPU, latency, request counts. Numbers tell you that something is wrong. To learn why, you read the logs: the stack trace, the failed SQL statement, the IP that hammered the login page. This lesson is about where those logs live on AWS.
#The problem
A Dhaka news portal runs twelve web servers on EC2, a few Lambda functions that resize photos, an API Gateway for its mobile app, and an old printing system on a server in a Tejgaon data centre. Every one of them writes logs, each to its own disk. On election night the home page slows down. To find the cause, an engineer would have to log into twelve servers, read twelve files, and the servers that Auto Scaling already terminated took their logs with them.
Amazon CloudWatch Logs solves this: it collects the logs of applications, operating systems and AWS services (VPC Flow Logs, Route 53, CloudTrail and many more) in one regional service, keeps them as long as you choose, and lets you search them, turn them into metrics, alert on them and send them elsewhere.
#Events, streams and groups
CloudWatch Logs has a three-level structure:
| Level | What it is | Example |
|---|---|---|
| Log event | One record: a message plus a timestamp. Usually one line of a log file. Up to 1 MB (1,024 KB). | 2026-11-01T09:00:03Z /sports 200 41ms 198.51.100.23 |
| Log stream | The sequence of events from one source, in time order. | All lines from instance i-0a1b2c3d, one container, or one Lambda execution environment |
| Log group | A container of streams that share the same settings: retention, encryption, access control, log class, filters. Usually one per application or component. | /news/app, /aws/lambda/resize-photo |
A group can hold any number of streams, and the streams are usually created automatically by whatever writes to them (the agent, Lambda, ECS).
Log events, streams and a log group
A Dhaka news portal runs two web servers. Watch their lines become log events, each server get its own stream, and the group hold the settings: retention, encryption key and access.
Scenario
Event = one line with a timestamp. Stream = the events of one source, in time order. Group = the folder of streams where the settings live.
Log event
timestamp
2026-11-01T09:00:03Z
message
/sports 200 41ms 198.51.100.23
A reader opens the sports page. The agent on web-1 reads the new line in the access log and sends it as a log event: a message plus a timestamp. That single line is the smallest unit of CloudWatch Logs.
#Settings on the group
Retention. By default a log group keeps its events forever (Never Expire). You can set a retention period of 1 day up to 10 years, in fixed steps: 1, 3, 5, 7, 14, 30, 60, 90, 120, 150, 180, 365, 400, 545, 731, 1,096, 1,827, 2,192, 2,557, 2,922, 3,288 or 3,653 days. Events older than the retention are marked for deletion (they stop being billed at once; the delete itself can take up to 72 hours) and cannot be recovered. A common design: short retention in CloudWatch Logs for searching, long-term copies in S3 with lifecycle rules (lesson 10.5).
Encryption. Log data is always encrypted at rest, by default with 256-bit AES under keys the service manages. For more control you can associate a customer managed KMS key (symmetric only) with a log group, when you create it or later with associate-kms-key. The key policy must allow the CloudWatch Logs service principal of that Region. New events are then encrypted with your key; if the key is disabled the logs cannot be read, and if it is deleted they are lost for good. KMS itself is lesson 21.2.
Access. IAM policies control who can read or write a group (by its ARN, or with tags). Streams have no permissions of their own; access is decided at the group level. Deletion protection can stop a group from being deleted by mistake.
Log class. Chosen when the group is created and cannot be changed later:
| Standard | Essentials (formerly Infrequent Access) | |
|---|---|---|
| Ingestion price | Normal (about 0.50 USD per GB in us-east-1) | About half |
| Storage and Logs Insights price | Same | Same |
| Logs Insights queries, export to S3, KMS, cross-account, data protection | Yes | Yes |
| Metric filters, subscription filters, Live Tail, anomaly detection, embedded metrics | Yes | No |
| Good for | Logs you alert on, stream or watch live | Debug and forensic logs you query rarely |
#Where logs come from
- Your code: the AWS SDK and CLI (
PutLogEvents), in batches of up to 1 MB. Events older than 14 days or more than 2 hours in the future are rejected. - Agents on servers: the unified CloudWatch agent (and the deprecated CloudWatch Logs agent), on EC2 or on premises. More below.
- Elastic Beanstalk: streams instance logs of the application when log streaming is turned on.
- ECS and EKS: containers write to stdout and the
awslogslog driver (or Fluent Bit) ships each container’s output as a stream (lesson 14.2). - Lambda: every function writes to
/aws/lambda/<function-name>automatically, if its execution role allows it (lesson 15.1). - VPC Flow Logs: accepted and rejected connections of a VPC, subnet or network interface (lesson 22.3).
- API Gateway: execution logs and access logs per stage.
- CloudTrail: a trail can also deliver its API events to a log group, so metric filters can alarm on them (lesson 19.6).
- Route 53: DNS query logs. Public hosted zone query logs go to a log group in us-east-1; Resolver query logs record the DNS queries made from inside your VPCs.
- And many more: RDS and Aurora database logs, AWS WAF, Step Functions, Systems Manager Run Command output…
#Metric filters: from log lines to numbers
Alarms watch metrics, not text. To be told when a certain line appears in the logs, you first turn matching lines into a number. That is a metric filter:
- It belongs to a log group and has a filter pattern: a word or phrase (
ERROR,"payment failed"), several terms (?ERROR ?Timeoutfor either one), a space-delimited pattern for fields of a line ([ip, user, ts, request, status=5*, size]), or a JSON pattern ({ $.level = "error" },{ $.latency > 2000 }). - For each new event that matches, it publishes a value (usually
1, or a field such as the latency) to a custom metric in the namespace and name you choose, optionally with dimensions taken from fields of the event (up to 3). - A default value (usually 0) is published for periods with no match; without it, quiet periods have no data at all.
- It applies only to events ingested after it is created: it never scans old data. Up to 100 metric filters per log group, Standard class only.
Then the usual chain completes the story: an alarm on that metric (lesson 19.3), with an action that publishes to an SNS topic (email, SMS, a Lambda function, a chat channel; lesson 13.4).
From a log line to a phone alert
A mobile wallet’s payment API writes JSON logs. A metric filter counts the failed payments, an alarm watches the count, and SNS wakes the on-call engineer. Then see the two classic surprises.
Scenario
Logs → metric filter → metric → alarm → SNS: the exam’s chain for “notify when a specific error appears in the logs”.
Two log events
09:14:02
{"level":"info","path":"/send-money","status":200}
09:14:03
{"level":"error","code":"payment_failed","status":502}
Eid salary day, heavy traffic. Most lines say info, but some payments fail and the app writes a line with level error and code payment_failed. Logs alone cannot raise an alarm; alarms watch metrics.
#Logs Insights: ask questions of stored logs
CloudWatch Logs Insights is an interactive query engine over the events already stored in your log groups. You pick a time range and one or more log groups (also in other accounts, through CloudWatch cross-account observability) and write a query. Typical questions: count the ERROR lines per 5 minutes, find every request of one IP address, list the ten slowest endpoints, see the top error messages after a deployment.
- Three query languages. The purpose-built Logs Insights query language (commands joined by pipes:
fields,filter,stats,sort,limit,parse,dedup,pattern…), and since late 2024 also OpenSearch PPL and OpenSearch SQL (SQL even allows joins across log groups). - Discovered fields. It finds fields automatically in logs of AWS services (Lambda, VPC Flow Logs, CloudTrail, Route 53) and in any JSON log, so you filter on
statusordurationby name;parseextracts fields from plain text. - Not real time. It queries what is already stored, from seconds ago to years ago. A query runs at most 60 minutes, and results are kept 7 days. Up to 100 queries can run at the same time per account.
- Price. You pay per GB of data scanned (about 0.005 USD per GB in us-east-1), however few rows match. Narrow time ranges and fewer log groups cost less; field indexes can skip events.
- Reuse. Save queries (with parameters), add them to CloudWatch dashboards, run them on a schedule, or generate them from a question in plain English.
Logs Insights playground
Sixteen events of the log group /wallet/api from 09:00 to 09:15. Pick a query and see which lines it touches and what it returns. Then switch to Live Tail and watch new lines arrive.
Choose a query
Log events (matching lines highlighted)
- 09:00:12 {"level":"info","path":"/send-money","status":200,"duration":120,"ip":"198.51.100.23"}
- 09:01:40 {"level":"info","path":"/balance","status":200,"duration":35,"ip":"198.51.100.41"}
- 09:02:05 {"level":"info","path":"/cash-out","status":200,"duration":210,"ip":"203.0.113.7"}
- 09:03:18 {"level":"warn","path":"/send-money","status":429,"duration":8,"ip":"203.0.113.7"}
- 09:04:51 {"level":"info","path":"/balance","status":200,"duration":31,"ip":"198.51.100.23"}
- 09:05:30 {"level":"info","path":"/send-money","status":200,"duration":140,"ip":"198.51.100.62"}
- 09:06:02 {"level":"error","path":"/cash-out","status":502,"duration":3010,"ip":"198.51.100.41"}
- 09:07:44 {"level":"info","path":"/balance","status":200,"duration":29,"ip":"203.0.113.7"}
- 09:08:15 {"level":"error","path":"/send-money","status":502,"duration":3002,"ip":"198.51.100.23"}
- 09:09:59 {"level":"info","path":"/send-money","status":200,"duration":133,"ip":"198.51.100.62"}
- 09:10:20 {"level":"error","path":"/send-money","status":504,"duration":5000,"ip":"198.51.100.88"}
- 09:11:03 {"level":"error","path":"/cash-out","status":502,"duration":3005,"ip":"203.0.113.7"}
- 09:11:47 {"level":"info","path":"/balance","status":200,"duration":33,"ip":"198.51.100.41"}
- 09:12:30 {"level":"error","path":"/send-money","status":502,"duration":3011,"ip":"198.51.100.62"}
- 09:13:09 {"level":"info","path":"/cash-out","status":200,"duration":190,"ip":"198.51.100.88"}
- 09:14:26 {"level":"error","path":"/send-money","status":504,"duration":5000,"ip":"198.51.100.23"}
Result
Choose a query
#Live Tail: watch logs as they arrive
Live Tail is the opposite of Logs Insights: instead of searching the past, it streams new events from up to 10 log groups to your console or CLI within about a second of ingestion, with filter terms and highlighting. Developers open it during a deployment or an incident to see, right now, what the new version prints.
| Logs Insights | Live Tail | |
|---|---|---|
| Looks at | Stored, historical events in a time range | New events from now on |
| Use | Investigation, statistics, reports, dashboards | Watching a deployment or incident live |
| Billing | Per GB scanned | Per minute of session (about 0.01 USD per minute) |
| Log class | Standard and Essentials | Standard only |
Sessions are interactive: up to 15 at a time per account, and an API session lasts up to 3 hours.
#Getting logs out: export or subscription
CloudWatch Logs is a good place to search recent logs, but not always the place to keep them for years, nor the tool the security team already uses. There are two ways out.
#Export to S3: batch
An export task (CreateExportTask) copies the events of one log group and one time range to an S3 bucket as gzip files, under a prefix you choose.
- It is not real time: log data can take up to 12 hours to become available for export.
- Only one export task can be active at a time per account and Region; tasks that run longer than 24 hours time out.
- The bucket policy must allow the CloudWatch Logs service to write. Buckets encrypted with SSE-S3 or SSE-KMS and buckets with Object Lock are supported (DSSE-KMS is not). The bucket may be in another account.
- AWS recommends not to use regular exports for continuous archiving; use subscriptions.
Good for: a one-off copy of last month for an auditor, or a migration.
#Subscription filters: real time
A subscription filter sends the events of a log group that match a pattern (an empty pattern means all), as they are ingested, to:
- Kinesis Data Streams: for custom real-time consumers and replay (lesson 13.5).
- Amazon Data Firehose (formerly Kinesis Data Firehose): near real-time delivery to S3, Redshift, OpenSearch or a partner such as Splunk or Datadog, with no code (lesson 13.6).
- Lambda: custom processing of each batch of events.
- OpenSearch Service: the console wires this through a Lambda function it creates, for full-text search and dashboards (lesson 17.3).
Details that matter:
- Events arrive base64-encoded and gzip-compressed, usually well within three minutes, at least once (a rare duplicate is possible).
- Up to 5 subscription filters per log group, each with its own pattern and destination.
- CloudWatch Logs needs permission to write to the destination: an IAM role for Kinesis and Firehose, a resource-based permission on the Lambda function.
- If the destination throttles or fails with a retryable error, delivery is retried for up to 24 hours; after that the events are dropped from the subscription. Watch the delivery throttling metric.
- Account-level subscription filter (since 2024): one per account and Region that covers all log groups (or all except those you exclude) and sends to Kinesis Data Streams, Firehose or Lambda. Exclude the log groups of the delivery pipeline itself, or the subscription can feed itself in an endless, expensive loop.
- Standard class only.
| Export task | Subscription filter | |
|---|---|---|
| Timing | Batch; data exportable after up to 12 hours | Real time, as events are ingested |
| Covers | One log group, one past time range | New events only, from creation on |
| Destinations | S3 (same or other account) | Kinesis Data Streams, Firehose, Lambda, OpenSearch |
| Limit | One active task per account and Region | 5 per log group, plus one account-level |
| Use for | One-off copies, audits | Continuous archiving, SIEM, real-time processing |
Getting logs out: subscription or export
The news portal must keep its logs for years in S3. A subscription filter streams them out within seconds or minutes; an export task copies a time range in a batch that can be hours behind.
Scenario
A subscription filter sends matching events as they are ingested to Kinesis Data Streams, Firehose, Lambda or OpenSearch. Up to 5 per log group.
The team creates a Firehose stream that writes to the archive bucket, and an IAM role that lets CloudWatch Logs put records into Firehose. Then it adds a subscription filter to the log group with an empty pattern, meaning every event.
#Collecting logs from many accounts and Regions
Large organisations put every application in its own AWS account (lesson 20.1). The security team then wants all their logs in a separate log-archive account: one place to search, and a place application teams cannot tamper with.
The classic way is a cross-account subscription:
Build the receiver in the archive account
Create a Kinesis data stream (or a Firehose stream), and an IAM role that CloudWatch Logs can assume, with permission to put records into it.
Create a destination
A CloudWatch Logs destination (
PutDestination) is a named endpoint that wraps the stream and the role. Attach a destination access policy that names the source accounts allowed to subscribe, or the whole organization with a condition on the organization ID.Subscribe from each source account
In each source account, create a subscription filter on the log group with the destination’s ARN as its target. The source account never gets access to the stream itself.
Deliver and store
Kinesis Data Streams → Firehose → S3 in the archive account (and any real-time consumers on the stream). Protect the bucket with Object Lock and a strict bucket policy (lesson 10.9).
Two rules: the log group and the destination must be in the same Region, so you create one destination per source Region; the resource behind a destination (the stream) may be in another Region. Destinations can also wrap a Firehose stream directly, and Lambda can be a cross-account target too.
Every account’s logs in one archive account
A bank keeps each application in its own AWS account. The security team wants all their logs, as they happen, in a separate log-archive account that application teams cannot touch.
Scenario
Subscription filter (source) → destination with an access policy (archive) → Kinesis Data Streams → Firehose → S3.
In the log-archive account the team builds the receiving side: a Kinesis data stream, an IAM role that CloudWatch Logs can assume to put records into it, and a CloudWatch Logs destination that wraps the stream and the role.
#Agents: the unified CloudWatch agent
EC2 instances and on-premises servers need software inside the operating system to send log files, and to report what only the operating system knows. There have been two agents:
| CloudWatch Logs agent (old) | Unified CloudWatch agent | |
|---|---|---|
| Status | Deprecated, no longer recommended; does not support IMDSv2; removed from Amazon Linux 2023 | Current, recommended |
| Sends | Logs only | Logs and metrics |
| System metrics | None | Memory, disk space used, swap, CPU detail, disk I/O, netstat (TCP/UDP connections), processes |
| Operating systems | Linux only | Linux, Windows Server and macOS |
| Where | EC2 and on-premises Linux servers | EC2 and on-premises servers (hybrid) |
| Configuration | A local config file per server | JSON config, stored centrally in SSM Parameter Store and fetched by every server |
| Extras | None | StatsD and collectd custom metrics, procstat per-process metrics, traces to X-Ray |
The metrics the unified agent adds (in the CWAgent namespace by default):
- CPU: time spent active, idle, guest, system, user, steal…
- Disk: free, used, used percent, total; and disk I/O: reads, writes, bytes, IOPS.
- Memory (RAM): free, used, used percent, cached, inactive, total.
- Netstat: number of TCP connections by state and UDP sockets; network packets and bytes.
- Processes: total, running, sleeping, blocked, idle, dead (and per-process CPU and memory with procstat).
- Swap: free, used, used percent.
Compare with the built-in EC2 metrics, which come from the hypervisor, outside the guest operating system: CPU utilisation, network in and out, disk operations for instance store, and status checks. Memory and disk space used are not among them (lesson 19.1).
Permissions.
- On EC2: attach an instance role with the AWS managed policy
CloudWatchAgentServerPolicy(it allows writing metrics and logs and readingAmazonCloudWatch-*parameters). No keys on disk. Lesson 2.4 covers roles. - On premises there is no instance role. The agent reads credentials from a named profile (
AmazonCloudWatchAgentby default) in a shared credentials file: the access keys of an IAM user with the same policy, or better, short-lived credentials refreshed by IAM Roles Anywhere. The server also needs a network path to AWS (internet, VPN or Direct Connect).
Central configuration. Create the config once (the agent’s wizard writes it), store it in Parameter Store as AmazonCloudWatch-linux, and every server runs amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -c ssm:AmazonCloudWatch-linux -s (-m onPremise on premises). Systems Manager can install and configure the agent on the whole fleet (lesson 25.3). The agent creates the log groups and streams it needs.
The unified CloudWatch agent on EC2 and on premises
The news portal runs web servers on EC2 and its old printing system on a server in a Tejgaon data centre. One agent, one shared config in Parameter Store, logs and memory metrics from both.
Scenario
Instance role with the agent server policy → fetch config from Parameter Store → ship logs and system metrics.
The web server has the amazon-cloudwatch-agent package installed (often through Systems Manager) and an instance role with the AWS managed policy for the agent server, which allows writing logs and metrics and reading the config parameter.
#More features worth knowing
#Which feature for which need?
Where should these logs go?
Pick a situation, choose the CloudWatch Logs feature you would use, then read why. Listen for the keywords: alert, one-off, continuous, per event, search, another account, past, right now, sensitive, memory.
1. Choose a situation(Right first time: 0/10)
A wallet company wants the on-call engineer’s phone to ring within a minute whenever more than five payment_failed lines appear in its API logs in one minute.
2. Your answer
| The question says | Answer |
|---|---|
| Alert when a keyword or error appears in logs | Metric filter → metric → alarm → SNS |
| Count or analyse stored logs, top N, ad hoc | Logs Insights |
| Watch logs live during a deployment | Live Tail |
| Real time to S3, OpenSearch, Splunk, Lambda, Kinesis | Subscription filter (with Firehose for S3 and partners) |
| One-off export of old logs to S3, not real time | Export task (CreateExportTask) |
| Logs of many accounts and Regions in one place | Cross-account destination + Kinesis Data Streams + Firehose + S3 |
| Memory or disk space of EC2; on-premises logs and metrics | Unified CloudWatch agent |
| Keep logs cheaply for rarely run queries | Infrequent Access (Essentials) class, or archive to S3 |
| Hide sensitive data in logs | Data protection policy |
#What goes wrong
- No logs from an instance: no agent, agent stopped, no instance role or wrong policy, wrong Region in the config, wrong file path. The agent’s own log on the instance says which.
- Logs gone: retention deleted them; or a log group was deleted. Keep an archive in S3 for anything you might need later, and use deletion protection.
- Huge bill: retention left at Never Expire, verbose debug logs ingested at full price, or Logs Insights queries over months of data. Set retention, log less, use the cheaper class for rarely read logs, narrow queries.
- Metric shows nothing for past errors: metric filters count only new events. Query the past with Logs Insights.
- Alarm in insufficient data at night: the metric filter has no default value; publish 0 when nothing matches.
- Unreadable logs: the customer managed KMS key was disabled, its policy changed, or it was scheduled for deletion.
- Subscription stops delivering: the destination throttles (Lambda concurrency, too few Kinesis shards) and events are dropped after 24 hours of retries; or the role lost its permission. Watch the delivery metrics.
- Endless loop and bill: an account-level subscription that also subscribes the log group of its own Lambda or Firehose. Exclude those log groups.
- Cross-account subscription denied: the source account is not in the destination access policy, or the destination is in another Region.
- Export missing the last hours: data can take up to 12 hours to become exportable; and a second export waits behind the first.
- Cannot add a subscription or metric filter: the group is in the Infrequent Access (Essentials) class, which cannot be changed.
- Secrets in logs: passwords or card numbers printed by debug code. Fix the code, add a data protection policy, and restrict who can read the group.
#CloudWatch Logs in system design
- Observability baseline: every service writes structured JSON logs to a log group per component, with retention set by infrastructure code, metric filters for the errors that matter, and alarms to SNS. Dashboards combine the resulting metrics with Logs Insights widgets.
- Security and compliance archive: subscription filters (or centralization rules) send every account’s logs to a log-archive account; Firehose writes to an Object Lock bucket with lifecycle rules; Athena queries the archive when an investigation needs years of data (lesson 17.1).
- Real-time detection: subscription to Kinesis Data Streams feeding a SIEM or a Lambda detector; VPC Flow Logs and CloudTrail in CloudWatch Logs with metric filters for rejected traffic or root logins (lessons 19.6 and 21.7).
- Hybrid estate: the unified agent on EC2 and on-premises servers, one config in Parameter Store, the same dashboards for both.
- Cost control: the cheaper class for debug logs, short retention plus S3 archive, log sampling for very chatty services.
Where else to look: performance graphs are metrics (19.1), deeper container and Lambda views are the Insights family (lesson 19.4), who called which API is CloudTrail (19.6), and the full comparison of CloudWatch, CloudTrail and Config is in lesson 19.7.
#Try it yourself
These commands use the AWS CLI v2 in ap-southeast-1. Ingestion, storage, queries and Live Tail cost a little; delete everything at the end.
# 1. A log group with 30-day retention, and a test event
aws logs create-log-group --log-group-name /wallet/api
aws logs put-retention-policy --log-group-name /wallet/api --retention-in-days 30
aws logs create-log-stream --log-group-name /wallet/api --log-stream-name test-1
aws logs put-log-events --log-group-name /wallet/api --log-stream-name test-1 \
--log-events "timestamp=$(date +%s000),message={\"level\":\"error\",\"path\":\"/send-money\",\"status\":502}"
# { "nextSequenceToken": "49654...", "rejectedLogEventsInfo": null }
# 2. A metric filter that counts error lines, with 0 when nothing matches
aws logs put-metric-filter --log-group-name /wallet/api --filter-name payment-errors \
--filter-pattern '{ $.level = "error" }' \
--metric-transformations metricName=PaymentErrors,metricNamespace=Wallet,metricValue=1,defaultValue=0
aws logs test-metric-filter --filter-pattern '{ $.level = "error" }' \
--log-event-messages '{"level":"error","status":502}' '{"level":"info","status":200}'
# { "matches": [ { "eventNumber": 1, "eventMessage": "{\"level\":\"error\",\"status\":502}", ... } ] }
# 3. A Logs Insights query over the last hour
QID=$(aws logs start-query --log-group-name /wallet/api \
--start-time $(date -d '-1 hour' +%s) --end-time $(date +%s) \
--query-string 'filter level = "error" | stats count(*) as errors by bin(5m)' \
--query queryId --output text)
aws logs get-query-results --query-id "$QID"
# { "status": "Complete", "statistics": { "recordsMatched": 1.0, "recordsScanned": 1.0, "bytesScanned": 78.0 },
# "results": [ [ { "field": "bin(5m)", "value": "2026-11-01 09:15:00.000" }, { "field": "errors", "value": "1" } ] ] }
# 4. Live Tail for 5 minutes, highlighting errors (Ctrl+C to stop)
aws logs start-live-tail \
--log-group-identifiers arn:aws:logs:ap-southeast-1:111122223333:log-group:/wallet/api \
--log-event-filter-pattern '{ $.level = "error" }'
# 5. Export yesterday to S3 (bucket policy must allow logs.ap-southeast-1.amazonaws.com)
aws logs create-export-task --task-name wallet-2026-10-31 --log-group-name /wallet/api \
--from 1761868800000 --to 1761955200000 \
--destination wallet-logs-archive-example --destination-prefix exports/2026-10-31
# { "taskId": "c2a1f6d8-5b9e-4e1a-9a4f-0e7d2b3c4f51" }
aws logs describe-export-tasks --task-id c2a1f6d8-5b9e-4e1a-9a4f-0e7d2b3c4f51 --query 'exportTasks[0].status'
# { "code": "COMPLETED", "message": "Completed successfully" }
# 6. Real-time subscription to an existing Firehose stream (role lets logs put records)
aws logs put-subscription-filter --log-group-name /wallet/api --filter-name to-firehose \
--filter-pattern '' \
--destination-arn arn:aws:firehose:ap-southeast-1:111122223333:deliverystream/wallet-logs \
--role-arn arn:aws:iam::111122223333:role/CWLtoFirehoseRole
# 7. Cross-account: in the archive account 444455556666, a destination and its access policy
aws logs put-destination --destination-name org-logs \
--target-arn arn:aws:kinesis:ap-southeast-1:444455556666:stream/org-logs \
--role-arn arn:aws:iam::444455556666:role/CWLtoKinesisRole
aws logs put-destination-policy --destination-name org-logs --access-policy '{
"Version": "2012-10-17",
"Statement": [{ "Effect": "Allow", "Principal": { "AWS": ["111122223333", "777788889999"] },
"Action": "logs:PutSubscriptionFilter",
"Resource": "arn:aws:logs:ap-southeast-1:444455556666:destination:org-logs" }] }'
# ... and in each source account:
aws logs put-subscription-filter --log-group-name /wallet/api --filter-name to-archive --filter-pattern '' \
--destination-arn arn:aws:logs:ap-southeast-1:444455556666:destination:org-logs
# 8. Unified agent on EC2 (instance role with CloudWatchAgentServerPolicy), config from Parameter Store
sudo dnf install -y amazon-cloudwatch-agent
sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl \
-a fetch-config -m ec2 -c ssm:AmazonCloudWatch-linux -s
sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a status
# { "status": "running", "starttime": "2026-11-01T09:20:11+00:00", "configstatus": "configured", "version": "1.300..." }
# 9. Clean up
aws logs delete-subscription-filter --log-group-name /wallet/api --filter-name to-firehose
aws logs delete-log-group --log-group-name /wallet/api
Also try in the console: open Logs Insights, choose a Lambda log group and run the sample query for the most expensive invocations; then open Live Tail on the same group and invoke the function.
#Summary
Key takeaways
- Log event (message + timestamp) → log stream (one source, in order) → log group (settings: retention, KMS key, access, log class). Retention 1 day to 10 years, default Never Expire.
- Logs are always encrypted at rest; optionally associate a customer managed KMS key with the group (lose the key, lose the logs).
- Sources: SDK, agents, Beanstalk, ECS, Lambda, VPC Flow Logs, API Gateway, CloudTrail, Route 53 and more. Nothing leaves an EC2 instance without an agent and IAM permissions.
- Metric filter turns matching lines into a metric, only for new events; exam chain: logs → metric filter → metric → alarm → SNS.
- Logs Insights: query stored logs (own language, OpenSearch PPL, OpenSearch SQL), pay per GB scanned, not real time. Live Tail: watch new logs right now.
- Export task (
CreateExportTask) → S3, batch, up to 12 hours behind, one active task. Subscription filter → Kinesis Data Streams, Data Firehose, Lambda, OpenSearch, real time, 5 per log group, plus account-level filters. - Multi-account / multi-Region: subscription filter → destination with access policy (same Region) → Kinesis Data Streams → Firehose → S3 in a log-archive account. Newer: centralization rules, cross-account observability.
- Unified CloudWatch agent (logs + memory, disk, swap, netstat, processes; Linux, Windows, macOS; EC2 and on premises; config in SSM Parameter Store) replaces the deprecated CloudWatch Logs agent. EC2 needs a role with
CloudWatchAgentServerPolicy; on premises needs credentials.
Answer on your own first, then read the explanation, even when you got it right.
Self-checkQuestion
In CloudWatch Logs, where are retention, the KMS key and the log class set?
Self-checkQuestion
What is the default retention of a new CloudWatch Logs log group?
Self-checkQuestion
A team runs 40 EC2 instances and notices that no application logs appear in CloudWatch Logs. Nothing was configured besides launching the instances. Why?
Self-checkQuestion
Which statement about encryption of CloudWatch Logs data is correct?
Self-checkQuestion
An admin disables the customer managed KMS key associated with a log group. What happens?
Self-checkQuestion
A payment API must page the on-call engineer within a minute whenever more than 5 lines containing payment_failed appear in its logs in one minute. Which design is correct?
Self-checkQuestion
At 10:25 an engineer creates a metric filter for errors that happened between 10:00 and 10:20. What does the new metric show for 10:00 to 10:20?
Self-checkQuestion
An alarm on a metric-filter metric sits in insufficient data every night, when no errors happen. What is the simplest fix?
Self-checkQuestion
An engineer wants the ten slowest API paths of last week, with average and maximum latency, from JSON logs already stored in CloudWatch Logs. What is the quickest tool?
Self-checkQuestion
During a release, developers want to see new log lines from the new version as they are written, filtered on one request ID and highlighted. Which feature fits?
Self-checkQuestion
Which two statements about CloudWatch Logs Insights are correct?
Choose 2
Self-checkQuestion
A company must copy all log events of one log group for last month to S3 once, for an auditor. Speed does not matter. What is the simplest option?
Self-checkQuestion
Why is an export task the wrong answer for near real-time delivery of logs to S3?
Self-checkQuestion
Which two are valid destinations of a CloudWatch Logs subscription filter?
Choose 2
Self-checkQuestion
A hospital must keep application logs for 7 years at the lowest cost but search only the last 30 days in CloudWatch. Logs must reach the archive continuously with no servers. Which design fits best?
Self-checkQuestion
A subscription filter sends logs to a Lambda function. During a traffic spike the function is throttled. What does CloudWatch Logs do?
Self-checkQuestion
How many subscription filters can one log group have today?
Self-checkQuestion
A security team wants the logs of 30 AWS accounts, in two Regions, streamed in real time to a central log-archive account that application teams cannot modify. Which design is the classic answer?
Self-checkQuestion
A newly created account tries to add a subscription filter that points at the central destination and gets an access denied error. Which two changes fix it in a lasting way?
Choose 2
Self-checkQuestion
A log group in ap-south-1 cannot subscribe to a destination created in ap-southeast-1. What is the rule?
Self-checkQuestion
A company wants to monitor the memory utilisation and free disk space of its EC2 instances. What should it do?
Self-checkQuestion
Which two statements compare the old CloudWatch Logs agent and the unified CloudWatch agent correctly?
Choose 2
Self-checkQuestion
The unified CloudWatch agent is installed on an on-premises server in a data centre. Which is required so it can send data?
Self-checkQuestion
A new EC2 instance has the unified agent running with a correct config, but no logs or memory metrics appear, and the agent log shows access errors. What is the most likely cause?
Self-checkQuestion
Where is the shared configuration of the unified CloudWatch agent usually stored so that a whole fleet can use it?
Self-checkQuestion
To save money, a team creates log groups in the Infrequent Access (Essentials) class. Later it cannot add a metric filter or a subscription filter. What is true?
Self-checkQuestion
A payment app accidentally logs card numbers. Auditors require that most readers never see them, while a small investigation team still can. What CloudWatch Logs feature helps immediately?
Self-checkQuestion
An account-level subscription filter sends all log groups to a Lambda function, and the logging bill explodes. What is the most likely cause?
Self-checkQuestion
Which two statements about sources of CloudWatch Logs are correct?
Choose 2
Self-checkQuestion
The notes give this cheat-sheet line: Log stream to S3 or Elasticsearch → Subscription Filter → Kinesis Data Firehose → S3. Which update is correct today?
Self-checkQuestion
A colleague says: Logs Insights is a real-time engine, so we can replace Live Tail and metric-filter alarms with it. What is correct?