Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/dull-snakes-unite.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@e2b/python-sdk': minor
'e2b': minor
'@e2b/cli': minor
---

Expose sandbox metrics
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Installing beta SDKs

To use features like [sandbox persistence](/docs/sandbox/persistence) and [metrics](/docs/sandbox/metrics), you need to install the beta version of the SDKs.
To use features like [sandbox persistence](/docs/sandbox/persistence) you need to install the beta version of the SDKs.

### Code Interpreter SDK

Expand Down
92 changes: 49 additions & 43 deletions apps/web/src/app/(docs)/docs/sandbox/metrics/page.mdx
Original file line number Diff line number Diff line change
@@ -1,22 +1,10 @@
# Sandbox metrics

<Note>
This feature is in a private beta.
</Note>

The sandbox metrics allows you to get information about the sandbox's CPU and memory usage.

## Installation

To get sandbox metrics, you need to install the beta version of the SDKs and CLI:

- [Installing beta SDKs](/docs/sandbox/installing-beta-sdks)

- [Installing beta CLI](/docs/cli#beta-cli)
The sandbox metrics allows you to get information about the sandbox's CPU, memory and disk usage.

## Getting sandbox metrics
Getting the metrics of a sandbox returns an array of timestamped metrics containing CPU and memory usage information.
The metrics are collected at the start of the sandbox, then every 2 seconds, and finally right before the sandbox is deleted.
Getting the metrics of a sandbox returns an array of timestamped metrics containing CPU, memory and disk usage information.
The metrics are collected every 5 seconds.

### Getting sandbox metrics using the SDKs

Expand All @@ -27,59 +15,75 @@ import { Sandbox } from '@e2b/code-interpreter'
const sbx = await Sandbox.create()
console.log('Sandbox created', sbx.sandboxId)

// Wait for a few seconds to collect some metrics
await new Promise((resolve) => setTimeout(resolve, 10_000))

const metrics = await sbx.getMetrics() // $HighlightLine

// You can also get the metrics by sandbox ID:
// const metrics = await Sandbox.getMetrics(sbx.sandboxId)

console.log('Sandbox metrics:', metrics)

// Sandbox metrics:
// [
// {
// timestamp: 2025-07-28T08:04:05.000Z,
// cpuUsedPct: 20.33,
// cpuCount: 2,
// cpuUsedPct: 50.05,
// memTotalMiB: 484,
// memUsedMiB: 37,
// timestamp: '2025-01-23T23:44:12.222Z'
// memUsed: 32681984, // in bytes
// memTotal: 507592704, // in bytes
// diskUsed: 1514856448, // in bytes
// diskTotal: 2573185024 // in bytes
// },
// {
// timestamp: 2025-07-28T08:04:10.000Z,
// cpuUsedPct: 0.2,
// cpuCount: 2,
// cpuUsedPct: 4.5,
// memTotalMiB: 484,
// memUsedMiB: 37,
// timestamp: '2025-01-23T23:44:13.220Z'
// memUsed: 33316864, // in bytes
// memTotal: 507592704, // in bytes
// diskUsed: 1514856448, // in bytes
// diskTotal: 2573185024 // in bytes
// }
// ]

```
```python
from time import sleep
from e2b_code_interpreter import Sandbox

sbx = Sandbox()
print('Sandbox created', sbx.sandbox_id)

# Wait for a few seconds to collect some metrics
sleep(10)

metrics = sbx.get_metrics() # $HighlightLine

# You can also get the metrics by sandbox ID:
# metrics = Sandbox.get_metrics(sbx.sandbox_id)

print('Sandbox metrics', metrics)

# Sandbox metrics
# [
# SandboxMetrics(timestamp=datetime.datetime(
# 2025, 1, 23, 23, 58, 42, 84050, tzinfo=tzutc()),
# cpu_count=2,
# cpu_used_pct=50.07,
# mem_total_mib=484
# mem_used_mib=37,
# ),
# SandboxMetrics(timestamp=datetime.datetime(
# 2025, 1, 23, 23, 58, 44, 84845, tzinfo=tzutc()),
# cpu_count=2,
# cpu_used_pct=4.75,
# mem_total_mib=484
# mem_used_mib=38,
# ),
# SandboxMetric(
# cpu_count=2,
# cpu_used_pct=13.97,
# disk_total=2573185024, # in bytes
# disk_used=1514856448, # in bytes
# mem_total=507592704, # in bytes
# mem_used=30588928, # in bytes
# timestamp=datetime.datetime(2025, 7, 28, 8, 8, 15, tzinfo=tzutc()),
# ),
# SandboxMetric(
# cpu_count=2,
# cpu_used_pct=0.1,
# disk_total=2573185024, # in bytes
# disk_used=1514856448, # in bytes
# mem_total=507592704, # in bytes
# mem_used=31084544, # in bytes
# timestamp=datetime.datetime(2025, 7, 28, 8, 8, 20, tzinfo=tzutc()),
# ),
# ]
```
</CodeGroup>
Expand All @@ -91,11 +95,13 @@ e2b sandbox metrics <sandbox_id> # $HighlightLine

# Metrics for sandbox <sandbox_id>
#
# [2025-01-23 00:58:58.829Z] { cpuCount: 2, cpuUsedPct: 50.21, logger: '', memTotalMiB: 484, memUsedMiB: 38, timestamp: '2025-01-23T00:58:58.829638869Z' }
# [2025-01-23 00:59:03.814Z] { cpuCount: 2, cpuUsedPct: 5.16, logger: '', memTotalMiB: 484, memUsedMiB: 37, timestamp: '2025-01-23T00:59:03.814028031Z' }
# [2025-01-23 00:59:08.815Z] { cpuCount: 2, cpuUsedPct: 1.6, logger: '', memTotalMiB: 484, memUsedMiB: 37, timestamp: '2025-01-23T00:59:08.815933749Z' }
# [2025-07-25 14:05:55Z] CPU: 8.27% / 2 Cores | Memory: 31 / 484 MiB | Disk: 1445 / 2453 MiB
# [2025-07-25 14:06:00Z] CPU: 0.5% / 2 Cores | Memory: 32 / 484 MiB | Disk: 1445 / 2453 MiB
# [2025-07-25 14:06:05Z] CPU: 0.1% / 2 Cores | Memory: 32 / 484 MiB | Disk: 1445 / 2453 MiB
# [2025-07-25 14:06:10Z] CPU: 0.3% / 2 Cores | Memory: 32 / 484 MiB | Disk: 1445 / 2453 MiB
```
</CodeGroup>

## Limitations while in beta
- It may take a second or more to get the metrics after the sandbox is created. Until the logs are collected from the sandbox, you will get an empty array.
<Note>
It may take a second or more to get the first metrics after the sandbox is created. Until the first metrics are collected from the sandbox, you will get an empty array.
</Note>
2 changes: 2 additions & 0 deletions packages/cli/src/commands/sandbox/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { listCommand } from './list'
import { killCommand } from './kill'
import { spawnCommand } from './spawn'
import { logsCommand } from './logs'
import { metricsCommand } from './metrics'

export const sandboxCommand = new commander.Command('sandbox')
.description('work with sandboxes')
Expand All @@ -14,3 +15,4 @@ export const sandboxCommand = new commander.Command('sandbox')
.addCommand(killCommand)
.addCommand(spawnCommand)
.addCommand(logsCommand)
.addCommand(metricsCommand)
71 changes: 11 additions & 60 deletions packages/cli/src/commands/sandbox/logs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,45 +8,7 @@ import { asBold, asTimestamp, withUnderline } from 'src/utils/format'
import { listSandboxes } from './list'
import { wait } from 'src/utils/wait'
import { handleE2BRequestError } from '../../utils/errors'

const maxRuntime = 24 * 60 * 60 * 1000 // 24 hours in milliseconds

function getShortID(sandboxID: string) {
return sandboxID.split('-')[0]
}

function waitForSandboxEnd(sandboxID: string) {
let isRunning = true

async function monitor() {
const startTime = new Date().getTime()

// eslint-disable-next-line no-constant-condition
while (true) {
const currentTime = new Date().getTime()
const elapsedTime = currentTime - startTime // Time elapsed in milliseconds

// Check if 24 hours (in milliseconds) have passed
if (elapsedTime >= maxRuntime) {
break
}

const response = await listSandboxes()
const sandbox = response.find(
(s) => s.sandboxID === getShortID(sandboxID)
)
if (!sandbox) {
isRunning = false
break
}
await wait(5000)
}
}

monitor()

return () => isRunning
}
import { waitForSandboxEnd, formatEnum, Format, getShortID } from './utils'

enum LogLevel {
DEBUG = 'DEBUG',
Expand Down Expand Up @@ -76,17 +38,6 @@ function isLevelIncluded(level: LogLevel, allowedLevel?: LogLevel) {
}
}

function formatEnum(e: { [key: string]: string }) {
return Object.values(e)
.map((level) => asBold(level))
.join(', ')
}

enum LogFormat {
JSON = 'json',
PRETTY = 'pretty',
}

function cleanLogger(logger?: string) {
if (!logger) {
return ''
Expand All @@ -112,8 +63,8 @@ export const logsCommand = new commander.Command('logs')
.option('-f, --follow', 'keep streaming logs until the sandbox is closed')
.option(
'--format <format>',
`specify format for printing logs (${formatEnum(LogFormat)})`,
LogFormat.PRETTY
`specify format for printing logs (${formatEnum(Format)})`,
Format.PRETTY
)
.option(
'--loggers [loggers]',
Expand All @@ -126,7 +77,7 @@ export const logsCommand = new commander.Command('logs')
opts?: {
level: string
follow: boolean
format: LogFormat
format: Format
loggers?: string[]
}
) => {
Expand All @@ -136,8 +87,8 @@ export const logsCommand = new commander.Command('logs')
throw new Error(`Invalid log level: ${level}`)
}

const format = opts?.format.toLowerCase() as LogFormat | undefined
if (format && !Object.values(LogFormat).includes(format)) {
const format = opts?.format.toLowerCase() as Format | undefined
if (format && !Object.values(Format).includes(format)) {
throw new Error(`Invalid log format: ${format}`)
}

Expand All @@ -149,7 +100,7 @@ export const logsCommand = new commander.Command('logs')
let isFirstRun = true
let firstLogsPrinted = false

if (format === LogFormat.PRETTY) {
if (format === Format.PRETTY) {
console.log(`\nLogs for sandbox ${asBold(sandboxID)}:`)
}

Expand Down Expand Up @@ -178,7 +129,7 @@ export const logsCommand = new commander.Command('logs')
const isRunning = await isRunningPromise

if (!isRunning && logs.length === 0 && isFirstRun) {
if (format === LogFormat.PRETTY) {
if (format === Format.PRETTY) {
console.log(
`\nStopped printing logs — sandbox ${withUnderline(
'not found'
Expand All @@ -189,7 +140,7 @@ export const logsCommand = new commander.Command('logs')
}

if (!isRunning) {
if (format === LogFormat.PRETTY) {
if (format === Format.PRETTY) {
console.log(
`\nStopped printing logs — sandbox is ${withUnderline(
'closed'
Expand Down Expand Up @@ -219,7 +170,7 @@ function printLog(
timestamp: string,
line: string,
allowedLevel: LogLevel | undefined,
format: LogFormat | undefined,
format: Format | undefined,
allowedLoggers?: string[] | undefined
) {
const log = JSON.parse(line)
Expand Down Expand Up @@ -266,7 +217,7 @@ function printLog(
delete log['envID']
delete log['sandboxID']

if (format === LogFormat.JSON) {
if (format === Format.JSON) {
console.log(
JSON.stringify({
timestamp: new Date(timestamp).toISOString(),
Expand Down
Loading