---
cuppy_hub: aws
cuppy_path: guides/cloudwatch/query-logs.md
cuppy_section: docs
cuppy_doc: VHXE-77N1
cuppy_doc_uuid: c79a05e2-df49-484e-a3bc-6900fedccee9
cuppy_permalink: https://cuppy.io/aws/d/VHXE-77N1
cuppy_version: 4
cuppy_updated: 2026-10-23T16:16:52.575Z
cuppy_canonical: https://cuppy.io/aws/docs/guides/cloudwatch/query-logs
cuppy_mcp: https://cuppy.io/mcp
cuppy_body_sha256: f9bbab0efba218ae8b7ed1e5b24e4399ea334514e88b4c8024a6ca32a15c1b17
# cuppy_body_sha256 is over the document body ALONE, excluding both generated blocks.
---


# Query CloudWatch Logs with Logs Insights

**Pick the fewest log groups and the narrowest time window, filter early, then aggregate.** You pay for the data scanned, so a query over one log group for 15 minutes costs a fraction of one over ten groups for a week.

## Where to run it

- **Console:** Since 2026-06-15, **Log Analytics** combines Logs Insights, Live Tail and Contributor Insights in one view, with several query tabs. It is the default; opting out shows the three tools separately. Cancel long queries before you close the page: they keep running, and you keep paying.
- **Watch live:** `aws logs tail /aws/lambda/<fn> --since 30m --follow`.
- **CLI or code:** `StartQuery`, then poll `GetQueryResults` (below).

## Query shape

```
fields @timestamp, @message, @logStream
| filter @message like /ERROR|Task timed out/
| sort @timestamp desc
| limit 50
```

- Commands are separated by `|`, and `#` starts a comment. `where` is an alias of `filter`.
- **Always-present fields** (Standard log class): `@timestamp`, `@message`, `@ingestionTime`, `@logStream`, and `@log` (`account-id:log-group-name`).
- Service logs get **discovered fields**:
  - **Lambda:** `@type`, `@requestId`, `@duration`, `@billedDuration`, `@memorySize`, `@maxMemoryUsed`, and the X-Ray IDs.
  - **VPC flow logs:** `srcAddr`, `dstAddr`, `dstPort`, `action`, `bytes` and the rest.
  - **Route 53** query logs.
- **JSON logs** are flattened with dot notation (`userIdentity.arn`), and arrays by index (`items.0.instanceId`). At most 200 fields are extracted. For JSON stored inside a string, use `jsonParse(field)`. For text, use `parse @message "* [*] *" as a, b, c` (glob) or a regex with named groups.
- **Aggregate:** `stats count(*) by bin(5m)`, `stats avg(@duration), max(@duration) by bin(5m)`, `dedup @requestId`.
- **Recent additions** (2026-07-15) include `topk`, `variance`, `countFrequent`, `sessionize`, `fillmissing`, `appendcols`, `cidrlookup`, and functions such as `parseDate`, `formatDate`, `jsonArrayContains` and `messageSize`. The syntax page also lists `join`, `lookup`, `subqueries`, `anomaly` and `pattern`.
- **Before a big query:** `estimate` returns the bytes it would scan, and `limit any` stops scanning once enough rows are found.

Ready-made queries for the hub's error catalogues are in `reference/cloudwatch/logs-insights-queries.md`.

## Limits

- **50 log groups** per query, given as names or ARNs. ARNs must have no trailing `*`. With the CLI or API, `SOURCE` in the query string can select log groups by prefix, account, class or tag instead; the console doesn't support `SOURCE`.
- **60 minutes** of runtime. Split the time range if a query times out.
- **Results:** `GetQueryResults` returns at most 10,000 rows per call. `limit` can go up to 100,000 with paging (Logs Insights QL only).
- **Concurrent queries:** one account-wide quota, shared with dashboard widgets and scheduled queries. Frequently refreshing dashboards use it up.
- **Languages:** `queryLanguage` is `CWLI` (Logs Insights QL), `SQL` (OpenSearch SQL: log groups go in the query) or `PPL` (OpenSearch PPL).
- **Infrequent Access log class:** cheaper to ingest. It supports Logs Insights QL, and since 2026-03-27 also PPL, SQL and data-protection masking, but not `pattern`, `diff`, `unmask` or `filterIndex`.

## From TypeScript

```ts
import { CloudWatchLogsClient, StartQueryCommand, GetQueryResultsCommand } from "@aws-sdk/client-cloudwatch-logs";

const logs = new CloudWatchLogsClient({});

export async function insights(logGroups: string[], query: string, minutesBack = 60) {
  const end = Math.floor(Date.now() / 1000);                 // epoch SECONDS
  const { queryId } = await logs.send(new StartQueryCommand({
    logGroupIdentifiers: logGroups,                          // up to 50 names or ARNs
    startTime: end - minutesBack * 60,
    endTime: end,
    queryString: query,
    limit: 1000,
  }));
  for (;;) {
    await new Promise((r) => setTimeout(r, 1000));
    const res = await logs.send(new GetQueryResultsCommand({ queryId }));
    if (res.status === "Complete") {
      // Each row is [{ field, value }, ...]; turn it into an object.
      return (res.results ?? []).map((row) => Object.fromEntries(row.map((c) => [c.field, c.value])));
    }
    if (res.status === "Failed" || res.status === "Cancelled" || res.status === "Timeout") {
      throw new Error(`Query ${queryId} ended with status ${res.status}`);
    }
  }
}

const rows = await insights(["/aws/lambda/orders"],
  'filter @type = "REPORT" | stats max(@duration) as maxMs, avg(@maxMemoryUsed/1000/1000) as avgMB by bin(5m)');
console.table(rows);
```

- `startTime` and `endTime` are **seconds**. Log event timestamps and `PutLogEvents` use milliseconds.
- `MalformedQueryException` includes a `QueryCompileError` with the location of the error.
- `LimitExceededException`: the API says only that a maximum was reached. With many dashboards or parallel jobs, the concurrent-query quota is the likely one, so back off and retry.
- Other errors are in `reference/cloudwatch/errors.md`.

## When the log group itself is the problem

Missing, unreadable or expiring log groups (retention, KMS, log classes, Lambda log format) are covered in `reference/cloudwatch/log-groups.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 -->
