Observability¶
Corvix ships with three complementary observability signals so you can run it
in production with confidence: structured logging, Prometheus metrics,
and optional OpenTelemetry tracing. All three are configured through
environment variables, so they fit cleanly into a Docker Compose or Kubernetes
deployment without touching corvix.yaml.
Structured logging¶
Both the web service and the poller emit one JSON object per log line to stdout. Every line carries a consistent schema:
{"timestamp": "2026-05-26T21:44:53.105038+00:00", "level": "INFO", "logger": "corvix.services", "module": "services", "event": "poll cycle complete", "fetched": 12, "excluded": 3, "actions_taken": 1, "errors": 0}
timestamp— UTC ISO-8601 timestamp.level— log level (INFO,WARNING,ERROR, …).logger/module— the originating logger name and module.event— the human-readable log message.Any extra fields passed at the call site (
fetched,request_id, …) appear as top-level keys, so they are easy to index in a log pipeline.
During a web request, every log line is automatically tagged with the
request_id for that request (see Request IDs).
Configuration¶
Environment variable |
Default |
Description |
|---|---|---|
|
|
Root log level ( |
|
|
|
Prometheus metrics¶
The web service exposes a Prometheus endpoint at GET /metrics. It is
always public (no authentication required) so a scraper can reach it even when
CORVIX_SECRET_TOKEN is set, mirroring the /api/health endpoint.
curl http://localhost:8000/metrics
Exported metrics¶
Metric |
Type |
Labels |
Meaning |
|---|---|---|---|
|
counter |
|
Poll cycles run, by outcome ( |
|
histogram |
— |
Wall-clock duration of a poll cycle. |
|
counter |
— |
Notifications fetched from GitHub. |
|
counter |
— |
Automation actions executed. |
|
counter |
— |
Poll cycles that raised an unhandled error. |
|
counter |
|
GitHub API requests, by HTTP method and outcome ( |
|
histogram |
|
GitHub API request latency. |
|
counter |
|
HTTP requests served, by route template and status code. |
|
histogram |
|
HTTP request latency. |
The endpoint label uses the matched route template (for example
/api/v1/notifications/{account_id}/{thread_id}/dismiss) rather than the
concrete path, so per-notification requests do not explode label cardinality.
Scraping with Prometheus¶
scrape_configs:
- job_name: corvix
static_configs:
- targets: ["corvix-web:8000"]
The poller process records the poll-cycle and GitHub-API metrics but does not
expose its own HTTP endpoint; those counters are most useful on the web service
where /metrics is served. Poller health is also available via the
corvix poller-health command and the /api/v1/health endpoint.
Request IDs¶
Every HTTP response includes an X-Request-ID header. If the incoming request
already carries an X-Request-ID, Corvix reuses it (so a request ID set by an
upstream reverse proxy flows through); otherwise a new one is generated. The
same ID is bound into the logging context, so all log lines emitted while
handling a request share a request_id field — making it straightforward to
correlate a user-visible error with its server-side logs.
OpenTelemetry tracing (optional)¶
Tracing is opt-in and requires the otel extra to be installed:
uv sync --extra otel # or: pip install "corvix[otel]"
Enable it by setting CORVIX_OTEL_ENABLED=true. Corvix creates spans for each
poll cycle (poll_cycle), each GitHub API request (github.api.request), and
each web request (http.request). Spans are exported over OTLP/HTTP using the
standard OpenTelemetry environment variables.
Environment variable |
Default |
Description |
|---|---|---|
|
|
Master switch for tracing. When false (or the |
|
|
Service name reported to your tracing backend. Takes precedence over the built-in default. |
|
|
OTLP/HTTP collector endpoint. |
Any other standard OTEL_* variable (headers, timeouts, resource attributes)
is honoured by the OpenTelemetry SDK directly.
Example: export to a local collector¶
export CORVIX_OTEL_ENABLED=true
export OTEL_SERVICE_NAME=corvix-web
export OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4318
uvicorn corvix.web.app:app --host 0.0.0.0 --port 8000 --proxy-headers
When CORVIX_OTEL_ENABLED is unset the tracing code path adds no runtime
overhead, so it is safe to leave the instrumentation in place at all times.