# Observability
URL: /docs/core/observability

SandboxHooks lifecycle callbacks and OpenTelemetry tracing with @alineo-labs/otel.



## SandboxHooks [#sandboxhooks]

`SandboxHooks` is a set of lifecycle callbacks you can pass to `client.sandbox()` to observe what's happening during a run. All hooks are optional.

```ts
import type { SandboxHooks } from "@alineo-labs/sandbox";

const hooks: SandboxHooks = {
  onSandboxCreated(sandboxId, name) {
    console.log(`[sandbox] created: ${name} (${sandboxId})`);
  },
  onExecStart(sandboxId, seq, cmd) {
    console.log(`[exec:${seq}] start: ${cmd.slice(0, 80)}`);
  },
  onExecComplete(sandboxId, seq, result) {
    console.log(`[exec:${seq}] done: exit ${result.exitCode}`);
  },
  onCheckpoint(sandboxId, snapshotId, name) {
    console.log(`[checkpoint] ${name ?? ""} → ${snapshotId}`);
  },
  onSandboxClosed(sandboxId) {
    console.log(`[sandbox] closed: ${sandboxId}`);
  },
  onSandboxFailed(sandboxId, error) {
    console.error(`[sandbox] failed: ${error.message}`);
  },
};

const sb = await client.sandbox({
  image: "ubuntu:22.04",
  resources: { cpu: "500m", memory: "256Mi" },
  hooks,
});
```

## Hook signatures [#hook-signatures]

| Hook               | When                            | Arguments                        |
| ------------------ | ------------------------------- | -------------------------------- |
| `onSandboxCreated` | Container created and running   | `(sandboxId, name)`              |
| `onExecStart`      | `sb.exec()` called              | `(sandboxId, seq, cmd)`          |
| `onExecComplete`   | exec finished                   | `(sandboxId, seq, ExecResult)`   |
| `onCheckpoint`     | `sb.checkpoint()` completed     | `(sandboxId, snapshotId, name?)` |
| `onSandboxClosed`  | `sb.close()` completed          | `(sandboxId)`                    |
| `onSandboxFailed`  | sandbox creation or boot failed | `(sandboxId, error)`             |

## OpenTelemetry with @alineo-labs/otel [#opentelemetry-with-alineo-labsotel]

`@alineo-labs/otel` provides an `otelHooks()` factory that produces `SandboxHooks` emitting OTEL traces:

```bash
bun add @alineo-labs/otel @opentelemetry/api
```

```ts
import { otelHooks } from "@alineo-labs/otel";
import { trace } from "@opentelemetry/api";

const tracer = trace.getTracer("my-app");

const sb = await client.sandbox({
  image: "node:22",
  resources: { cpu: "500m", memory: "512Mi" },
  hooks: otelHooks(tracer),
});
```

### Span structure [#span-structure]

```
sandbox.run            ← root span (alineo.sandbox.id, alineo.sandbox.name)
  sandbox.exec         ← child per exec (alineo.exec.cmd, alineo.exec.seq, process.exit_code)
  sandbox.checkpoint   ← child per checkpoint (alineo.snapshot.id, alineo.checkpoint.name)
```

### OtelHooksOptions [#otelhooksoptions]

| Option           | Type      | Default | Description                                     |
| ---------------- | --------- | ------- | ----------------------------------------------- |
| `recordExitCode` | `boolean` | `true`  | Add `process.exit_code` attribute to exec spans |

```ts
const hooks = otelHooks(tracer, { recordExitCode: false });
```

### Setting up OTEL [#setting-up-otel]

`otelHooks` uses the `@opentelemetry/api` package — it integrates with whatever OTEL SDK you have configured in your application. Example with the Node SDK:

```ts
import { NodeSDK } from "@opentelemetry/sdk-node";
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
import { trace } from "@opentelemetry/api";
import { otelHooks } from "@alineo-labs/otel";

const sdk = new NodeSDK({
  traceExporter: new OTLPTraceExporter({ url: "http://localhost:4318/v1/traces" }),
});
sdk.start();

const tracer = trace.getTracer("my-app");

const sb = await client.sandbox({
  image: "ubuntu:22.04",
  resources: { cpu: "500m", memory: "256Mi" },
  hooks: otelHooks(tracer),
});
```
