Skip to main content
Beads exports metrics via OTLP HTTP. Telemetry is disabled by default — zero overhead when no variable is set.
ServicePortRole
VictoriaMetrics8428OTLP metrics storage
VictoriaLogs9428Reserved for future OTLP log storage
Grafana9429Dashboards
# From your personal stack's opentelemetry/ folder
docker compose up -d

Configuration

One variable is enough. Add it to your shell profile or workspace .env:
export BD_OTEL_METRICS_URL=http://localhost:8428/opentelemetry/api/v1/push
Every bd command will then automatically push its metrics. Log export is not implemented yet. BD_OTEL_LOGS_URL is reserved for a future VictoriaLogs exporter and does not activate telemetry today.
# ~/.zshrc or ~/.bashrc
export BD_OTEL_METRICS_URL=http://localhost:8428/opentelemetry/api/v1/push

Environment variables

VariableExampleDescription
BD_OTEL_METRICS_URLhttp://localhost:8428/opentelemetry/api/v1/pushPush metrics to VictoriaMetrics. Activates telemetry.
BD_OTEL_LOGS_URLhttp://localhost:9428/insert/opentelemetry/v1/logsReserved for future log export. Does not activate telemetry today.
BD_OTEL_STDOUTtrueWrite spans and metrics to stderr (dev/debug). Also activates telemetry.

Local debug mode

BD_OTEL_STDOUT=true bd list

Verification

bd list   # triggers metrics → visible in VictoriaMetrics
Verification query in Grafana (VictoriaMetrics datasource):
bd_storage_operations_total

Confirm storage instrumentation locally

To verify the storage decorator chain is wired up without standing up a collector, run bd with stdout exporters and look for bd.storage.* records on stderr:
BD_OTEL_STDOUT=true bd list 2>&1 | grep -F bd.storage.operations
Expect at least one line per storage call (GetReadyWork, GetIssue, …). If bd.storage.* and bd.issue.count are absent but bd.db.pool_* is present, the storage decorator is not in the chain — check wireStorageDecorators in cmd/bd/storage_chain.go.

Metrics

Storage (bd_storage_*)

MetricTypeAttributesDescription
bd_storage_operations_totalCounterdb.operationStorage operations executed
bd_storage_operation_duration_msHistogramdb.operationOperation duration (ms)
bd_storage_errors_totalCounterdb.operationStorage errors
These metrics are emitted by InstrumentedStorage, the beads SDK wrapper.

Dolt database (bd_db_*)

MetricTypeAttributesDescription
bd_db_retry_count_totalCounterSQL retries in server mode
bd_db_lock_wait_msHistogramdolt_lock_exclusiveWait time to acquire database locks

Issues (bd_issue_*)

MetricTypeAttributesDescription
bd_issue_countGaugestatusNumber of issues by status
status values: open, in_progress, closed, deferred.

AI (bd_ai_*)

MetricTypeAttributesDescription
bd_ai_input_tokens_totalCounterbd_ai_modelAnthropic input tokens
bd_ai_output_tokens_totalCounterbd_ai_modelAnthropic output tokens
bd_ai_request_duration_msHistogrambd_ai_modelAPI call latency

Traces (spans)

Spans are only exported when BD_OTEL_STDOUT=true — there is no trace backend in the recommended local stack.
SpanSourceDescription
bd.command.<name>CLITotal duration of the command
dolt.exec / dolt.query / dolt.query_rowSQLEach SQL operation
dolt.commit / dolt.push / dolt.pull / dolt.mergeDolt VCVersion control procedures
ephemeral.count / ephemeral.nukeSQLiteEphemeral store operations
hook.execHooksHook execution (root span, fire-and-forget)
tracker.sync / tracker.pull / tracker.pushSyncTracker sync phases
anthropic.messages.newAIClaude API calls

Notable attributes

bd.command.<name>
AttributeDescription
bd.commandSubcommand name (list, create, …)
bd.versionbd version
bd.argsRaw arguments passed to the command (e.g. “create ‘title’ -p 2”)
bd.actorActor (resolved from git config / env)
hook.exec
Attribute / EventDescription
hook.eventEvent type (create, update, close)
hook.pathAbsolute path to the script
bd.issue_idID of the triggering issue
event hook.stdoutScript standard output (truncated to 1 024 bytes)
event hook.stderrScript error output (truncated to 1 024 bytes)
The hook.stdout / hook.stderr events carry two attributes: output (the text) and bytes (original size before truncation).

Architecture

cmd/bd/main.go
  └─ telemetry.Init()
      ├─ BD_OTEL_STDOUT=true  → TracerProvider stdout + MeterProvider stdout
      └─ BD_OTEL_METRICS_URL  → MeterProvider HTTP → VictoriaMetrics

internal/storage/dolt/        → bd_db_* metrics + dolt.* spans
internal/storage/ephemeral/   → ephemeral.* spans
internal/hooks/               → hook.exec span
internal/tracker/             → tracker.* spans
internal/compact/             → bd_ai_* metrics + anthropic.* spans
internal/telemetry/storage.go → bd_storage_* metrics (SDK wrapper)
When neither variable is set, telemetry.Init() installs no-op providers: hot paths execute only no-op calls with no memory allocation.