CloudWatch log groups: retention, classes, tiering, encryption and Lambda log format
How log groups store and expire data — default never-expire retention and the allowed retention values, the 72-hour deletion lag, Standard, Infrequent Access and Delivery classes and what IA can't query, 2026 intelligent tiering, KMS encryption with the logs service principal and encryption-context condition, PutLogEvents batch rules (sequence tokens no longer matter), and Lambda's Text/JSON log format and log levels.
markdownreferencev4XCP5-0KJYstage: referencetrust: officialservice: cloudwatchinterface: anylog group retentionnever expirePutRetentionPolicy valuesretentionInDayslog retention deletion 72 hourslog class Standard Infrequent Access DeliveryLogs IA limitationsintelligent tiering logsCloudWatch Logs KMSassociate-kms-keylogs.region.amazonaws.com key policykms:EncryptionContext:aws:logs:arnPutLogEvents limitssequence token ignoredInvalidSequenceTokenExceptionrejectedLogEventsInfoLambda log format JSONLoggingConfig LogFormatApplicationLogLevel/aws/lambda log group
CloudWatch log groups
New log groups keep data forever (Never Expire) unless you set retention. Set it when the group is created, in IaC, because log groups that Lambda and other services create on first write have no retention.
Retention
- Allowed values (days): 1, 3, 5, 7, 14, 30, 60, 90, 120, 150, 180, 365, 400, 545, 731, 1096, 1827, 2192, 2557, 2922, 3288, 3653. To return to "never expire", call
DeleteRetentionPolicy.
aws logs put-retention-policy --log-group-name /aws/lambda/orders --retention-in-days 30
- Expired events are marked and then deleted, typically within 72 hours (occasionally longer). Once marked, they stop counting toward storage cost and
storedBytes. - Lengthening retention can keep events that were marked but not yet deleted. To make sure data is gone, keep the shorter setting until 72 hours after the old expiry.
Classes and tiering
Class (LogGroupClass) | Use | Limits |
|---|---|---|
STANDARD | Default; full features | — |
INFREQUENT_ACCESS | Logs queried occasionally (forensics), at a lower ingestion price per GB | Logs Insights works, plus PPL and SQL and data-protection masking since 2026-03-27. Not supported: pattern, diff, unmask, filterIndex. The always-present @ fields are generated for Standard groups only |
DELIVERY | Listed in the SDK's enum | Not described on the fetched pages |
Whether a group's class can be changed after creation isn't stated on the fetched pages.
Intelligent tiering (account-level, since 2026-07-15):
- Data unread for 30 days moves to Infrequent Access, and after 90 days to Archive Instant Access.
- Reading old data moves it back to Standard for 30 days.
- Queries work the same in every tier.
Encryption with a KMS key
Log groups are always encrypted. To use your key:
- Use a symmetric key. In its key policy, allow the logs service principal for the key's Region, scoped to the log group:
{
"Effect": "Allow",
"Principal": { "Service": "logs.<region>.amazonaws.com" },
"Action": ["kms:Encrypt", "kms:Decrypt", "kms:ReEncrypt*", "kms:GenerateDataKey*", "kms:Describe*"],
"Resource": "*",
"Condition": { "ArnEquals": {
"kms:EncryptionContext:aws:logs:arn": "arn:aws:logs:<region>:<account-id>:log-group:<log-group-name>"
} }
}
Use ArnLike with a wildcard to cover many groups.
- Associate the key:
aws logs create-log-group --log-group-name <name> --kms-key-id <key-arn>, oraws logs associate-kms-key …on an existing group. The caller needskms:DescribeKey, or the call fails withAccessDeniedException. - Wait: association and disassociation take up to five minutes to apply.
If the key is disabled, CloudWatch Logs can't read data encrypted with it. If it is deleted or the permission removed, that data can't be retrieved again. disassociate-kms-key affects only new data. Key policy mechanics are in reference/kms/key-policies-and-errors.md.
Writing (PutLogEvents)
| Rule | Value |
|---|---|
| Batch size | ≤ 1,048,576 bytes: message bytes plus 26 bytes per event |
| Events per batch | ≤ 10,000 |
| Event size | ≤ 1 MB |
| Order | Chronological within a batch, or the call fails |
| Span | ≤ 24 hours between first and last event |
| Timestamps | Milliseconds. More than 2 hours in the future, or older than 14 days or the retention period, are rejected individually (rejectedLogEventsInfo: tooNewLogEventStartIndex, tooOldLogEventEndIndex, expiredLogEventEndIndex), and the rest are stored |
Sequence tokens are ignored now. Parallel writers to one stream are fine, and the old per-stream rate limit was replaced by a per-account limit you can raise. InvalidSequenceTokenException and DataAlreadyAcceptedException are no longer returned, so code that handles them is dead.
Lambda's log group and format
- Lambda writes to the log group
/aws/lambda/<function>by default (the hub's earlier docs; the format page doesn't restate it). - The execution role needs permission to write logs, normally through
AWSLambdaBasicExecutionRole(guides/lambda/debug-failing-function.md). - Log format:
--logging-config LogFormat=JSON(also in CloudFormation, SAM and the CDK). The default isText; Lambda Managed Instances always use JSON. - In JSON, Node.js
console.*lines become objects withtimestamp,level,messageandrequestId. - Errors logged with several arguments add
errorType,errorMessageandstackTrace. - Platform lines use
time,typeandrecord. - Switching to JSON without setting levels sets application and system levels to INFO, so
console.debugoutput should be filtered out (the hub's reading of the level filter). Changing the format doesn't rewrite old logs. - Querying JSON-format Lambda logs:
reference/cloudwatch/logs-insights-queries.md.