Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 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
90 changes: 48 additions & 42 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.
Comment thread
ValentaTomas marked this conversation as resolved.
Outdated

## 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)

## 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