Files
unpoller_unpoller/pkg/promunifi
Prototype0645 ca64173b35 feat(wan): label WAN interface state with its source
unpoller_wan_interface_state ships with site_name and nothing else to
identify where it came from. That is not enough: every UniFi OS console
names its only site "default", so site_name is "Default (default)" on all
of them. An instance polling several controllers emits WAN status that is
indistinguishable, and downstream attribution has to be guessed.

Observed in production before the fix: a scrape config guessing from
site_name filed a UDM's WAN state under the wrong customer. No error, no
missing series — just wrong data under someone else's name.

unifi/v6.1.0 (#244) added SourceName to WANStatus, stamped from the
controller URL in all three read paths. go.mod is already on v6.1.0, so
this only has to read the field.

Two lines, same shape as #1071 which did this for unpoller_wan_*.

Tests use the fakeReport already present in the package: every emitted
metric carries both site_name and source, the raw state is kept as a
label, and a nil status still produces nothing rather than panicking a
poll. TestExportWANStatusIsAttributed fails on master and passes here.
2026-08-31 23:42:30 +02:00
..
2022-12-02 20:46:16 -05:00
2023-07-31 14:40:33 -05:00

prometheus

This package provides the interface to turn UniFi measurements into prometheus exported metrics. Requires the poller package for actual UniFi data collection.

Scrape cache

Prometheus scrapes are served from an in-memory cache refreshed by a background poller on a fixed interval. This decouples the scrape cadence from the UniFi API call cadence: scrapes always return immediately, and upstream backpressure (e.g. 429 Too Many Requests) no longer stalls /metrics.

Config (TOML):

[prometheus]
  http_listen = "0.0.0.0:9130"
  # How often the background poller refreshes the cache served to /metrics.
  # Default: 60s. Values below 15s are clamped to 15s.
  interval = "60s"

Environment variable: UP_PROMETHEUS_INTERVAL=60s.

On poll error the last successful snapshot is preserved, so a transient 429 no longer empties /metrics. To monitor cache staleness, scrape the unpoller_prometheus_cache_age_seconds gauge — it reports seconds since the last successful background refresh, or -1 if no refresh has succeeded yet.

The /scrape endpoint (per-target dynamic scrapes) still fetches live but coalesces concurrent requests for the same target via singleflight, so a noisy scraper cannot multiply upstream load.