UNPKG

openclaw-grafana-lens

Version:

OpenClaw plugin that gives AI agents full Grafana access — 18 composable tools for PromQL/LogQL/TraceQL queries, dashboard creation, alerting, SRE investigation, security monitoring, data collection pipeline management via Grafana Alloy (29 recipes), and

368 lines (307 loc) 14.3 kB
# Dashboard Composition Guide How to choose templates, compose custom dashboards, and select the right panel types for any metric. ## Template Selection Pick the right template based on what the user is asking: | User mentions | Template | Why | |--------------|----------|-----| | Server, CPU, memory, disk, system health | `node-exporter` | Pre-built system panels with `$instance` filter | | HTTP, API, requests, errors, latency, RED | `http-service` | Golden signals with `$job` filter | | "Explore", single metric deep-dive | `metric-explorer` | 6 angles on any single `$metric` | | Multiple KPIs, overview, top-N metrics | `multi-kpi` | 4-metric dashboard with `$metric1`..`$metric4` | | Agent health, cost, latency, errors, live feeds | `llm-command-center` | Single-pane AI health with `$provider`, `$model`, `$channel` filters | | Per-session drill-down, trace hierarchy | `session-explorer` | Session debug with `$session` textbox + Tempo traces | | Cost trends, model attribution, cache savings | `cost-intelligence` | Financial analysis with `$provider`, `$model` filters | | Tool reliability, latency ranking, SRE | `tool-performance` | Tool leaderboard with `$tool` filter + Tempo traces | | Weekly review, calendar, commits, habits | `weekly-review` | Cross-domain life dashboard | | Nothing fits | **Custom JSON** | Compose using the rules below | **All templates have dropdown selectors** in Grafana. AI templates (`llm-command-center`, `session-explorer`, `cost-intelligence`, `tool-performance`) also have Loki log-to-trace correlation via Tempo. ## Metric Type Panel Type Use `grafana_list_metrics` with `metadata: true` to learn metric types before composing. | Metric Type | Best Panel | PromQL Pattern | Example | |-------------|-----------|----------------|---------| | counter | timeseries (rate) | `rate(metric[5m])` | `rate(http_requests_total[5m])` | | counter (total) | stat | `sum(metric)` | `sum(orders_total)` | | gauge (current) | stat or gauge | `metric` | `sensor_temperature_celsius` | | gauge (over time) | timeseries | `metric` | `portfolio_value_usd` | | histogram (percentiles) | timeseries | `histogram_quantile(0.95, sum(rate(metric_bucket[5m])) by (le))` | HTTP latency p95 | | histogram (heatmap) | heatmap | `sum(rate(metric_bucket[5m])) by (le)` | Request duration distribution | **Important**: `histogram_quantile` MUST include `sum(...) by (le)` to aggregate across label dimensions. Without it, percentile calculations are incorrect when multiple series exist (e.g., per-model, per-channel). ## Unit Detection from Metric Name Suffix-based unit selection for `fieldConfig.defaults.unit`: | Suffix | Unit | Example Metric | |--------|------|---------------| | `_bytes` | `bytes` | `node_memory_MemTotal_bytes` | | `_seconds` | `s` | `http_request_duration_seconds` | | `_total` | `short` | `http_requests_total` | | `_total` (in rate()) | `reqps` or `ops` | `rate(http_requests_total[5m])` | | `_usd` | `currencyUSD` | `openclaw_cost_usd_total` | | `_ratio` | `percentunit` | `cache_hit_ratio` | | `_celsius` | `celsius` | `sensor_temperature_celsius` | | `_percent` | `percent` | `cpu_usage_percent` | | `_bpm` | `short` | `fitness_heart_rate_bpm` | | no suffix | `short` | `up`, `active_users` | ## Variable-Based Panel JSON All custom panels should reference `$datasource` so users can switch datasources in the UI. ### Stat Panel ```json { "id": 1, "title": "Current Orders", "type": "stat", "gridPos": { "h": 4, "w": 6, "x": 0, "y": 0 }, "datasource": { "type": "prometheus", "uid": "$datasource" }, "targets": [{ "expr": "sum(orders_total)", "legendFormat": "orders", "refId": "A" }], "fieldConfig": { "defaults": { "unit": "short" } } } ``` ### Timeseries (Line) ```json { "id": 2, "title": "Request Rate", "type": "timeseries", "gridPos": { "h": 8, "w": 12, "x": 0, "y": 4 }, "datasource": { "type": "prometheus", "uid": "$datasource" }, "targets": [{ "expr": "rate(http_requests_total[5m])", "legendFormat": "{{ handler }}", "refId": "A" }], "fieldConfig": { "defaults": { "unit": "reqps", "custom": { "fillOpacity": 10 } } } } ``` ### Timeseries (Stacked Bars) ```json { "id": 3, "title": "Response Codes", "type": "timeseries", "gridPos": { "h": 8, "w": 12, "x": 12, "y": 4 }, "datasource": { "type": "prometheus", "uid": "$datasource" }, "targets": [{ "expr": "sum(rate(http_requests_total[5m])) by (code)", "legendFormat": "{{ code }}", "refId": "A" }], "fieldConfig": { "defaults": { "unit": "reqps", "custom": { "fillOpacity": 50, "stacking": { "mode": "normal" } } } } } ``` ### Gauge Panel (with thresholds) ```json { "id": 4, "title": "CPU Usage", "type": "gauge", "gridPos": { "h": 6, "w": 6, "x": 0, "y": 0 }, "datasource": { "type": "prometheus", "uid": "$datasource" }, "targets": [{ "expr": "cpu_usage_percent", "refId": "A" }], "fieldConfig": { "defaults": { "unit": "percent", "min": 0, "max": 100, "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": null }, { "color": "yellow", "value": 70 }, { "color": "red", "value": 90 } ] } } } } ``` ### Piechart ```json { "id": 5, "title": "Cost by Model", "type": "piechart", "gridPos": { "h": 8, "w": 12, "x": 0, "y": 12 }, "datasource": { "type": "prometheus", "uid": "$datasource" }, "targets": [{ "expr": "sum(openclaw_cost_usd_total) by (openclaw_model)", "legendFormat": "{{ openclaw_model }}", "refId": "A", "instant": true }] } ``` ### Table ```json { "id": 6, "title": "All Series", "type": "table", "gridPos": { "h": 8, "w": 12, "x": 12, "y": 12 }, "datasource": { "type": "prometheus", "uid": "$datasource" }, "targets": [{ "expr": "up", "format": "table", "instant": true, "refId": "A" }] } ``` ### Logs Panel (Loki) For dashboards with Loki datasources. Use `$loki` variable for multi-signal dashboards. ```json { "id": 7, "title": "Error Logs", "type": "logs", "gridPos": { "h": 10, "w": 12, "x": 0, "y": 0 }, "datasource": { "type": "loki", "uid": "$loki" }, "targets": [{ "expr": "{service_name=\"openclaw\"} | logfmt | level=~\"ERROR|WARN\"", "refId": "A" }], "options": { "showTime": true, "showLabels": false, "showCommonLabels": false, "wrapLogMessage": true, "prettifyLogMessage": false, "enableLogDetails": true, "sortOrder": "Descending", "dedupStrategy": "none" } } ``` ### Traces Panel (Tempo) For dashboards with Tempo datasources. Use `$tempo` variable for multi-signal dashboards. Uses `traceqlSearch` query type with structured filters. ```json { "id": 8, "title": "Recent Traces", "type": "traces", "gridPos": { "h": 10, "w": 12, "x": 12, "y": 0 }, "datasource": { "type": "tempo", "uid": "$tempo" }, "targets": [{ "queryType": "traceqlSearch", "filters": [ { "id": "service-name", "tag": "service.name", "operator": "=", "value": ["openclaw"], "scope": "resource" }, { "id": "span-name", "tag": "name", "operator": "=~", "value": ["invoke_agent.*"], "scope": "span" } ], "limit": 20, "refId": "A" }] } ``` **Multi-signal datasource variables**: When combining Prometheus, Loki, and Tempo in one dashboard, use separate datasource variables (`$prometheus`, `$loki`, `$tempo`) instead of a single `$datasource`. Each panel references the variable matching its datasource type. ## Grid Layout Rules Grafana uses a 24-column grid: ``` Row panel (y:0): section header (w:24, h:1) — collapsible Row 0 (y:1): 6× stat panels (w:4, h:3) — summary KPIs Row panel (y:4): section header (w:24, h:1) Row 1 (y:5): 2× timeseries (w:12, h:8) — main charts Row panel (y:13): section header (w:24, h:1) Row 2 (y:14): 2× detail panels (w:12, h:8) — breakdowns, tables ``` ### Row Panels (Section Headers) ```json { "id": 1, "title": "Section Name", "type": "row", "gridPos": { "h": 1, "w": 24, "x": 0, "y": 0 }, "collapsed": false, "panels": [] } ``` ### Panel Conventions - **Descriptions**: Every panel should have a `"description"` field explaining what it shows, healthy ranges, and when to investigate - **Table legends**: Timeseries panels should use `"options": { "legend": { "displayMode": "table", "placement": "bottom", "calcs": ["lastNotNull", "mean", "max"] } }` - **Stat sparklines**: Add `"options": { "graphMode": "area" }` to stat panels where trends matter - **Background color**: Add `"options": { "colorMode": "background" }` to critical stat panels with thresholds - **Threshold mode**: Always include `"mode": "absolute"` in threshold blocks for Grafana 10+ compatibility - **spanNulls**: Add `"custom": { "spanNulls": true }` to timeseries to prevent broken lines during metric gaps - **Dashboard links**: Add `"links"` with tag-based discovery for dashboard navigation - **Annotations**: Add `"annotations.list"` with built-in Grafana annotations for deployment markers - **Piechart queries**: Use `"instant": true` on piechart targets to show current proportions - **Histogram percentiles**: Always use `sum(...) by (le)` inside `histogram_quantile` — e.g., `histogram_quantile(0.95, sum(rate(metric_bucket[5m])) by (le))` ## Dashboard JSON Skeleton When composing a custom dashboard, wrap panels in this structure: ```json { "title": "My Dashboard", "tags": ["grafana-lens"], "timezone": "browser", "editable": true, "schemaVersion": 39, "time": { "from": "now-24h", "to": "now" }, "refresh": "30s", "links": [ { "title": "Related Dashboards", "tags": ["grafana-lens"], "type": "dashboards", "asDropdown": true, "icon": "external link" } ], "annotations": { "list": [ { "builtIn": 1, "datasource": { "type": "grafana", "uid": "-- Grafana --" }, "enable": true, "hide": true, "iconColor": "rgba(0, 211, 255, 1)", "name": "Annotations & Alerts", "type": "dashboard" } ] }, "templating": { "list": [ { "name": "datasource", "type": "datasource", "query": "prometheus", "current": {}, "hide": 0 } ] }, "panels": [ ] } ``` ## Domain Worked Examples ### E-commerce Dashboard **Discovery**: `grafana_list_metrics` with `metadata: true`, `prefix: "orders_"` or `"revenue_"` **Template choice**: `multi-kpi` for quick overview, or custom for specific layout. **Custom composition** (if `multi-kpi` isn't enough): | Panel | Type | PromQL | Unit | |-------|------|--------|------| | Total Revenue | stat | `sum(revenue_usd_total)` | `currencyUSD` | | Orders/min | stat | `sum(rate(orders_total[5m])) * 60` | `short` | | Cart Abandonment | gauge | `cart_abandonment_ratio * 100` | `percent` | | Revenue Over Time | timeseries | `sum(rate(revenue_usd_total[1h])) by (product)` | `currencyUSD` | | Orders by Region | timeseries (stacked) | `sum(rate(orders_total[5m])) by (region)` | `ops` | ### IoT / Sensor Monitoring **Discovery**: `grafana_list_metrics` with `prefix: "sensor_"` **Template choice**: `metric-explorer` for one sensor, `multi-kpi` for overview of 4 sensors. **Custom composition**: | Panel | Type | PromQL | Unit | |-------|------|--------|------| | Temperature | gauge | `sensor_temperature_celsius` | `celsius` | | Humidity | gauge | `sensor_humidity_ratio * 100` | `percent` | | Temperature Over Time | timeseries | `sensor_temperature_celsius` | `celsius` | | Battery Voltage | timeseries | `sensor_battery_volts` | `volt` | ### Fitness / Health Tracking **Discovery**: `grafana_list_metrics` with `prefix: "fitness_"` or `"health_"` **Template choice**: `multi-kpi` with steps, heart rate, sleep hours, calories. **Custom composition**: | Panel | Type | PromQL | Unit | |-------|------|--------|------| | Steps Today | stat | `fitness_steps_total` | `short` | | Heart Rate | gauge | `fitness_heart_rate_bpm` | `short` (suffix: bpm) | | Steps Over Time | timeseries | `rate(fitness_steps_total[1h]) * 3600` | `short` | | Sleep Hours | timeseries | `health_sleep_hours` | `short` | ### Financial / Portfolio **Discovery**: `grafana_list_metrics` with `prefix: "portfolio_"` or `"crypto_"` **Template choice**: `metric-explorer` for deep-dive on one asset, `multi-kpi` for portfolio overview. **Custom composition**: | Panel | Type | PromQL | Unit | |-------|------|--------|------| | Portfolio Value | stat | `portfolio_value_usd` | `currencyUSD` | | BTC Price | stat | `crypto_price_usd{coin="btc"}` | `currencyUSD` | | Portfolio Over Time | timeseries | `portfolio_value_usd` | `currencyUSD` | | Asset Allocation | piechart | `portfolio_allocation_usd` by asset (instant) | `currencyUSD` | ### Redis / Database **Discovery**: `grafana_list_metrics` with `metadata: true`, `prefix: "redis_"` **Template choice**: CustomRedis has specific panel needs. **Custom composition**: | Panel | Type | PromQL | Unit | |-------|------|--------|------| | Commands/sec | stat | `rate(redis_commands_processed_total[5m])` | `ops` | | Memory Used | gauge | `redis_memory_used_bytes / redis_memory_max_bytes * 100` | `percent` | | Connected Clients | stat | `redis_connected_clients` | `short` | | Command Rate Over Time | timeseries | `rate(redis_commands_processed_total[5m])` | `ops` | | Memory Over Time | timeseries | `redis_memory_used_bytes` | `bytes` | | Hit Rate | timeseries | `rate(redis_keyspace_hits_total[5m]) / (rate(redis_keyspace_hits_total[5m]) + rate(redis_keyspace_misses_total[5m]))` | `percentunit` | ### CI/CD Pipeline **Discovery**: `grafana_list_metrics` with `prefix: "ci_"` or `"github_"` **Template choice**: Custom for specific CI workflow. **Custom composition**: | Panel | Type | PromQL | Unit | |-------|------|--------|------| | Builds Today | stat | `sum(ci_builds_total)` | `short` | | Success Rate | gauge | `sum(rate(ci_builds_total{status="success"}[24h])) / sum(rate(ci_builds_total[24h])) * 100` | `percent` | | Build Duration p95 | stat | `histogram_quantile(0.95, sum(rate(ci_build_duration_seconds_bucket[1h])) by (le))` | `s` | | Build Duration Over Time | timeseries | p50/p95/p99 of `ci_build_duration_seconds` | `s` | | Builds by Status | timeseries (stacked) | `sum(rate(ci_builds_total[1h])) by (status)` | `ops` |