How to Store Claude Code or Codex Traces in Local DuckLake
Run the duckdb-otlp server image locally and point Claude Code or Codex at it with OTLP/HTTP. The container listens on localhost:4318 and writes traces into a DuckLake catalog stored in a Docker volume.
The examples follow the Claude Code monitoring docs and the Codex observability and configuration docs.
Start the Local Writer
Section titled “Start the Local Writer”Create a Docker volume for DuckLake metadata and Parquet data:
docker volume create duckdb-otlp-ducklakeCreate .env:
DUCKDB_MODE=local-ducklakeDUCKLAKE_NAME=lakeDUCKDB_OTLP_TOKEN=dev-otlp-token-123456
DUCKDB_QUACK_ENABLED=1DUCKDB_QUACK_ADDR=0.0.0.0:9494DUCKDB_QUACK_TOKEN=dev-quack-token-123456Start the published server image:
docker run --rm --name duckdb-otlp \ --env-file .env \ -p 4318:4318 \ -p 9494:9494 \ -v duckdb-otlp-ducklake:/data \ ghcr.io/smithclay/duckdb-otlp:latestThis starts duckdb-otlp at http://localhost:4318. Send OTLP/HTTP traces to:
http://localhost:4318/v1/tracesThe token in .env is:
dev-otlp-token-123456To use a different local token, change DUCKDB_OTLP_TOKEN and use the same value in the agent exporter headers.
Export Claude Code Traces
Section titled “Export Claude Code Traces”Run Claude Code with tracing enabled and route spans to the local writer:
# To make this apply to every Claude Code session, add these to ~/.claude/settings.jsonexport CLAUDE_CODE_ENABLE_TELEMETRY=1export CLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1export OTEL_TRACES_EXPORTER=otlpexport OTEL_EXPORTER_OTLP_TRACES_PROTOCOL=http/protobufexport OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://localhost:4318/v1/tracesexport OTEL_EXPORTER_OTLP_HEADERS='Authorization=Bearer dev-otlp-token-123456'
claude -p "write one sentence about local trace storage"To store Claude Code events in DuckLake, add the logs exporter before starting Claude Code:
export OTEL_LOGS_EXPORTER=otlpexport OTEL_EXPORTER_OTLP_LOGS_PROTOCOL=http/protobufexport OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=http://localhost:4318/v1/logsLeave OTEL_LOG_USER_PROMPTS, OTEL_LOG_TOOL_DETAILS, OTEL_LOG_TOOL_CONTENT, and OTEL_LOG_RAW_API_BODIES unset unless you intend to store prompt text, tool details, tool content, or raw API bodies.
Export Codex Traces
Section titled “Export Codex Traces”Put telemetry routing in your user-level Codex config. Codex ignores otel in project-local .codex/config.toml.
Edit ~/.codex/config.toml:
[otel]environment = "local"log_user_prompt = falsemetrics_exporter = "none"exporter = { otlp-http = { endpoint = "http://localhost:4318/v1/logs", protocol = "binary", headers = { "Authorization" = "Bearer dev-otlp-token-123456" }}}trace_exporter = { otlp-http = { endpoint = "http://localhost:4318/v1/traces", protocol = "binary", headers = { "Authorization" = "Bearer dev-otlp-token-123456" }}}Start a new Codex process after editing the config:
codex exec "write one sentence about local trace storage"To collect spans without Codex event logs, set exporter = "none" and keep the trace_exporter block.
Flush Buffered Telemetry
Section titled “Flush Buffered Telemetry”duckdb-otlp buffers accepted rows. Flush before you query a short local run:
duckdb <<'SQL'INSTALL quack;LOAD quack;
FROM quack_query( 'quack:localhost:9494', 'SELECT * FROM otlp_flush(''otlp:0.0.0.0:4318'')', token = 'dev-quack-token-123456');SQLInspect Stored Traces
Section titled “Inspect Stored Traces”Query through the running DuckDB process with Quack. The server process owns the DuckLake catalog lock while it runs, and the distroless image has no shell or bundled DuckDB CLI, so do not use docker exec ... sh -c for inspection SQL.
duckdb <<'SQL'INSTALL quack;LOAD quack;
FROM quack_query( 'quack:localhost:9494', $$ SELECT trace_id, name, service_name, duration_time_unix_nano FROM lake.main.otlp_traces ORDER BY start_time_unix_nano DESC LIMIT 20 $$, token = 'dev-quack-token-123456');SQLIf you enabled event logs, inspect recent log rows:
duckdb <<'SQL'INSTALL quack;LOAD quack;
FROM quack_query( 'quack:localhost:9494', $$ SELECT time_unix_nano, service_name, severity_text, body FROM lake.main.otlp_logs ORDER BY time_unix_nano DESC LIMIT 20 $$, token = 'dev-quack-token-123456');SQLStop the Writer
Section titled “Stop the Writer”Stop the container with Ctrl-C if your terminal is attached, or run:
docker stop duckdb-otlpDuring shutdown, the image sends otlp_stop('otlp:0.0.0.0:4318') to DuckDB so the process commits remaining buffered rows before it exits.
If No Traces Appear
Section titled “If No Traces Appear”- Start the agent process after you set the telemetry settings.
- Confirm the trace endpoint is
http://localhost:4318/v1/traces. - Confirm the Authorization header uses the same token as
DUCKDB_OTLP_TOKEN. - Flush the writer before querying short sessions.
- Keep the query window recent enough to include the agent run.