Skip to content

Observability ​

CodeLotse exports traces, metrics and logs over OTLP/HTTP when OTEL_EXPORTER_OTLP_ENDPOINT is set on the API and the worker. Without it nothing is exported and the instruments are no-ops.

The observability pack runs on your own infrastructure and sends nothing to third parties:

ComponentVersionRole
OpenTelemetry Collector (contrib)0.162.0OTLP receiver (4317 gRPC, 4318 HTTP), Prometheus exporter (8889), traces to Jaeger; logs are dropped
Prometheusv3.15.0Scrapes the collector, evaluates the alert rules
Jaegerv2.22.0Traces, in-memory storage (50 000 traces)
Persesv0.54.0Dashboards, read-only, provisioned from files

Shared files (used by Compose and the Helm chart):

  • deploy/helm/codelotse/files/observability/otel-collector.yaml
  • deploy/helm/codelotse/files/observability/prometheus-rules.yaml
  • deploy/helm/codelotse/files/observability/dashboards/*.yaml

Compose ​

Start the stack with the overlay from deploy/compose:

sh
docker compose -f docker-compose.yml -f ../observability/docker-compose.observability.yml up -d

The overlay points the API and the worker at the collector and adds the four services. All images are pinned by digest, run read-only without capabilities, and publish their UIs on 127.0.0.1 only:

UIDefault addressVariable
Perseshttp://127.0.0.1:8080PERSES_PORT
Prometheushttp://127.0.0.1:9090PROMETHEUS_PORT
Jaegerhttp://127.0.0.1:16686JAEGER_PORT

Prometheus keeps data for PROMETHEUS_RETENTION (default 15d) in the prometheus-data volume. Jaeger and Perses keep nothing across restarts. Use an SSH tunnel to reach the UIs from another machine; Perses runs without authentication and must not be exposed publicly.

To send telemetry to an existing collector instead, set OTEL_EXPORTER_OTLP_ENDPOINT in deploy/compose/.env (for example http://collector.internal:4318) and do not use the overlay.

Helm ​

The chart renders the same collector configuration, alert rules and dashboards. The observability.* values switch the collector, the PrometheusRule/ServiceMonitor resources and the Perses dashboards on or off; see kubernetes.md. The chart sets OTEL_EXPORTER_OTLP_ENDPOINT on the API and the worker when the collector is enabled.

Metrics ​

Prometheus names follow the OpenTelemetry conversion (dots become underscores, units are appended, counters end in _total). No metric carries tenant, repository, person or login attributes; installation is the internal installation id.

MetricPrometheus nameAttributes
quavon.webhook.receivedquavon_webhook_received_totalprovider, outcome
quavon.webhook.ingest.durationquavon_webhook_ingest_duration_secondsprovider
quavon.queue.depthquavon_queue_depthqueue, state
quavon.job.durationquavon_job_duration_secondsqueue, outcome
quavon.job.dead_letteredquavon_job_dead_lettered_totalqueue, error
quavon.outbox.oldest_pending.agequavon_outbox_oldest_pending_age_seconds—
quavon.github.budget.remainingquavon_github_budget_remaininginstallation, resource
github.secondary_rate_limitgithub_secondary_rate_limit_total—
quavon.llm.request.durationquavon_llm_request_duration_secondsfeature, tier, provider, status
quavon.llm.tokensquavon_llm_tokens_totalfeature, tier, provider, kind
quavon.llm.costquavon_llm_cost_EUR_totalfeature, tier, provider
quavon.review.outcomequavon_review_outcome_totalmode, status, decision
quavon.sandbox.run.durationquavon_sandbox_run_duration_secondspurpose, status, backend
quavon.workspace.freequavon_workspace_free_bytes—
quavon.rate_limit.store_unavailablequavon_rate_limit_store_unavailable_totalbucket, outcome, reason
quavon.mcp.server.requestsquavon_mcp_server_requests_totaltool, outcome
quavon.mcp.server.durationquavon_mcp_server_duration_secondstool
quavon.mcp.context.callsquavon_mcp_context_calls_totaloutcome
quavon.mcp.context.bytesquavon_mcp_context_bytes—

Queue depth and outbox age are sampled every 30 s by one worker (leader key quavon:metrics:leader in Valkey); every worker reports its own free workspace.

Alerts ​

AlertCondition
CodeLotseDeadLetterGrowthA queue dead-lettered jobs in the last 15 minutes
CodeLotseGitHubBudgetLowGitHub budget of an installation below 500 requests (10 %) for 10 minutes
CodeLotseWebhookSignatureFailuresMore than 5 rejected webhook deliveries per minute for 5 minutes
CodeLotseLlmErrorRateMore than 5 % of LLM requests fail for 15 minutes
CodeLotseWorkspaceLowA worker has less than 10 % of its 2 GiB workspace free for 5 minutes
CodeLotseOutboxStuckOldest pending outbox row older than 10 minutes (critical)
CodeLotseSandboxRunFailuresMore than 20 % of sandbox runs fail for 15 minutes
CodeLotseRateLimitStoreUnavailableRate limiting could not reach Valkey for 5 minutes

Prometheus evaluates the rules; route them with your own Alertmanager. The pack does not include one.

Dashboards ​

Project codelotse: overview, ingest (webhooks, outbox, rate limiting), queues and dead letters, GitHub budget and secondary rate limits, LLM latency, tokens, cost and errors, review outcomes, sandbox.

Validation ​

deploy/observability/validate.sh runs promtool check config, promtool check rules, the rule unit tests in deploy/observability/prometheus-rules.test.yaml, percli lint and otelcol-contrib validate. Missing tools are skipped unless QUAVON_REQUIRE_OBSERVABILITY_TOOLS=1.

The open core is released under the Apache License 2.0.