# How to Debug Code Running Inside microsandbox: A Complete Guide

> Debug code running inside microsandbox with live log streaming. Use --log-level debug and msb logs -f or log_stream API to monitor guest execution and runtime traces in real time.

- Repository: [Super Rad Company/microsandbox](https://github.com/superradcompany/microsandbox)
- Tags: how-to-guide
- Published: 2026-08-20

---

**Set `--log-level debug` via the CLI or SDK, then stream live logs with `msb logs -f` or the `log_stream` API to observe guest execution, runtime traces, and kernel messages in real time.**

The **microsandbox** project provides multiple debugging layers for both host-side tooling and guest code running inside micro-VMs. Whether you're using the CLI, Rust SDK, Python SDK, Node-TS SDK, or Go SDK, the same core observability primitives apply: configurable log levels, structured tracing, and programmatic log streaming. This guide walks through each debugging mechanism based on the actual source implementation in the [superradcompany/microsandbox](https://github.com/superradcompany/microsandbox) repository.

## Understanding the Log-Level Architecture

Every debugging session starts with **log-level configuration**. This flows from the CLI/SDK through to the runtime, ultimately controlling what gets emitted from three sources: the **exec** process (your guest code), the **runtime** (the microsandbox host), and the **kernel** (the underlying VM).

### CLI Entry Point: Parsing `--log-level`

The `msb run` command accepts a `--log-level` flag that sets the baseline verbosity for all subsequent operations.

```rust
// crates/cli/lib/commands/run.rs#L45-L48
// The run command struct includes log-level parsing:
#[derive(Parser)]
pub struct RunArgs {
    #[arg(long, value_enum, default_value = "info")]
    pub log_level: LogLevel,
}

```

This value propagates through to the runtime via the spawn module.

### Runtime Translation: From Enum to CLI Flag

In [`crates/runtime/lib/spawn.rs`](https://github.com/superradcompany/microsandbox/blob/main/crates/runtime/lib/spawn.rs), the `SandboxLogLevel` stored in the sandbox spec is converted into runtime flags:

```rust
// crates/runtime/lib/spawn.rs#L2824-L2830
fn sandbox_log_level_cli_flag(level: SandboxLogLevel) -> &'static str {
    match level {
        SandboxLogLevel::Error => "--error",
        SandboxLogLevel::Warn => "--warn",
        SandboxLogLevel::Info => "--info",
        SandboxLogLevel::Debug => "--debug",
        SandboxLogLevel::Trace => "--trace",
    }
}

```

This ensures the sandbox process launches with the correct verbosity flag at **line 2824-2830** of the spawn implementation.

### SDK Propagation: Builder Pattern Across Languages

All SDKs expose a `log_level` method that sets this value in the sandbox specification:

| SDK | Method | Source Location |
|-----|--------|-----------------|
| **Rust** | `builder.log_level(LogLevel::Debug)` | [`sdk/rust/lib/sandbox/builder.rs#L375-L382`](https://github.com/superradcompany/microsandbox/blob/main/sdk/rust/lib/sandbox/builder.rs#L375-L382) |
| **Python** | `builder.log_level("debug")` | [`sdk/python/src/helpers.rs#L485-L499`](https://github.com/superradcompany/microsandbox/blob/main/sdk/python/src/helpers.rs#L485-L499) |
| **Node-TS** | `sandboxBuilder.logLevel("debug")` | [[`sdk/node-ts/native/sandbox_builder.rs`](https://github.com/superradcompany/microsandbox/blob/main/sdk/node-ts/native/sandbox_builder.rs)](https://github.com/superradcompany/microsandbox/blob/main/sdk/node-ts/native/sandbox_builder.rs) |
| **Go** | `builder.WithLogLevel(microsandbox.LogLevelDebug)` | [[`sdk/go/native/src/lib.rs`](https://github.com/superradcompany/microsandbox/blob/main/sdk/go/native/src/lib.rs)](https://github.com/superradcompany/microsandbox/blob/main/sdk/go/native/src/lib.rs) |

The Rust SDK's builder stores the log level directly into the sandbox spec with type conversion:

```rust
// sdk/rust/lib/sandbox/builder.rs#L375-L382
pub fn log_level(mut self, level: LogLevel) -> Self {
    self.spec.log_level = Some(match level {
        LogLevel::Error => SandboxLogLevel::Error,
        LogLevel::Warn => SandboxLogLevel::Warn,
        LogLevel::Info => SandboxLogLevel::Info,
        LogLevel::Debug => SandboxLogLevel::Debug,
        LogLevel::Trace => SandboxLogLevel::Trace,
    });
    self
}

```

## Reading Sandbox Logs: CLI and Programmatic Access

Once a sandbox is running, its output streams are captured to the `logs/` directory. You can access these through either the CLI or SDK APIs.

### CLI: The `msb logs` Command

The `msb logs` subcommand aggregates and streams logs from all sources:

```bash

# Read all logs for a sandbox

msb logs my-sandbox

# Follow logs in real time (like tail -f)

msb logs -f my-sandbox

```

The tracing initialization that enables this happens in:

```rust
// crates/cli/lib/log_args.rs#L48-L50
// Sets up the subscriber with the requested level
let subscriber = tracing_subscriber::fmt()
    .with_max_level(log_level)
    .finish();

```

### SDK: Programmatic Log Streaming

For debugging automation, use the `log_stream` API. This provides **cursor-based resumption** — you can stop and restart log consumption without losing your place.

The Rust SDK implementation demonstrates full usage:

```rust
use microsandbox::{
    Builder, 
    LogLevel, 
    logs::{LogStreamOptions, LogStreamStart}
};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    // 1. Build with debug logging enabled
    let sandbox = Builder::new("debug-demo")
        .log_level(LogLevel::Debug)
        .build()
        .await?;
    
    // 2. Start the sandbox
    sandbox.start().await?;
    
    // 3. Stream logs with cursor support
    let opts = LogStreamOptions {
        start: LogStreamStart::Now,  // or FromCursor(cursor) to resume
        include_rotated: true,         // include archived log files
        ..Default::default()
    };
    
    let mut stream = sandbox.log_stream(&opts).await?;
    while let Some(entry) = stream.next().await {
        println!("[{}] {}", entry.timestamp, entry.message);
    }
    
    Ok(())
}

```

This pattern is validated in the test suite at [`sdk/rust/tests/log_stream.rs#L90-L124`](https://github.com/superradcompany/microsandbox/blob/main/sdk/rust/tests/log_stream.rs#L90-L124).

### Python SDK: Equivalent Approach

```python
from microsandbox import SandboxBuilder, LogLevel

builder = SandboxBuilder("py-debug")
builder.log_level(LogLevel.Debug)
sandbox = builder.build()

sandbox.start()

# Follow logs with automatic reconnection

for entry in sandbox.log_stream(
    follow=True,
    include_rotated=True,
    start="now"  # or provide cursor string to resume

):
    print(f"[{entry.timestamp}] {entry.source}: {entry.message}")

```

## Debugging the Guest Execution Lifecycle

The runtime emits detailed **tracing events** at key lifecycle points. With `LogLevel::Debug` or `LogLevel::Trace`, you'll see:

| Event | Source File | Description |
|-------|-------------|-------------|
| Sandbox spawn | [`crates/runtime/lib/spawn.rs#L2423-L2425`](https://github.com/superradcompany/microsandbox/blob/main/crates/runtime/lib/spawn.rs#L2423-L2425) | Command assembly and process launch |
| Process signals | [`crates/runtime/lib/runtime/handle.rs#L136-L148`](https://github.com/superradcompany/microsandbox/blob/main/crates/runtime/lib/runtime/handle.rs#L136-L148) | SIGTERM, SIGKILL delivery and handling |
| Sandbox start confirmation | [`sdk/rust/lib/sandbox/mod.rs#L813-L815`](https://github.com/superradcompany/microsandbox/blob/main/sdk/rust/lib/sandbox/mod.rs#L813-L815) | Successful initialization logging |

The handle module demonstrates debug-level signal tracing:

```rust
// crates/runtime/lib/runtime/handle.rs#L136-L148
tracing::debug!(
    sandbox_id = %self.id,
    pid = %child_pid,
    signal = ?sig,
    "delivering signal to sandbox process"
);

```

## Step-by-Step Debugging Workflow

Follow this sequence when troubleshooting code inside microsandbox:

1. **Enable debug logging at build time**
   - CLI: `msb --log-level debug run <spec>`
   - SDK: `builder.log_level(LogLevel::Debug)` before calling `build()`

2. **Start the sandbox and capture initial output**
   - The first lines of `exec.log` show environment setup and command execution

3. **Stream logs during execution**
   - CLI: `msb logs -f <sandbox>` in a separate terminal
   - SDK: Use `log_stream()` with `follow=True` for programmatic monitoring

4. **Inspect exit status and rotated logs**
   - Check `msb status <sandbox>` or the API return value
   - Use `include_rotated=True` in log options to see earlier iterations

5. **Escalate to trace level if needed**
   - `LogLevel::Trace` includes kernel-level VMM messages (very verbose, use sparingly)

## Key Source Files for Debugging Reference

| Component | File | Purpose |
|-----------|------|---------|
| CLI command handling | [[`crates/cli/lib/commands/run.rs`](https://github.com/superradcompany/microsandbox/blob/main/crates/cli/lib/commands/run.rs)](https://github.com/superradcompany/microsandbox/blob/main/crates/cli/lib/commands/run.rs) | Parses `--log-level` and other debug flags |
| Tracing initialization | [[`crates/cli/lib/log_args.rs`](https://github.com/superradcompany/microsandbox/blob/main/crates/cli/lib/log_args.rs)](https://github.com/superradcompany/microsandbox/blob/main/crates/cli/lib/log_args.rs) | Configures subscriber with requested level |
| Sandbox spawning | [[`crates/runtime/lib/spawn.rs`](https://github.com/superradcompany/microsandbox/blob/main/crates/runtime/lib/spawn.rs)](https://github.com/superradcompany/microsandbox/blob/main/crates/runtime/lib/spawn.rs) | Translates `SandboxLogLevel` to runtime flags |
| Process lifecycle | [[`crates/runtime/lib/runtime/handle.rs`](https://github.com/superradcompany/microsandbox/blob/main/crates/runtime/lib/runtime/handle.rs)](https://github.com/superradcompany/microsandbox/blob/main/crates/runtime/lib/runtime/handle.rs) | Signal handling with debug traces |
| Rust SDK builder | [[`sdk/rust/lib/sandbox/builder.rs`](https://github.com/superradcompany/microsandbox/blob/main/sdk/rust/lib/sandbox/builder.rs)](https://github.com/superradcompany/microsandbox/blob/main/sdk/rust/lib/sandbox/builder.rs) | Stores log level in specification |
| Log streaming API | [[`sdk/rust/lib/sandbox/mod.rs`](https://github.com/superradcompany/microsandbox/blob/main/sdk/rust/lib/sandbox/mod.rs)](https://github.com/superradcompany/microsandbox/blob/main/sdk/rust/lib/sandbox/mod.rs) | Implements `log_stream()` method |
| Streaming tests | [[`sdk/rust/tests/log_stream.rs`](https://github.com/superradcompany/microsandbox/blob/main/sdk/rust/tests/log_stream.rs)](https://github.com/superradcompany/microsandbox/blob/main/sdk/rust/tests/log_stream.rs) | Reference implementation for cursor handling |
| Python bindings | [[`sdk/python/src/helpers.rs`](https://github.com/superradcompany/microsandbox/blob/main/sdk/python/src/helpers.rs)](https://github.com/superradcompany/microsandbox/blob/main/sdk/python/src/helpers.rs) | Parses `log_level` from Python to Rust |

## Summary

- **Log levels flow uniformly** from CLI/SDK → spec → runtime flags → tracing output
- **`--log-level debug`** is the primary switch for troubleshooting guest execution
- **`msb logs -f`** and `log_stream()` provide live, resumable access to sandbox output
- **Three log sources** exist: `exec.log` (guest), `runtime.log` (host), and `kernel.log` (VMM)
- **Cursor-based streaming** lets you resume log consumption across disconnections

## Frequently Asked Questions

### What's the difference between host logging and sandbox logging?

Host logging (controlled by `--log-level` on the CLI) traces the **microsandbox tooling itself** — command parsing, spec building, and runtime coordination. Sandbox logging (set via `builder.log_level()`) controls what the **guest VM and its hypervisor** emit. Both use the same verbosity levels but target different components. You typically want host-level `Debug` for troubleshooting connection issues, and sandbox-level `Debug` for guest code problems.

### Can I change the log level of a running sandbox?

No — the log level is **fixed at sandbox creation time** when the spec is built. This is enforced by the spawn module at [`crates/runtime/lib/spawn.rs#L2423-L2425`](https://github.com/superradcompany/microsandbox/blob/main/crates/runtime/lib/spawn.rs#L2423-L2425). To change verbosity, you must stop the sandbox and recreate it with a different `log_level` setting.

### How do I debug a sandbox that crashes immediately?

Use the **cursor-based log streaming** with `include_rotated=True`. Even if the sandbox terminates before you attach, the logs are preserved in the `logs/` directory. Retrieve them with `msb logs <sandbox>` or resume from the start with `LogStreamStart::Beginning` in the SDK. Also check the **exit status** via `msb status` — non-zero codes indicate guest process failure versus runtime errors.

### What do I do if trace output is too noisy?

Filter by **log source** after collection. The `LogEntry` struct returned by `log_stream()` includes a `source` field (`"exec"`, `"runtime"`, or `"kernel"`). You can programmatically filter these, or simply use `LogLevel::Debug` instead of `Trace` — Debug omits the most verbose kernel VMM messages while preserving guest stdout/stderr and runtime events.