From 9e773c2984fd735d9ddc0865195377e3b64d88b4 Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 19 Aug 2026 23:06:20 +0000 Subject: [PATCH] fix(observability): show alerts with a table, not the alert list panel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The alertlist panels added in #37 rendered nothing. Three attempts to make them work, each wrong in a different way, and the third only proved the approach was unusable here. First the panels pointed at the Alertmanager datasource, chosen so silences would show. alertlist reads alert *rules*; an Alertmanager datasource has none, so there was nothing to enumerate. Then they pointed at Prometheus and Loki by datasource uid, but UnifiedAlertList.tsx matches options.datasource against the datasource *name* (`dataSourceName === options.datasource`), so nothing matched. With the name corrected the panel still showed "No alerts matching filters" — and still did with the label filter removed entirely, which is what settled it. The panel source says this should work; it does not, and the fault is not visible from the server side. So these are plain table panels over the ALERTS series Prometheus exposes for its own rules, filtered by component and state. The whole point is that this is an ordinary PromQL query: the exact expression each panel runs was verified through Grafana's own datasource proxy before asking anyone to look at a screen. The previous three attempts all passed every check I could run and were still broken, because none of those checks touched what the panel actually renders. Two costs, both deliberate: Silences are not reflected. ALERTS is Prometheus's view and silences live in Alertmanager, so a silenced alert still appears. That was the original argument for the Alertmanager datasource and it is given up for a panel that works. The Logs dashboard loses its panel entirely. Loki's eight rules are evaluated by Loki's ruler and go straight to Alertmanager, never entering Prometheus ALERTS, so component="logs" would be permanently empty. A panel that always reads clear implies there are no log alerts, which is worse than no panel. The check_dashboards.py exemption from #37 is reverted — no alertlist panel remains, and it documented behaviour that turned out not to work here. Refs #36 --- .../grafana/dashboards/docker-containers.json | 171 ++++++++++++++++-- .../grafana/dashboards/host-overview.json | 171 ++++++++++++++++-- .../grafana/dashboards/logs-explorer.json | 59 ++---- .../grafana/dashboards/network-snmp.json | 171 ++++++++++++++++-- .../grafana/dashboards/ups-power.json | 171 ++++++++++++++++-- 5 files changed, 617 insertions(+), 126 deletions(-) diff --git a/stacks/observability/grafana/dashboards/docker-containers.json b/stacks/observability/grafana/dashboards/docker-containers.json index f55dbe7..ff18d4f 100644 --- a/stacks/observability/grafana/dashboards/docker-containers.json +++ b/stacks/observability/grafana/dashboards/docker-containers.json @@ -84,12 +84,12 @@ }, "panels": [ { - "type": "alertlist", - "title": "Container alerts firing", - "description": "Firing and pending alerts carrying component=containers, from the Alertmanager datasource — so a silenced alert shows as silenced rather than simply vanishing. Empty is the good state.", + "type": "table", + "title": "Container alerts", + "description": "Firing and pending alerts carrying component=containers, from the ALERTS series Prometheus exposes for its own rules. Empty is the good state. Silences live in Alertmanager and are not reflected here — see the dashboard README note.", "datasource": { - "type": "alertmanager", - "uid": "alertmanager" + "type": "prometheus", + "uid": "prometheus" }, "gridPos": { "h": 6, @@ -97,23 +97,154 @@ "x": 0, "y": 0 }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "editorMode": "code", + "exemplar": false, + "expr": "ALERTS{component=\"containers\",alertstate=~\"firing|pending\"}", + "format": "table", + "instant": true, + "legendFormat": "", + "range": false, + "refId": "A" + } + ], + "transformations": [ + { + "id": "organize", + "options": { + "excludeByName": { + "Time": true, + "__name__": true, + "Value": true, + "component": true, + "job": true, + "monitor": true, + "site": true, + "role": true + }, + "indexByName": {}, + "renameByName": { + "alertname": "Alert", + "alertstate": "State", + "severity": "Severity", + "category": "Category", + "device": "Device", + "instance": "Instance", + "vlan": "VLAN" + } + } + } + ], + "fieldConfig": { + "defaults": { + "custom": { + "align": "auto", + "filterable": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Severity" + }, + "properties": [ + { + "id": "mappings", + "value": [ + { + "type": "value", + "options": { + "critical": { + "color": "red", + "index": 0 + }, + "warning": { + "color": "orange", + "index": 1 + }, + "info": { + "color": "blue", + "index": 2 + } + } + } + ] + }, + { + "id": "custom.cellOptions", + "value": { + "type": "color-text" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "State" + }, + "properties": [ + { + "id": "mappings", + "value": [ + { + "type": "value", + "options": { + "firing": { + "color": "red", + "index": 0 + }, + "pending": { + "color": "yellow", + "index": 1 + } + } + } + ] + }, + { + "id": "custom.cellOptions", + "value": { + "type": "color-text" + } + } + ] + } + ] + }, "options": { - "alertInstanceLabelFilter": "{component=\"containers\"}", - "alertName": "", - "dashboardAlerts": false, - "datasource": "alertmanager", - "groupBy": [], - "groupMode": "default", - "maxItems": 20, - "sortOrder": 1, - "stateFilter": { - "error": true, - "firing": true, - "noData": false, - "normal": false, - "pending": true + "showHeader": true, + "cellHeight": "sm", + "footer": { + "show": false, + "reducer": [ + "sum" + ], + "countRows": false, + "fields": "" }, - "viewMode": "list" + "sortBy": [ + { + "desc": true, + "displayName": "Severity" + } + ] } }, { diff --git a/stacks/observability/grafana/dashboards/host-overview.json b/stacks/observability/grafana/dashboards/host-overview.json index e558f97..e48c01a 100644 --- a/stacks/observability/grafana/dashboards/host-overview.json +++ b/stacks/observability/grafana/dashboards/host-overview.json @@ -62,12 +62,12 @@ }, "panels": [ { - "type": "alertlist", - "title": "Host alerts firing", - "description": "Firing and pending alerts carrying component=host, from the Alertmanager datasource — so a silenced alert shows as silenced rather than simply vanishing. Empty is the good state.", + "type": "table", + "title": "Host alerts", + "description": "Firing and pending alerts carrying component=host, from the ALERTS series Prometheus exposes for its own rules. Empty is the good state. Silences live in Alertmanager and are not reflected here — see the dashboard README note.", "datasource": { - "type": "alertmanager", - "uid": "alertmanager" + "type": "prometheus", + "uid": "prometheus" }, "gridPos": { "h": 6, @@ -75,23 +75,154 @@ "x": 0, "y": 0 }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "editorMode": "code", + "exemplar": false, + "expr": "ALERTS{component=\"host\",alertstate=~\"firing|pending\"}", + "format": "table", + "instant": true, + "legendFormat": "", + "range": false, + "refId": "A" + } + ], + "transformations": [ + { + "id": "organize", + "options": { + "excludeByName": { + "Time": true, + "__name__": true, + "Value": true, + "component": true, + "job": true, + "monitor": true, + "site": true, + "role": true + }, + "indexByName": {}, + "renameByName": { + "alertname": "Alert", + "alertstate": "State", + "severity": "Severity", + "category": "Category", + "device": "Device", + "instance": "Instance", + "vlan": "VLAN" + } + } + } + ], + "fieldConfig": { + "defaults": { + "custom": { + "align": "auto", + "filterable": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Severity" + }, + "properties": [ + { + "id": "mappings", + "value": [ + { + "type": "value", + "options": { + "critical": { + "color": "red", + "index": 0 + }, + "warning": { + "color": "orange", + "index": 1 + }, + "info": { + "color": "blue", + "index": 2 + } + } + } + ] + }, + { + "id": "custom.cellOptions", + "value": { + "type": "color-text" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "State" + }, + "properties": [ + { + "id": "mappings", + "value": [ + { + "type": "value", + "options": { + "firing": { + "color": "red", + "index": 0 + }, + "pending": { + "color": "yellow", + "index": 1 + } + } + } + ] + }, + { + "id": "custom.cellOptions", + "value": { + "type": "color-text" + } + } + ] + } + ] + }, "options": { - "alertInstanceLabelFilter": "{component=\"host\"}", - "alertName": "", - "dashboardAlerts": false, - "datasource": "alertmanager", - "groupBy": [], - "groupMode": "default", - "maxItems": 20, - "sortOrder": 1, - "stateFilter": { - "error": true, - "firing": true, - "noData": false, - "normal": false, - "pending": true + "showHeader": true, + "cellHeight": "sm", + "footer": { + "show": false, + "reducer": [ + "sum" + ], + "countRows": false, + "fields": "" }, - "viewMode": "list" + "sortBy": [ + { + "desc": true, + "displayName": "Severity" + } + ] } }, { diff --git a/stacks/observability/grafana/dashboards/logs-explorer.json b/stacks/observability/grafana/dashboards/logs-explorer.json index da6d80a..877415a 100644 --- a/stacks/observability/grafana/dashboards/logs-explorer.json +++ b/stacks/observability/grafana/dashboards/logs-explorer.json @@ -84,39 +84,6 @@ ] }, "panels": [ - { - "type": "alertlist", - "title": "Log-based alerts firing", - "description": "Firing and pending alerts carrying component=logs, from the Alertmanager datasource — so a silenced alert shows as silenced rather than simply vanishing. Empty is the good state.", - "datasource": { - "type": "alertmanager", - "uid": "alertmanager" - }, - "gridPos": { - "h": 6, - "w": 24, - "x": 0, - "y": 0 - }, - "options": { - "alertInstanceLabelFilter": "{component=\"logs\"}", - "alertName": "", - "dashboardAlerts": false, - "datasource": "alertmanager", - "groupBy": [], - "groupMode": "default", - "maxItems": 20, - "sortOrder": 1, - "stateFilter": { - "error": true, - "firing": true, - "noData": false, - "normal": false, - "pending": true - }, - "viewMode": "list" - } - }, { "type": "stat", "title": "Lines/s (all hosts)", @@ -129,7 +96,7 @@ "h": 4, "w": 5, "x": 0, - "y": 6 + "y": 0 }, "fieldConfig": { "defaults": { @@ -192,7 +159,7 @@ "h": 4, "w": 5, "x": 5, - "y": 6 + "y": 0 }, "fieldConfig": { "defaults": { @@ -262,7 +229,7 @@ "h": 4, "w": 5, "x": 10, - "y": 6 + "y": 0 }, "fieldConfig": { "defaults": { @@ -328,7 +295,7 @@ "h": 4, "w": 9, "x": 15, - "y": 6 + "y": 0 }, "fieldConfig": { "defaults": { @@ -394,7 +361,7 @@ "h": 1, "w": 24, "x": 0, - "y": 10 + "y": 4 }, "panels": [] }, @@ -410,7 +377,7 @@ "h": 8, "w": 12, "x": 0, - "y": 11 + "y": 5 }, "fieldConfig": { "defaults": { @@ -491,7 +458,7 @@ "h": 8, "w": 12, "x": 12, - "y": 11 + "y": 5 }, "fieldConfig": { "defaults": { @@ -572,7 +539,7 @@ "h": 8, "w": 12, "x": 0, - "y": 19 + "y": 13 }, "fieldConfig": { "defaults": { @@ -649,7 +616,7 @@ "h": 8, "w": 12, "x": 12, - "y": 19 + "y": 13 }, "fieldConfig": { "defaults": { @@ -726,7 +693,7 @@ "h": 1, "w": 24, "x": 0, - "y": 27 + "y": 21 }, "panels": [] }, @@ -742,7 +709,7 @@ "h": 11, "w": 24, "x": 0, - "y": 28 + "y": 22 }, "options": { "showTime": true, @@ -779,7 +746,7 @@ "h": 11, "w": 24, "x": 0, - "y": 39 + "y": 33 }, "options": { "showTime": true, @@ -816,7 +783,7 @@ "h": 12, "w": 24, "x": 0, - "y": 50 + "y": 44 }, "options": { "showTime": true, diff --git a/stacks/observability/grafana/dashboards/network-snmp.json b/stacks/observability/grafana/dashboards/network-snmp.json index 0b099ef..67c10b5 100644 --- a/stacks/observability/grafana/dashboards/network-snmp.json +++ b/stacks/observability/grafana/dashboards/network-snmp.json @@ -40,12 +40,12 @@ }, "panels": [ { - "type": "alertlist", - "title": "Network & firewall alerts firing", - "description": "Firing and pending alerts carrying component=network, from the Alertmanager datasource — so a silenced alert shows as silenced rather than simply vanishing. Empty is the good state.", + "type": "table", + "title": "Network & firewall alerts", + "description": "Firing and pending alerts carrying component=network, from the ALERTS series Prometheus exposes for its own rules. Empty is the good state. Silences live in Alertmanager and are not reflected here — see the dashboard README note.", "datasource": { - "type": "alertmanager", - "uid": "alertmanager" + "type": "prometheus", + "uid": "prometheus" }, "gridPos": { "h": 6, @@ -53,23 +53,154 @@ "x": 0, "y": 0 }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "editorMode": "code", + "exemplar": false, + "expr": "ALERTS{component=\"network\",alertstate=~\"firing|pending\"}", + "format": "table", + "instant": true, + "legendFormat": "", + "range": false, + "refId": "A" + } + ], + "transformations": [ + { + "id": "organize", + "options": { + "excludeByName": { + "Time": true, + "__name__": true, + "Value": true, + "component": true, + "job": true, + "monitor": true, + "site": true, + "role": true + }, + "indexByName": {}, + "renameByName": { + "alertname": "Alert", + "alertstate": "State", + "severity": "Severity", + "category": "Category", + "device": "Device", + "instance": "Instance", + "vlan": "VLAN" + } + } + } + ], + "fieldConfig": { + "defaults": { + "custom": { + "align": "auto", + "filterable": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Severity" + }, + "properties": [ + { + "id": "mappings", + "value": [ + { + "type": "value", + "options": { + "critical": { + "color": "red", + "index": 0 + }, + "warning": { + "color": "orange", + "index": 1 + }, + "info": { + "color": "blue", + "index": 2 + } + } + } + ] + }, + { + "id": "custom.cellOptions", + "value": { + "type": "color-text" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "State" + }, + "properties": [ + { + "id": "mappings", + "value": [ + { + "type": "value", + "options": { + "firing": { + "color": "red", + "index": 0 + }, + "pending": { + "color": "yellow", + "index": 1 + } + } + } + ] + }, + { + "id": "custom.cellOptions", + "value": { + "type": "color-text" + } + } + ] + } + ] + }, "options": { - "alertInstanceLabelFilter": "{component=\"network\"}", - "alertName": "", - "dashboardAlerts": false, - "datasource": "alertmanager", - "groupBy": [], - "groupMode": "default", - "maxItems": 20, - "sortOrder": 1, - "stateFilter": { - "error": true, - "firing": true, - "noData": false, - "normal": false, - "pending": true + "showHeader": true, + "cellHeight": "sm", + "footer": { + "show": false, + "reducer": [ + "sum" + ], + "countRows": false, + "fields": "" }, - "viewMode": "list" + "sortBy": [ + { + "desc": true, + "displayName": "Severity" + } + ] } }, { diff --git a/stacks/observability/grafana/dashboards/ups-power.json b/stacks/observability/grafana/dashboards/ups-power.json index 3a8af73..1ada9af 100644 --- a/stacks/observability/grafana/dashboards/ups-power.json +++ b/stacks/observability/grafana/dashboards/ups-power.json @@ -40,12 +40,12 @@ }, "panels": [ { - "type": "alertlist", - "title": "Power alerts firing", - "description": "Firing and pending alerts carrying component=power, from the Alertmanager datasource — so a silenced alert shows as silenced rather than simply vanishing. Empty is the good state.", + "type": "table", + "title": "Power alerts", + "description": "Firing and pending alerts carrying component=power, from the ALERTS series Prometheus exposes for its own rules. Empty is the good state. Silences live in Alertmanager and are not reflected here — see the dashboard README note.", "datasource": { - "type": "alertmanager", - "uid": "alertmanager" + "type": "prometheus", + "uid": "prometheus" }, "gridPos": { "h": 6, @@ -53,23 +53,154 @@ "x": 0, "y": 0 }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "editorMode": "code", + "exemplar": false, + "expr": "ALERTS{component=\"power\",alertstate=~\"firing|pending\"}", + "format": "table", + "instant": true, + "legendFormat": "", + "range": false, + "refId": "A" + } + ], + "transformations": [ + { + "id": "organize", + "options": { + "excludeByName": { + "Time": true, + "__name__": true, + "Value": true, + "component": true, + "job": true, + "monitor": true, + "site": true, + "role": true + }, + "indexByName": {}, + "renameByName": { + "alertname": "Alert", + "alertstate": "State", + "severity": "Severity", + "category": "Category", + "device": "Device", + "instance": "Instance", + "vlan": "VLAN" + } + } + } + ], + "fieldConfig": { + "defaults": { + "custom": { + "align": "auto", + "filterable": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Severity" + }, + "properties": [ + { + "id": "mappings", + "value": [ + { + "type": "value", + "options": { + "critical": { + "color": "red", + "index": 0 + }, + "warning": { + "color": "orange", + "index": 1 + }, + "info": { + "color": "blue", + "index": 2 + } + } + } + ] + }, + { + "id": "custom.cellOptions", + "value": { + "type": "color-text" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "State" + }, + "properties": [ + { + "id": "mappings", + "value": [ + { + "type": "value", + "options": { + "firing": { + "color": "red", + "index": 0 + }, + "pending": { + "color": "yellow", + "index": 1 + } + } + } + ] + }, + { + "id": "custom.cellOptions", + "value": { + "type": "color-text" + } + } + ] + } + ] + }, "options": { - "alertInstanceLabelFilter": "{component=\"power\"}", - "alertName": "", - "dashboardAlerts": false, - "datasource": "alertmanager", - "groupBy": [], - "groupMode": "default", - "maxItems": 20, - "sortOrder": 1, - "stateFilter": { - "error": true, - "firing": true, - "noData": false, - "normal": false, - "pending": true + "showHeader": true, + "cellHeight": "sm", + "footer": { + "show": false, + "reducer": [ + "sum" + ], + "countRows": false, + "fields": "" }, - "viewMode": "list" + "sortBy": [ + { + "desc": true, + "displayName": "Severity" + } + ] } }, {