# Logs and metrics

Logs and metrics are read per Workload and merged across its units, or narrowed to one with `unit_id`. Both stay readable for **30 days**, including for units that were scaled away, destroyed, or whose Workload is stopped.

**curl**

```bash
# newest 200 lines of every unit, then follow
curl -sS -G "$UNITS/logs" -H "$AUTH" --data-urlencode "limit=200" --data-urlencode "search=error"
curl -sS -G "$UNITS/metrics" -H "$AUTH" --data-urlencode "window=1h" --data-urlencode "series=cpu,memory"
```

**TypeScript**

```ts
const logs = await call("GET", `${UNITS}/logs?limit=200&search=error`);
let cursor = logs.body.follow_cursor;
for (;;) {
  const page = await call("GET", `${UNITS}/logs?direction=forward&cursor=${encodeURIComponent(cursor)}`);
  for (const l of page.body.lines) console.log(l);
  cursor = page.body.follow_cursor ?? cursor;
  await new Promise((r) => setTimeout(r, page.body.poll_after_ms ?? 1000));
}
```

**Python**

```python
import time

_, logs = call("GET", f"{UNITS}/logs", params={"limit": 200, "search": "error"})
cursor = logs["follow_cursor"]
while True:
    _, page = call("GET", f"{UNITS}/logs", params={"direction": "forward", "cursor": cursor})
    for line in page["lines"]:
        print(line)
    cursor = page.get("follow_cursor") or cursor
    time.sleep(page.get("poll_after_ms", 1000) / 1000)
```


## Logs

| Parameter | Meaning |
|---|---|
| `unit_id` | one unit (live or gone); omit for all units merged by instant |
| `direction` | `backward` (default, newest first, how a panel opens) or `forward` (how a follow polls) |
| `cursor` | opaque; resumes exactly, with no duplicate and no gap |
| `limit`, `from`, `to` | page size and time window |
| `search` | case-insensitive substring |
| `container` | which container of the pod |

`lines` are always oldest first. To follow: read the first page backward, take `follow_cursor`, then poll `direction=forward&cursor=<follow_cursor>` every `poll_after_ms`. The platform agent's own lines are hidden unless you ask for them.

For a live socket instead of polling, `POST .../units/{unit_id}/logs/stream-ticket` mints a short-lived ticket for the stream, and `GET .../units/logs/download` exports a range as a file.

## Metrics

`GET .../units/metrics` returns CPU, memory and network over `window` of `1h`, `6h`, `24h`, `7d` or `30d`, always as 120 buckets. `series` is a comma-separated list of `cpu`, `memory`, `network_in`, `network_out` and `log_lines_archived`. Without `unit_id` each series has one aggregate line and a line for each of at most ten units (the highest), with `omitted_units` for the rest.

`GET .../units/count` is the unit-count series over time, and `GET .../units/{unit_id}/terminations` lists how a unit's containers ended.
