OBLETHdocs

Observability

Where to look when you want to know how the gateway is doing, and what happened to one request.

4 min readUpdated for v2.4.0Edit on GitHub

Fleet healthPrometheus at :9091/metrics
One requestRequest logs in the dashboard
Your tracing backendSet OBLETH_OTEL_ENDPOINT
Per tenantThe usage ledger in ClickHouse

Prometheus metrics

Every gateway replica serves metrics on OBLETH_METRICS_LISTEN (default 0.0.0.0:9091, path /metrics). In Kubernetes, set serviceMonitor.enabled: true if the Prometheus Operator is installed. In Compose, the observability profile adds Prometheus on 127.0.0.1:9090 and Grafana on port 3001 with an Obleth Gateway dashboard; set GRAFANA_ADMIN_PASSWORD in deploy/docker/.env first, or Compose refuses to start it.

Labels stay low-cardinality: no metric carries a tenant, key or model label. The series worth an alert:

MetricWatch for
obleth_requests_total{admission,status}A rise in status="5xx"; sustained growth of admission="queued" means pools are full
obleth_queue_depth, obleth_in_flightRequests waiting for a slot and running, on this replica
obleth_telemetry_droppedAny increase: usage rows were lost
obleth_fairshare_shared_slots_mode{mode}mode="fallback" at 1: this replica cannot reach Redis and is splitting the limits
obleth_upstream_attempts_total{outcome}Growth of failover or exhausted; see Reliability
obleth_jwks_refresh_total{result="error"}A sustained rate for one issuer; see the Details below

Every metric and its labels are in Prometheus metrics.

Request logs

Each request, rejections included, writes one row to the ClickHouse usage ledger: time, tenant, key, model, session, request type, tokens (with cached input tokens when the backend reports them), time to first token, total time, status and cost. Request logs in the dashboard shows them within a few seconds, with filters for status, model, team, key, type and session. Select a request to see what happened in one line, its timeline, every number, the variant it named, and the Helper calls its boons made, each linked back to it.

A request open in the side panel of Request logs: what happened, a timeline split into auth, admission and upstream, and the tokens, cost and times

Without tracing, the timeline comes from the log row: the wait for a slot, the time to first token, and the rest. Traced requests show each step, and a step's attributes, such as why the auto router picked the model. See Control plane for the rest of the page.

The same rows are at GET /api/v1/usage/logs on the Management API. Health probes and traffic from tenants flagged synthetic (obench runs, the dashboard's own tests) are excluded from usage and cost figures by default; pass include_internal=true to include them.

Per-request span tracing

Turn on Trace requests for a tenant (all its keys) on the tenant's page, or for one key in its panel on API keys. Or use the API:

curl -X PUT http://localhost:9180/api/v1/tenants/$TENANT_ID/tracing \
  -H "Authorization: Bearer $OBLETH_ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"tracing_enabled": true}'

Use /api/v1/keys/$KEY_ID/tracing for a single key. Traced requests write their spans to the ClickHouse spans table, the log row carries has_trace: true, and GET /api/v1/usage/logs/{request_id}/spans returns them. Spans produced while ClickHouse is unreachable are discarded; unlike usage rows, they are not spilled.

OTLP export

Set OBLETH_OTEL_ENDPOINT to an OTLP/HTTP collector's base URL and every request's spans are exported to {endpoint}/v1/traces under the service name obleth. Unset, there is no tracing overhead.

# Compose .env: the observability profile runs Jaeger, UI on 127.0.0.1:16686
OBLETH_OTEL_ENDPOINT=http://jaeger:4318

In Helm, set obleth.otelEndpoint. Export runs through a background batch processor, so a slow collector does not slow requests. Each request's spans carry session.id and session.id.source (client or derived), so a conversation's turns can be grouped. See Conversations and sessions.

Per-tenant data

Prometheus answers "how is the gateway doing"; per-tenant, per-key and per-model questions go to ClickHouse. The raw usage table keeps OBLETH_USAGE_RETENTION_DAYS (default 180) of rows, and the usage_daily rollup keeps one row per day, tenant, key and model forever. Reports in the dashboard reads both, and the tables are described in ClickHouse usage.

Model health and the Slurm provisioner's state are per-model state, not time series, so they are shown in the dashboard rather than exported: see Health checks and Slurm provisioning.

Spans recorded in ClickHouse
SpanCovers
proxy_requestThe whole request (root)
auth_resolveKey and tenant lookup
auto_routeThe auto router's choice, when used
admissionThe wait for a fairshare slot
cache_lookupThe response cache check
boon:vision, boon:knowledge, boon:compression, boon:structured_repairEach boon that ran
boon:tool_loop, boon:tool_loop:iter:{n}, boon:image_generationThe gateway's tool loop, per turn, and image generation calls within it
boon:guardrails_input, boon:guardrails_outputGuardrails scans
upstreamThe upstream call
upstream_diagnosticsA DNS and port check after a 502 or 504, when Debug upstream failures is on for the model
verdict:q:<id>One question of a Verdicts request

OTLP export has its own span set: proxy_request with auth_resolve, cache_lookup, reserve_budget, reserve_key_budget and upstream_request, and mcp_request for MCP gateway calls.

Identity-token signals

With OBLETH_JWT_ISSUERS set, obleth_jwt_verify_total{result} counts each verification and obleth_jwks_refresh_total{issuer_index,result} each signing-key fetch.

A failed key refresh keeps the last good keys, so nothing breaks until that issuer rotates its signing key. Treat a sustained error rate as a warning with lead time. Each failure also raises a jwks_fetch_failed alert.

On obleth_jwt_verify_total, a spike in a single result points at one misconfiguration: unknown_issuer or bad_audience for a client pointed at the wrong issuer, expired for a client reusing an old token. Rejections are total - ok, not the sum of the other labels, because not_provisioned and provision_failed are counted in addition to ok. See Identity tokens.