feat(alerts): a sentence per alert, a link to what it watches, a count in the sidebar (#5124)
The Alerts page's active list now says each alert in one line around the resource it watches, e.g. "cpu_pct on web-1 is 94% (> 90%), firing for 12m", with web-1 linking to its host, monitor or container page (or the plugin's page for Traefik, UniFi and SNMP). Values take their unit from the metric name through formatting.metric_value. A silent series reads "No cpu_pct from web-1 for 20m"; an up/down metric says the word. Actions follow the routes' roles: acknowledge, edit, toggle and New rule for operators and admins, Delete for admins only, nothing for viewers. The sidebar shows how many alerts need attention beside Alerts, from an async context processor that skips htmx fragments, logged-out pages and the database-down page, and renders without the count if the query fails. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -149,3 +149,30 @@ def worst_reading(readings, thresholds: dict):
|
||||
if _SEVERITY.get(grade, 0) > rank:
|
||||
worst, rank = (label, value, grade), _SEVERITY[grade]
|
||||
return worst
|
||||
|
||||
|
||||
def metric_value(name: str, value: Any) -> str:
|
||||
"""A metric reading in its unit, from the name's suffix: _pct → 94%,
|
||||
_ms → 120 ms, _bytes → 1.2 GiB, _bps → its rate, _secs / seconds_ → a
|
||||
span, _c → 71°C, _days → 12 days; is_up / is_healthy read as yes or no.
|
||||
None (no reading) is "—"."""
|
||||
if value is None:
|
||||
return "—"
|
||||
n = name or ""
|
||||
if n in ("is_up", "is_healthy"):
|
||||
return {"is_up": ("up", "down"), "is_healthy": ("healthy", "unhealthy")}[n][0 if value >= 0.5 else 1]
|
||||
if n.endswith("_pct") or "_pct_" in n:
|
||||
return f"{value:.1f}%" if abs(value) < 10 else f"{value:.0f}%"
|
||||
if n.endswith("_ms"):
|
||||
return f"{value:.0f} ms"
|
||||
if n.endswith("_bytes"):
|
||||
return human_bytes(value)
|
||||
if n.endswith(("_bps", "bytes_rate")):
|
||||
return human_rate(value)
|
||||
if n.endswith("_secs") or n.startswith("seconds_"):
|
||||
return human_duration(value)
|
||||
if n.endswith("_c"):
|
||||
return f"{value:.0f}°C"
|
||||
if n.endswith("_days"):
|
||||
return f"{value:.0f} days"
|
||||
return f"{value:g}"
|
||||
|
||||
Reference in New Issue
Block a user