---
cuppy_hub: aws
cuppy_path: reference/cloudwatch/metrics-and-math.md
cuppy_section: docs
cuppy_doc: QT04-VGV8
cuppy_doc_uuid: c004cbf7-c26a-43ac-9097-1aff3f07a407
cuppy_permalink: https://cuppy.io/aws/d/QT04-VGV8
cuppy_version: 4
cuppy_updated: 2026-10-23T16:16:52.575Z
cuppy_canonical: https://cuppy.io/aws/docs/reference/cloudwatch/metrics-and-math
cuppy_mcp: https://cuppy.io/mcp
cuppy_body_sha256: 04c3919b31cf462f10def9a99174218e198ee126dc9eca08a6b11b69c3698ab7
# cuppy_body_sha256 is over the document body ALONE, excluding both generated blocks.
---


# CloudWatch metrics: identity, retention, publishing, EMF and metric math

**A metric is its namespace, its name and its exact set of dimensions.** `Errors` with `FunctionName=a` and `Errors` with `FunctionName=a, Resource=a:prod` are two different metrics. CloudWatch doesn't add custom metrics up across dimensions for you, so query with exactly the dimensions you published.

## Basics

- **Regional.** A metric exists only in the Region it was published to.
- **Can't be deleted.** A metric expires after 15 months without new data.
- **Retention by resolution** (rolled up as it ages):

| Period | Kept |
|---|---|
| Under 60 s (1-second, high-resolution custom metrics) | 3 hours |
| 60 s | 15 days |
| 300 s | 63 days |
| 3600 s | 455 days (15 months) |

- **Periods** are 1, 5, 10, 30 or multiples of 60 seconds. The default is 60, and sub-minute periods only work on custom metrics stored at 1-second resolution.
- **Statistics:** Sum, Average, Minimum, Maximum, SampleCount, percentiles (`p99`, up to ten decimals) and trimmed mean.
  - Percentiles need raw values, none of them negative.
  - Statistic sets give percentiles only in narrow cases (for example, SampleCount 1).
- **Units:** default `None`. Points with different units are aggregated separately, so publish one unit consistently.

## Publishing (`PutMetricData`)

| Limit | Value |
|---|---|
| Request | 1 MB (gzip accepted), up to 1,000 metrics |
| Dimensions | Up to 30 per metric |
| `Values` / `Counts` arrays | Up to 150 values per metric per request |
| Timestamps | Up to 2 weeks in the past, 2 hours in the future |
| Values | Within ±2^360. No NaN or Infinity |
| Namespace | 1–255 characters; don't start with `AWS/` |

**Visibility delays:**

- A **new** metric can take up to **15 minutes** to appear in `ListMetrics` (and so in the console's metric browser).
- Data timestamped 3–24 hours ago can take up to 2 hours to become queryable; data older than 24 hours, 48 hours or more.
- `StorageResolution: 1` makes a high-resolution metric.

**Errors:** `InvalidParameterValue`, `InvalidParameterCombination`, `MissingParameter` (400) and `InternalServiceError` (500). The SDK names them `…Exception`; see `reference/cloudwatch/errors.md`.

## Embedded Metric Format (metrics from log lines)

Write each log event as one JSON object that follows the specification, and CloudWatch extracts the metrics from it. AWS names three ways to send the events: the open-source EMF client libraries, the CloudWatch agent, or `PutLogEvents`. Because there's no `PutMetricData` call, the `cloudwatch:PutMetricData` permission shouldn't be needed (the hub's reading). From Lambda, the common approach is to write the object to stdout, as below. That path isn't described on the fetched pages (UNVERIFIED), so confirm the metric appears before you rely on it:

```ts
export function emitMetric(name: string, value: number, dims: Record<string, string>) {
  console.log(JSON.stringify({
    _aws: {
      Timestamp: Date.now(),                                   // milliseconds
      CloudWatchMetrics: [{
        Namespace: "orders",
        Dimensions: [Object.keys(dims)],                       // array of dimension sets
        Metrics: [{ Name: name, Unit: "Milliseconds", StorageResolution: 60 }],
      }],
    },
    ...dims,                                                   // dimension values: top level
    [name]: value,                                             // metric value: top level
  }));
}

emitMetric("checkoutLatency", 142, { service: "checkout", stage: "prod" });
```

**Rules:**

- The whole log event must be the JSON object, with nothing before or after it.
- Dimension keys and metric values must be **top-level** members, not nested.
- Values are numbers or arrays of up to 100 numbers.
- Up to 100 metric definitions per event and 30 keys per dimension set. Each dimension set becomes its own metric. Event size up to 1 MB. Timestamps from 14 days past to 2 hours future.

**When EMF metrics don't appear:**

- Look in the `AWS/Logs` namespace, which counts extraction failures.
- A bad namespace or name drops that metric; a bad dimension drops that dimension set.
- An out-of-range value, an array over 100, or more than 100 definitions drops the whole event's metrics.
- Delivery is at-least-once, so occasional duplicates are normal.
How Lambda's Text and JSON log formats interact with the "nothing before or after" rule isn't on the fetched pages. If metrics are missing, check the `AWS/Logs` failure counts first.

## Metric math

- **IDs** start with a lowercase letter (`m1`, `errors`). **Functions** are uppercase. The result must be a time series, so scalar functions go inside a bigger expression (`m1 - AVG(m1)`).
- **Functions:**
  - `METRICS()` and `METRICS("err")` (by ID substring);
  - `SUM`, `AVG`, `MIN`, `MAX`, `STDDEV`;
  - `FILL(m, 0 | REPEAT | LINEAR)`, `IF(cond, a, b)`;
  - `RATE`, `DIFF`, `DIFF_TIME`, `PERIOD`;
  - `ANOMALY_DETECTION_BAND(m, n)` (default 2 standard deviations);
  - `SEARCH(...)`, `INSIGHT_RULE_METRIC`, `SERVICE_QUOTA`.
- **Missing data:** treated as 0 in arithmetic and comparisons. A division by zero **drops** that point.
- **Limits:**
  - 500 metrics and expressions per graph;
  - **no alarms on `SEARCH`**;
  - anomaly detection on math can't use `METRICS`, `SEARCH`, more than 10 inputs, mixed periods, periods over an hour or high-resolution metrics.

## AWS/Lambda metrics: what they count

| Metric | Statistic | Counts | Doesn't count |
|---|---|---|---|
| `Invocations` | Sum | Every invocation, including those that errored (= billed requests) | Throttled requests, other invocation errors |
| `Errors` | Sum | Function errors: thrown exceptions, **timeouts**, runtime and configuration errors. Timestamped at invoke time | **Throttles** |
| `Throttles` | Sum | Requests refused with `TooManyRequestsException` | — |
| `Duration` | Avg, Max, pNN | Time in your handler | Cold-start init time |
| `ConcurrentExecutions` | Max | Instances processing events | — |
| `ClaimedAccountConcurrency` | Max | Regional concurrency not available on demand (unreserved in use + reserved + provisioned) | — |
| `IteratorAge` | Max | Age (ms) of the last record in a batch: DynamoDB, Kinesis, DocumentDB | — |
| `OffsetLag` | Max | Kafka / MSK consumer lag | — |
| `AsyncEventAge` / `AsyncEventsDropped` | Max / Sum | Queue time of async events / events dropped after max age or retries | — |
| `DeadLetterErrors`, `DestinationDeliveryFailures` | Sum | Failed sends to a DLQ or destination (FIFO SQS and SNS destinations aren't supported) | — |

Error rate = `Errors / Invocations` (alarm recipe: `guides/cloudwatch/create-alarms.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 -->
