# Metrics

Read CPU, memory and disk usage for a sandbox over a time range.

This page reads metrics through the TypeScript or Python SDK. The same
samples are charted per sandbox on the dashboard at
`dashboard.impello.ai/sandboxes/<id>/monitoring`. Every example reads
`IMPELLO_API_KEY` from the environment; get a key at
[dashboard.impello.ai/keys](https://dashboard.impello.ai/keys).

## Get metrics

`sandbox.getMetrics()` returns a list of samples, each stamped with the time
it was taken. With no arguments the range runs from the sandbox's start to
now.

<CodeTabs>
<Tab label="TypeScript">

```ts
import { Sandbox } from '@impello/sdk'

// Reads IMPELLO_API_KEY from the environment.
const sandbox = await Sandbox.create('base')

const metrics = await sandbox.getMetrics()
if (metrics.length === 0) {
  console.log('no samples yet')
}
for (const m of metrics) {
  console.log(
    m.timestamp.toISOString(),
    `cpu ${m.cpuUsedPct}% of ${m.cpuCount}`,
    `mem ${m.memUsed}/${m.memTotal} B`,
    `disk ${m.diskUsed}/${m.diskTotal} B`,
  )
}

await sandbox.kill()
```

</Tab>
<Tab label="Python">

```python
from impello import Sandbox

# Reads IMPELLO_API_KEY from the environment.
sandbox = Sandbox.create("base")

metrics = sandbox.get_metrics()
if len(metrics) == 0:
    print("no samples yet")
for m in metrics:
    print(
        m.timestamp.isoformat(),
        f"cpu {m.cpu_used_pct}% of {m.cpu_count}",
        f"mem {m.mem_used}/{m.mem_total} B",
        f"disk {m.disk_used}/{m.disk_total} B",
    )

sandbox.kill()
```

</Tab>
</CodeTabs>

The list can come back empty, so check its length before you read the last
entry. In Python, `AsyncSandbox.get_metrics()` takes the same arguments and
is awaited.

You can also read metrics for a sandbox you did not create in this process.
Pass the ID to the static form; the sandbox must still exist.

<CodeTabs>
<Tab label="TypeScript">

```ts
import { Sandbox } from '@impello/sdk'

const metrics = await Sandbox.getMetrics(process.env.SANDBOX_ID!)
console.log(metrics.length)
```

</Tab>
<Tab label="Python">

```python
import os

from impello import Sandbox

metrics = Sandbox.get_metrics(os.environ["SANDBOX_ID"])
print(len(metrics))
```

</Tab>
</CodeTabs>

## Fields

Each sample is a `SandboxMetrics` object in both SDKs. TypeScript uses
camelCase, Python uses snake_case. Memory and disk values are bytes.

<div className="docs-table-wrap">
<table>
  <thead>
    <tr><th>TypeScript</th><th>Python</th><th>Type</th><th>Meaning</th></tr>
  </thead>
  <tbody>
    <tr><td>`timestamp`</td><td>`timestamp`</td><td>`Date` / `datetime`</td><td>When the sample was taken.</td></tr>
    <tr><td>`cpuUsedPct`</td><td>`cpu_used_pct`</td><td>`number` / `float`</td><td>CPU usage in percent.</td></tr>
    <tr><td>`cpuCount`</td><td>`cpu_count`</td><td>`number` / `int`</td><td>Number of CPU cores.</td></tr>
    <tr><td>`memUsed`</td><td>`mem_used`</td><td>`number` / `int`</td><td>Memory used, in bytes.</td></tr>
    <tr><td>`memTotal`</td><td>`mem_total`</td><td>`number` / `int`</td><td>Total memory, in bytes.</td></tr>
    <tr><td>`memCache`</td><td>`mem_cache`</td><td>`number` / `int`</td><td>Cached memory (page cache), in bytes.</td></tr>
    <tr><td>`diskUsed`</td><td>`disk_used`</td><td>`number` / `int`</td><td>Disk used, in bytes.</td></tr>
    <tr><td>`diskTotal`</td><td>`disk_total`</td><td>`number` / `int`</td><td>Total disk space, in bytes.</td></tr>
  </tbody>
</table>
</div>

`cpuCount` and `memTotal` describe the size of the sandbox rather than its
load. [`getInfo()`](/docs/sandbox) reports the sandbox's configured size as
`cpuCount` and `memoryMB` (`cpu_count` and `memory_mb` in Python), where
`memoryMB` is in MiB. The `memTotal` here is the memory the sandbox itself
reports, in bytes. [Limits and pricing](/docs/limits-and-pricing) lists the
sizes a template can be built at.

## Time range

Pass `start` and `end` to narrow the range. Both are optional: `start`
defaults to when the sandbox started and `end` defaults to the current time.
TypeScript takes `Date` objects on the options argument; Python takes
`datetime` objects as positional or keyword arguments. Both SDKs send whole
seconds: TypeScript rounds to the nearest second, Python truncates.

<CodeTabs>
<Tab label="TypeScript">

```ts
import { Sandbox } from '@impello/sdk'

const sandbox = await Sandbox.create('base')

const end = new Date()
const start = new Date(end.getTime() - 5 * 60 * 1000)

const recent = await sandbox.getMetrics({ start, end })
const peak = Math.max(...recent.map((m) => m.memUsed), 0)
console.log(`peak memory in the last 5 min: ${peak} B`)

await sandbox.kill()
```

</Tab>
<Tab label="Python">

```python
from datetime import datetime, timedelta, timezone

from impello import Sandbox

sandbox = Sandbox.create("base")

end = datetime.now(timezone.utc)
start = end - timedelta(minutes=5)

recent = sandbox.get_metrics(start=start, end=end)
peak = max((m.mem_used for m in recent), default=0)
print(f"peak memory in the last 5 min: {peak} B")

sandbox.kill()
```

</Tab>
</CodeTabs>

<Callout kind="note">
In Python, pass timezone-aware `datetime` values. A naive `datetime` is read
in the process's local time zone, which shifts the range on any machine not
running in UTC.
</Callout>

The static form takes the same range. In TypeScript it is the second
argument, `Sandbox.getMetrics(sandboxId, { start, end })`; in Python the range
follows the ID, `Sandbox.get_metrics(sandbox_id, start=start, end=end)`.

## Errors

The instance method checks the sandbox's version before it calls the API. A
sandbox built from a template that is too old throws `TemplateError`
(`TemplateException` in Python) with the message "You need to update the
template to use the new SDK." Rebuild the template to fix it; see
[Build a custom template](/docs/templates/build).

A slightly newer but still old template does not throw. Instead the SDK
reports that disk metrics are not supported in that version of the sandbox and
that the template should be rebuilt. Python logs that warning through the
standard `logging` module, on the SDK's own loggers under `impello`
(`impello.sandbox_sync.main` and `impello.sandbox_async.main`), so configuring
`logging.getLogger("impello")` catches it. TypeScript logs it only if you
passed a `logger` to `create` or `connect`, and is otherwise silent.

A sandbox that no longer exists throws `SandboxNotFoundError`
(`SandboxNotFoundException` in Python), from both the instance form and the
static form. A sandbox that was killed at its timeout no longer exists, so
read its metrics before the deadline or extend it first; see
[Timeouts](/docs/sandbox/timeouts). Every error class is listed on
[Errors](/docs/errors).

Next: [Timeouts](/docs/sandbox/timeouts).
