Prometheus metrics#
GET /metrics serves a Prometheus text exposition. It needs the viewer role unless
the agent was started with --metrics-public. The content type is
text/plain; version=0.0.4; charset=utf-8, every series carries HELP and TYPE, and
the output is sorted, so a diff between two scrapes is readable.
curl -sk "$R/metrics" -H "Authorization: Bearer $T"Re-derive the list of series names for the version you are running:
grep -o 'wheelhouse_[a-z_]*' agent/metrics.go | sort -uEvery series#
Agent identity#
| Series | Type | Labels | Notes |
|---|---|---|---|
wheelhouse_up | gauge | — | Always 1 when the scrape succeeded. Alert on its absence, not on its value. |
wheelhouse_build_info | gauge | version | Always 1. The version is the agent's compiled-in version string. |
wheelhouse_uptime_seconds | gauge | — | Seconds since the metrics registry was created, which is process start. |
HTTP#
| Series | Type | Labels |
|---|---|---|
wheelhouse_http_requests_total | counter | method, route, status |
wheelhouse_http_request_duration_seconds | histogram | method, route |
Both are recorded before the request is authenticated, so an anonymous 401 is
counted. status="101" is the telemetry WebSocket, and for that route the duration is
the lifetime of the connection rather than a request latency.
The router#
| Series | Type | Labels | Notes |
|---|---|---|---|
wheelhouse_vyos_calls_total | counter | — | Every call the agent made to the router's API. |
wheelhouse_vyos_failures_total | counter | — | Of those, the ones that errored — either a transport failure or an error field in the router's answer. |
wheelhouse_vyos_request_duration_seconds | histogram | endpoint | Round-trip time by router API endpoint: /retrieve, /show, /configure, /config-file, /container-image, /image, /traceroute, /reboot, /poweroff. |
wheelhouse_router_reachable | gauge | — | 1 when the most recent call to the router succeeded. It does not say when that call was; the next series does. |
wheelhouse_router_last_success_seconds | gauge | — | Seconds since the last successful router call. Absent until the agent has succeeded once. |
The read cache#
| Series | Type | Labels | Notes |
|---|---|---|---|
wheelhouse_cache_reads_total | counter | result = hit | stale | miss | A stale read was served immediately from the cache and triggered one background refresh. |
wheelhouse_cache_primes_total | counter | — | Warm-up passes run by the primer. |
wheelhouse_cache_prime_reads_total | counter | — | Reads a warm-up pass asked for. The per-interface and per-app fan-out at the end of a full pass is not counted. |
wheelhouse_cache_prime_seconds_total | counter | — | Time spent in warm-up passes. |
Every router read the cache does not serve forks cli-shell-api on the router, so a
falling hit rate shows up as load on the box.
Configuration state#
| Series | Type | Labels | Notes |
|---|---|---|---|
wheelhouse_staged_operations | gauge | — | Operations in the working set, waiting for a commit. |
wheelhouse_desired_managed | gauge | — | Always 1 when present. Absent unless --reconcile-file is set — absence means "not managing a file", which is the default. |
wheelhouse_desired_drift | gauge | — | 1 when the router differs from its desired-state file. Same absence rule. |
wheelhouse_desired_pending_ops | gauge | — | How many operations the loop wants applied. Same absence rule. |
Accounts#
| Series | Type | Notes |
|---|---|---|
wheelhouse_users | gauge | Configured accounts. |
wheelhouse_sessions_active | gauge | Live browser sessions, after expired ones are pruned. |
wheelhouse_api_tokens | gauge | Issued API tokens, expired ones included until they are deleted. |
Single sign-on#
| Series | Type | Labels | Notes |
|---|---|---|---|
wheelhouse_oidc_assertions_total | counter | outcome = ok | failure | refused | failure is the protocol breaking. refused is an identity being told no. |
wheelhouse_oidc_roles_total | counter | action, role | The role handed out — or refused — at each successful sign-on. |
Both series are present but empty on an agent with no identity provider configured.
Interface throughput#
| Series | Type | Labels |
|---|---|---|
wheelhouse_interface_rx_bytes_per_second | gauge | interface |
wheelhouse_interface_tx_bytes_per_second | gauge | interface |
These come from the sampler, not from the telemetry stream: one counter read every five
seconds for every interface at once, with the rate derived from the difference between
successive reads. A counter that goes backwards — an interface reset — reports 0 for
that step rather than a negative rate. An interface the sampler has seen only once has a
rate of 0 until its second sample.
Histogram buckets#
Both histograms share one bucket layout, in seconds:
0.005 0.01 0.025 0.05 0.1 0.25 0.5 1 2.5 5 10 +InfEach exports _bucket (cumulative), _sum and _count. The buckets are not
configurable.
How the route label is built#
Request paths carry object names, and a label built from a client-supplied string would mint a fresh time series per request. Paths are collapsed:
| Path | Label |
|---|---|
/health, /metrics | themselves |
Anything else outside /api/ | /ui |
/api/<area>/… where <area> is not one the mux registers | /api/{unknown} |
/api/fleet/<id>/… | /api/fleet/{id}/… |
/api/admin/users/<name>, /api/admin/tokens/<id>, /api/auth/sessions/<id>, /api/apps/logs/<name>, /api/apps/hints/<page> | fourth segment becomes {id} |
Any remaining segment from the third onwards containing . or :, or longer than 20 characters | {id} |
Requests are observed before they are authenticated, so the /api/{unknown} rule is
what keeps an anonymous GET /api/<anything> from minting a series.
Label escaping#
Label values escape backslash, double quote and newline, and nothing else. Other control
characters become ?, so a path holding a tab character cannot produce an exposition
the reference parser rejects.
A scrape config#
scrape_configs:
- job_name: wheelhouse
scheme: https
tls_config: { insecure_skip_verify: true } # self-signed by default
authorization:
type: Bearer
credentials: wh_…
static_configs:
- targets: ['192.0.2.1:8443']Issue a viewer token for this; Prometheus needs nothing more. --metrics-public
removes the need for a credential entirely, and with it the authentication on a route
that reports your account count, your session count and your interface names.
What is not here#
Log lines, not metrics#
Some conditions are only ever a log line — a secret file with wrong permissions, a state file that would not parse, a reconcile pass that could not read its file. Those are on Log lines worth alerting on.
See also#
- The HTTP API
- Endpoint index
- The telemetry stream — where the
status="101"series comes from. - Log lines worth alerting on
- Every flag —
--metrics-public. - Telemetry