Metrics (Prometheus)
The backend serves request, background-job and database-pool metrics in the Prometheus text format at /metrics. The endpoint is off by default.
Turn it on
Section titled “Turn it on”Set METRICS_TOKEN in backend/container/.env (see Environment Variables), then restart the backend:
python -c "import secrets; print(secrets.token_urlsafe(48))"- The token must be at least 32 characters.
- With
METRICS_TOKENempty,/metricsanswers 404. - A missing or wrong token gets 401.
- The token is a secret and is never logged. Keep the file that holds it readable by Prometheus only.
Scrape it
Section titled “Scrape it”Prometheus must reach the backend container directly on the container network, for example backend:8000 with Compose. The frontend’s nginx does not proxy /metrics, so the public URL serves the web app instead. Do not publish the backend port to get around this.
scrape_configs: - job_name: patrimoine metrics_path: /metrics authorization: type: Bearer credentials_file: /etc/prometheus/patrimoine-metrics-token static_configs: - targets: ["backend:8000"]The older bearer_token_file key works too. To check the endpoint by hand from a container on the same network:
curl -H "Authorization: Bearer $METRICS_TOKEN" http://backend:8000/metricsMetrics
Section titled “Metrics”| Name | Type | Labels | Meaning |
|---|---|---|---|
patrimoine_http_requests_total |
counter | method, route, status_class |
HTTP requests handled by the backend |
patrimoine_http_request_duration_seconds |
histogram | method, route, status_class |
Request duration in seconds; buckets 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10 and 30 |
patrimoine_job_runs_total |
counter | job, outcome |
Background job runs; outcome is success or error |
patrimoine_db_pool_size |
gauge | none | Configured size of the database connection pool (PostgreSQL only) |
patrimoine_db_pool_checked_out |
gauge | none | Connections currently in use (PostgreSQL only) |
patrimoine_db_pool_overflow |
gauge | none | Connections open beyond the pool size; stays 0 until the pool is exhausted (PostgreSQL only) |
patrimoine_build_info |
gauge | version |
Always 1; the label carries the backend version |
Labels
Section titled “Labels”routeis the route template, for example/api/v1/accounts/{account_id}, never the raw path. Paths that match no route areunmatched.methodis one ofGET,POST,PUT,PATCH,DELETE,HEADorOPTIONS. Any other method isOTHER.status_classis1xxto5xx.- Scrapes of
/metricsitself are not counted. jobis the name of the background job:daily-backup,recurring-snapshots,planned-transaction-sweep,alert-check,fx-refresh,audit-cleanup,session-purge,revoked-token-purge,webhook-delivery-cleanup,delta-cache-refresh,price-refresh,scheduled-reportsandwebhook-delivery-retry. See Background Worker for what each one does.
Background jobs
Section titled “Background jobs”Without REDIS_URL, the jobs run in the backend process and are counted there.
With the arq worker, each run is counted in the Redis hash patrimoine:metrics:job_runs, and the backend reads that hash on every scrape. This needs the redis package in the backend image. The default image, published or built locally, includes it. Only a custom UV_EXTRA_ARGS that omits --extra redis removes it; podman.sh build adds it whenever REDIS_URL is set. When Redis cannot be read, the worker series are left out and the backend logs one warning.
Limits of the counts:
successmeans the job ran to its end. Per-user failures in the FX or delta cache refresh are logged and the run still counts as a success.- A worker daily backup with
AUTO_BACKUPoff counts as a success. - A worker job stopped by arq’s own timeout is not counted.
Database pool
Section titled “Database pool”The three patrimoine_db_pool_* gauges exist only on PostgreSQL. On SQLite they are absent.
Restarts and processes
Section titled “Restarts and processes”- The backend’s own counters restart from zero when it restarts. The worker’s counters restart when Redis loses its data; the hash has no expiry. Prometheus
rate()andincrease()handle these resets. - The backend runs as a single process. Running several uvicorn workers would give per-process counters, which is not supported.