feat(prometheus): the Prometheus dashboard widget, target + template (#5691)
#5691 "Generic Prometheus dashboard widget: pick a target and a template", step 3 of milestone 508 "Prometheus widget templates". - /plugins/prometheus/widget draws one target with one template in widget shape: the verdict (led by "isn't answering" when the target is down), then each block with problems first, figures beside a share bar or the last 24h of bars, and the healthy rest folding to "+N more healthy" past six. A missing target or template, or one that no longer checks, says so. - library.py: bundled templates from plugins/prometheus/widgets/*.yaml, the picker's target and template lists, and the default title. - widgets/fabled_archives.yaml: the first bundled template, against the series Archives reports (refined in #5693). - Dashboard: a widget setting may take `options_from` (read when the picker opens) with an `empty` sentence; `default_title`; a Details link may name settings ("/plugins/prometheus/targets/{target}"). Prometheus is grouped with the integrations. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -308,3 +308,57 @@ async def series_detail(target_id: str):
|
||||
"prometheus/series.html", target=target, resource=resource, metric=metric, labels=labels,
|
||||
now=shown_value(metric, now_row.value if now_row else None), hours=hours, ranges=RANGES,
|
||||
points_json=json.dumps([(t.isoformat(), v) for t, v in series]), stamp_axis=stamp_axis)
|
||||
|
||||
|
||||
# ── The dashboard widget ─────────────────────────────────────────────────────
|
||||
|
||||
SPARK_HOURS = 24
|
||||
|
||||
|
||||
def spark_heights(values: list[float]) -> list[float]:
|
||||
"""Bar heights in % of the series' own peak over the window (System
|
||||
154's ruling on small history bars); a flat zero draws flat."""
|
||||
peak = max((abs(v) for v in values), default=0.0)
|
||||
return [round(100.0 * abs(v) / peak, 1) if peak else 0.0 for v in values]
|
||||
|
||||
|
||||
@prometheus_bp.get("/widget")
|
||||
@require_role(UserRole.viewer)
|
||||
async def widget():
|
||||
"""The Prometheus dashboard widget: one target drawn by one template
|
||||
(System 181's ruling), in widget shape. Reads stored values only."""
|
||||
from . import library
|
||||
from .template_view import render as draw
|
||||
|
||||
target_id = request.args.get("target") or ""
|
||||
key = request.args.get("template") or ""
|
||||
now = datetime.now(timezone.utc)
|
||||
async with current_app.db_sessionmaker() as db:
|
||||
target = await db.get(PrometheusTarget, target_id) if target_id else None
|
||||
entry = await library.find(db, key) if key else None
|
||||
problem = ("This widget has no target; remove it and add it again with one." if not target_id
|
||||
else "The target this widget showed has been deleted." if target is None
|
||||
else "This widget has no template; remove it and add it again with one." if not key
|
||||
else "The template this widget used is gone." if entry is None
|
||||
else f"The template “{entry.name}” has a problem: {entry.problems[0]}"
|
||||
if entry.template is None else "")
|
||||
if problem:
|
||||
return await render_template("prometheus/widget.html", problem=problem, target=None,
|
||||
view=None)
|
||||
latest = await latest_values(db, SOURCE, target.name, include_children=True)
|
||||
view = draw(entry.template, target.name, latest, now)
|
||||
if view.wants:
|
||||
since = now - timedelta(hours=SPARK_HOURS)
|
||||
history = {}
|
||||
for resource, metric in view.wants:
|
||||
got = await points(db, SOURCE, resource, metric, since, target=SPARK_HOURS)
|
||||
history[(resource, metric)] = spark_heights([v for _, v in got])
|
||||
view = draw(entry.template, target.name, latest, now, history=history)
|
||||
return await render_template("prometheus/widget.html", problem="", view=view,
|
||||
target=view_target(target, now), spark_hours=SPARK_HOURS)
|
||||
|
||||
|
||||
def view_target(target: PrometheusTarget, now: datetime) -> dict:
|
||||
state = target_state(target, now)
|
||||
return {"id": target.id, "name": target.name, "reach": state.reach, "says": state.says,
|
||||
"ok_at": target.last_ok_at, "scraped_at": target.last_scraped_at}
|
||||
|
||||
Reference in New Issue
Block a user