apps/docs/src/content/docs/en/observability/otel-collection.mdx
import { TabItem, Tabs } from '@astrojs/starlight/components'
OpenTelemetry (OTEL) tracing allows you to monitor and debug your Daytona SDK operations by collecting distributed traces. This is particularly useful for understanding performance bottlenecks, debugging issues, and gaining visibility into your sandbox operations.
Daytona can collect traces, logs, and metrics directly from your sandboxes. This provides complete observability across your entire Daytona environment, from SDK calls to sandbox runtime behavior.
When sandbox telemetry is enabled, the following data is collected:
Metrics:
daytona.sandbox.cpu.utilization - CPU usage percentage (0-100%)daytona.sandbox.cpu.limit - CPU cores limitdaytona.sandbox.memory.utilization - Memory usage percentage (0-100%)daytona.sandbox.memory.usage - Memory used in bytesdaytona.sandbox.memory.limit - Memory limit in bytesdaytona.sandbox.filesystem.utilization - Disk usage percentage (0-100%)daytona.sandbox.filesystem.usage - Disk space used in bytesdaytona.sandbox.filesystem.available - Disk space available in bytesdaytona.sandbox.filesystem.total - Total disk space in bytesTraces:
Logs:
Logs, traces, and metrics collected from sandboxes can be viewed directly in the Daytona Dashboard. Open the Sandbox Details sheet for any sandbox and use the Logs, Traces, and Metrics tabs to inspect the collected telemetry data.
:::note Daytona retains sandbox telemetry data for 3 days. If you need to keep the data for longer, it is recommended that you connect your own OTLP-compatible collector using the sandbox collection configuration. :::
:::tip Sandbox telemetry collection works independently from SDK tracing. You can enable one or both depending on your observability needs:
To enable telemetry collection from sandboxes:
https://otlp.nr-data.net)api-key = YOUR_API_KEY)Once configured, all sandboxes will automatically export their telemetry data to your specified OTLP endpoint.
All sandbox telemetry is automatically annotated with the following OTel resource attributes:
daytona_organization_id - The organization the sandbox belongs todaytona_region_id - The region the sandbox is running indaytona_snapshot - The snapshot used to create the sandboxYou can attach additional resource labels by setting the DAYTONA_SANDBOX_OTEL_EXTRA_LABELS environment variable on a sandbox. Labels are specified as a comma-separated list of key=value pairs:
DAYTONA_SANDBOX_OTEL_EXTRA_LABELS="team=backend,env=staging,app=my-service"
These labels are added as OTel resource attributes to all traces, logs, and metrics emitted by the sandbox. This is useful for filtering and grouping telemetry data by custom dimensions in your observability platform.
In addition to per-sandbox telemetry, Daytona exports organization-level resource metrics to your configured OTLP endpoint. These metrics are pushed every 60 seconds and provide a high-level view of resource consumption and quotas across your organization.
| Metric | Unit | Description |
|---|---|---|
daytona.sandbox.used_cpu | cpu cores | Total CPU currently consumed by active sandboxes |
daytona.sandbox.used_ram | GiB | Total memory currently consumed by active sandboxes |
daytona.sandbox.used_storage | GiB | Total disk currently consumed by sandboxes |
daytona.sandbox.total_cpu | cpu cores | Total CPU quota for the organization |
daytona.sandbox.total_ram | GiB | Total memory quota for the organization |
daytona.sandbox.total_storage | GiB | Total disk quota for the organization |
Each metric includes the following attributes for filtering and grouping:
organization.id (resource attribute) — The organization the metrics belong toregion.id (data point attribute) — The region the resource usage and quota applies to, since quotas are per-regionOrganization metrics are automatically exported when you have a sandbox collection endpoint configured. No additional setup is required — the same OTLP endpoint receives both sandbox telemetry and organization metrics.
When enabled, the Daytona SDK automatically instruments all SDK operations including:
Traces are exported using the OTLP (OpenTelemetry Protocol) format and can be sent to any OTLP-compatible backend such as New Relic, Jaeger, or Zipkin.
To enable OpenTelemetry tracing, pass the otelEnabled flag when initializing the Daytona client:
Alternatively, you can set the DAYTONA_OTEL_ENABLED environment variable to true instead of passing the configuration option:
export DAYTONA_OTEL_ENABLED=true
# Using async context manager (recommended)
async with Daytona(DaytonaConfig(otel_enabled=True)) as daytona:
sandbox = await daytona.create()
# All operations will be traced
# OpenTelemetry traces are flushed on close
```
Or without context manager:
```python
daytona = Daytona(DaytonaConfig(otel_enabled=True))
try:
sandbox = await daytona.create()
# All operations will be traced
finally:
await daytona.close() # Flushes traces
```
// Using async dispose (recommended)
await using daytona = new Daytona({ otelEnabled: true })
const sandbox = await daytona.create()
// All operations will be traced
// Traces are automatically flushed on dispose
```
Or with explicit disposal:
```typescript
const daytona = new Daytona({ otelEnabled: true })
try {
const sandbox = await daytona.create()
// All operations will be traced
} finally {
await daytona[Symbol.asyncDispose]() // Flushes traces
}
```
"github.com/daytonaio/daytona/libs/sdk-go/pkg/daytona"
"github.com/daytonaio/daytona/libs/sdk-go/pkg/types"
)
client, err := daytona.NewClientWithConfig(&types.DaytonaConfig{
OtelEnabled: true,
})
if err != nil {
log.Fatal(err)
}
defer client.Close(context.Background()) // Flushes traces
sandbox, err := client.Create(context.Background(), nil)
// All operations will be traced
```
config = Daytona::Config.new(otel_enabled: true)
daytona = Daytona::Daytona.new(config)
sandbox = daytona.create
# All operations will be traced
daytona.close # Flushes traces
```
Or with `ensure` block:
```ruby
daytona = Daytona::Daytona.new(
Daytona::Config.new(otel_enabled: true)
)
begin
sandbox = daytona.create
# All operations will be traced
ensure
daytona.close # Flushes traces
end
```
:::note
The legacy _experimental.otelEnabled option (and the DAYTONA_EXPERIMENTAL_OTEL_ENABLED env var) still work as deprecated aliases for backwards compatibility.
:::
The SDK uses standard OpenTelemetry environment variables for configuration. Set these before running your application:
# OTLP endpoint (without the /v1/traces path)
OTEL_EXPORTER_OTLP_ENDPOINT=https://otlp.nr-data.net:4317
# Authentication headers (format: key1=value1,key2=value2)
OTEL_EXPORTER_OTLP_HEADERS="api-key=your-api-key-here"
OTEL_EXPORTER_OTLP_ENDPOINT=https://otlp.nr-data.net:4317
OTEL_EXPORTER_OTLP_HEADERS="api-key=YOUR_NEW_RELIC_LICENSE_KEY"
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
OTEL_EXPORTER_OTLP_ENDPOINT=https://otlp-gateway-prod-<region>.grafana.net/otlp
OTEL_EXPORTER_OTLP_HEADERS="Authorization=Basic <BASE64_ENCODED_CREDENTIALS>"
Setup: Go to Grafana Cloud Portal → Connections → Add new connection → Search for OpenTelemetry (OTLP) → Follow the wizard to create an access token. The endpoint and headers will be provided in the instrumentation instructions. See the Grafana dashboard example for detailed setup steps.
Here's a complete example showing how to use OpenTelemetry tracing with the Daytona SDK:
<Tabs syncKey="language"> <TabItem label="Python" icon="seti:python"> ```python import asyncio import os from daytona import Daytona, DaytonaConfig# Set OTEL configuration
os.environ["OTEL_EXPORTER_OTLP_ENDPOINT"] = "https://otlp.nr-data.net:4317"
os.environ["OTEL_EXPORTER_OTLP_HEADERS"] = "api-key=YOUR_API_KEY"
async def main():
# Initialize Daytona with OTEL enabled
async with Daytona(DaytonaConfig(otel_enabled=True)) as daytona:
# Create a sandbox - this operation will be traced
sandbox = await daytona.create()
print(f"Created sandbox: {sandbox.id}")
# Execute code - this operation will be traced
result = await sandbox.process.code_run(""
import numpy as np print(f"NumPy version: {np.version}") "") print(f"Execution result: {result.result}")
# Upload a file - this operation will be traced
await sandbox.fs.upload_file("local.txt", "/home/daytona/remote.txt")
# Delete sandbox - this operation will be traced
await daytona.delete(sandbox)
# Traces are automatically flushed when exiting the context manager
if __name__ == "__main__":
asyncio.run(main())
```
import { Daytona } from '@daytona/sdk'
async function main() {
// Initialize Daytona with OTEL enabled
await using daytona = new Daytona({ otelEnabled: true })
// Create a sandbox - this operation will be traced
const sandbox = await daytona.create()
console.log(`Created sandbox: ${sandbox.id}`)
// Execute code - this operation will be traced
const result = await sandbox.process.codeRun(`
import numpy as np
print(f"NumPy version: {np.version}")
) console.log(Execution result: ${result.result}`)
// Upload a file - this operation will be traced
await sandbox.fs.uploadFile('local.txt', '/home/daytona/remote.txt')
// Delete sandbox - this operation will be traced
await daytona.delete(sandbox)
// Traces are automatically flushed when the daytona instance is disposed
}
main().catch(console.error)
```
import (
"context"
"fmt"
"log"
"os"
"github.com/daytonaio/daytona/libs/sdk-go/pkg/daytona"
"github.com/daytonaio/daytona/libs/sdk-go/pkg/types"
)
func main() {
// Set OTEL configuration
os.Setenv("OTEL_EXPORTER_OTLP_ENDPOINT", "https://otlp.nr-data.net:4317")
os.Setenv("OTEL_EXPORTER_OTLP_HEADERS", "api-key=YOUR_API_KEY")
ctx := context.Background()
// Initialize Daytona with OTEL enabled
client, err := daytona.NewClientWithConfig(&types.DaytonaConfig{
OtelEnabled: true,
})
if err != nil {
log.Fatal(err)
}
defer client.Close(ctx) // Flushes traces on exit
// Create a sandbox - this operation will be traced
sandbox, err := client.Create(ctx, nil)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Created sandbox: %s\n", sandbox.ID)
// Execute code - this operation will be traced
result, err := sandbox.Process.CodeRun(ctx, &types.CodeRunParams{
Code: `
import numpy as np print(f"NumPy version: {np.version}") `, }) if err != nil { log.Fatal(err) } fmt.Printf("Execution result: %s\n", result.Result)
// Upload a file - this operation will be traced
err = sandbox.Fs.UploadFile(ctx, "local.txt", "/home/daytona/remote.txt")
if err != nil {
log.Fatal(err)
}
// Delete sandbox - this operation will be traced
err = client.Delete(ctx, sandbox, nil)
if err != nil {
log.Fatal(err)
}
// Traces are flushed when client.Close is called via defer
}
```
# Set OTEL configuration
ENV["OTEL_EXPORTER_OTLP_ENDPOINT"] = "https://otlp.nr-data.net:4317"
ENV["OTEL_EXPORTER_OTLP_HEADERS"] = "api-key=YOUR_API_KEY"
# Initialize Daytona with OTEL enabled
config = Daytona::Config.new(otel_enabled: true)
daytona = Daytona::Daytona.new(config)
begin
# Create a sandbox - this operation will be traced
sandbox = daytona.create
puts "Created sandbox: #{sandbox.id}"
# Execute code - this operation will be traced
result = sandbox.process.code_run("
import numpy as np print(f'NumPy version: {np.version}') ") puts "Execution result: #{result.result}"
# Upload a file - this operation will be traced
sandbox.fs.upload_file("local.txt", "/home/daytona/remote.txt")
# Delete sandbox - this operation will be traced
daytona.delete(sandbox)
ensure
daytona.close # Flushes traces
end
```
The Daytona SDK automatically instruments the following operations:
create() - Sandbox creation and initializationget() - Retrieving sandbox instanceslist() - Listing sandboxesstart() - Starting sandboxesstop() - Stopping sandboxesdelete() - Deleting sandboxesEach trace includes valuable metadata such as:
Traces not appearing:
otelEnabled: true is set in the configurationConnection refused:
Authentication errors:
OTEL_EXPORTER_OTLP_HEADERS format is correct (key=value pairs)async with (Python), await using (TypeScript), defer client.Close() (Go), or ensure daytona.close (Ruby) to ensure traces are properly flushed