---
cuppy_hub: aws
cuppy_path: reference/cloudwatch/log-groups.md
cuppy_section: docs
cuppy_doc: XCP5-0KJY
cuppy_doc_uuid: 412b2441-3159-4832-8897-c4ffd32ba06a
cuppy_permalink: https://cuppy.io/aws/d/XCP5-0KJY
cuppy_version: 4
cuppy_updated: 2026-10-23T16:16:52.575Z
cuppy_canonical: https://cuppy.io/aws/docs/reference/cloudwatch/log-groups
cuppy_mcp: https://cuppy.io/mcp
cuppy_body_sha256: 293bf5a050953b894c1726c9894fe438278902bafd0265dc489e8c9e86c665c3
# cuppy_body_sha256 is over the document body ALONE, excluding both generated blocks.
---


# 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:

1. Use a **symmetric** key. In its key policy, allow the logs service principal for the **key's Region**, scoped to the log group:

```json
{
  "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.

2. Associate the key: `aws logs create-log-group --log-group-name <name> --kms-key-id <key-arn>`, or `aws logs associate-kms-key …` on an existing group. The caller needs `kms:DescribeKey`, or the call fails with `AccessDeniedException`.
3. 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 is `Text`; Lambda Managed Instances always use JSON.
  - In JSON, Node.js `console.*` lines become objects with `timestamp`, `level`, `message` and `requestId`.
  - Errors logged with several arguments add `errorType`, `errorMessage` and `stackTrace`.
  - Platform lines use `time`, `type` and `record`.
- Switching to JSON without setting levels sets **application and system levels to INFO**, so `console.debug` output 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`.


---
<!-- cuppy -->
You are reading one file. The same content is free over MCP at https://cuppy.io/mcp with no signup: ranked search across all 131 documents in this hub, the merged view when you work in a layer, known workarounds matched to the error you hit, and depth control so you spend tokens on what you need. Start with the `_hello` tool. How to connect: https://cuppy.io/aws/ Something wrong in this document? Report it with the `session feedback` tool on the same endpoint.
<!-- /cuppy -->
