Query and manage Grafana dashboards and Prometheus metrics for Happy infrastructure. Covers grafanactl CLI usage, direct Prometheus queries through Grafana proxy, and dashboard-as-code workflows. Use when user asks about metrics, dashboards, monitoring, Grafana, Prometheus, or wants to add/modify panels.
Scanned 9/2/2026
Install to Claude Code
npx -y skills add Mereithhh/very-happy --skill metrics-graphana --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Metrics Graphana?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/mereithhh-metrics-graphana)More formats (shields.io, HTML) on the badges page.
---
name: metrics-graphana
description: >
Query and manage Grafana dashboards and Prometheus metrics for Happy infrastructure.
Covers grafanactl CLI usage, direct Prometheus queries through Grafana proxy,
and dashboard-as-code workflows. Use when user asks about metrics, dashboards,
monitoring, Grafana, Prometheus, or wants to add/modify panels.
---
# Metrics & Grafana
You are the observability operator for the Happy infrastructure. You can query live Prometheus metrics, manage Grafana dashboards as code, and investigate production behavior.
## Environment Variables
Credentials are stored in the repo root `.env` file (gitignored). Load them before running commands:
```
GRAFANA_URL=...
GRAFANA_USER=...
GRAFANA_PASSWORD=...
GRAFANA_PROMETHEUS_UID=...
```
To load in shell:
```bash
set -a; source .env; set +a
```
All commands below use `$GRAFANA_URL`, `$GRAFANA_USER`, `$GRAFANA_PASSWORD`, and `$GRAFANA_PROMETHEUS_UID` from the environment.
---
## Prerequisites
### Install grafanactl
```bash
go install github.com/grafana/grafanactl/cmd/grafanactl@latest
```
Ensure `$HOME/go/bin` is on your PATH.
### Configure grafanactl
```bash
# Load env vars first
set -a; source .env; set +a
# Create a context for the Happy Grafana instance
grafanactl config set contexts.happy.grafana.server "$GRAFANA_URL"
grafanactl config set contexts.happy.grafana.user "$GRAFANA_USER"
grafanactl config set contexts.happy.grafana.password "$GRAFANA_PASSWORD"
grafanactl config set contexts.happy.grafana.org-id 1
# Switch to the context
grafanactl config use-context happy
# Verify
grafanactl config check
```
Config file lives at `~/Library/Application Support/grafanactl/config.yaml` (macOS) or `~/.config/grafanactl/config.yaml` (Linux).
---
## grafanactl CLI Reference
### List resources
```bash
grafanactl resources list # List all resource types
grafanactl resources get dashboards # List all dashboards
grafanactl resources get folders # List all folders
```
### Pull dashboards (export to disk)
```bash
grafanactl resources pull dashboards -p ./resources -o json
grafanactl resources pull dashboards/DASHBOARD_ID -p ./resources -o json
```
### Push dashboards (deploy from disk)
```bash
# Push all dashboards from ./resources
grafanactl resources push dashboards -p ./resources
# Push a specific dashboard
grafanactl resources push dashboards/DASHBOARD_ID -p ./resources
# IMPORTANT: Use --omit-manager-fields to keep dashboards editable from the Grafana UI
grafanactl resources push dashboards -p ./resources --omit-manager-fields
# Dry run (no changes)
grafanactl resources push dashboards -p ./resources --dry-run
```
### Workflow: Edit a dashboard
```bash
# 1. Pull current state
mkdir -p /tmp/grafana-work
grafanactl resources pull dashboards -p /tmp/grafana-work -o json
# 2. Edit the JSON files (add panels, modify queries, etc.)
# 3. Push back — always use --omit-manager-fields to avoid locking the UI
grafanactl resources push dashboards -p /tmp/grafana-work --omit-manager-fields
```
> **Warning:** Pushing without `--omit-manager-fields` marks the dashboard as "provisioned"
> and locks it from UI edits. Always include this flag unless you explicitly want CLI-only management.
---
## Querying Prometheus Directly
You can query Prometheus through Grafana's datasource proxy API. This is useful for live investigation without touching the Grafana UI.
### Instant query (current value)
```bash
curl -s -u "$GRAFANA_USER:$GRAFANA_PASSWORD" \
--data-urlencode 'query=YOUR_PROMQL_HERE' \
"$GRAFANA_URL/api/datasources/proxy/uid/$GRAFANA_PROMETHEUS_UID/api/v1/query" \
| python3 -m json.tool
```
### Range query (time series)
```bash
curl -s -u "$GRAFANA_USER:$GRAFANA_PASSWORD" \
--data-urlencode 'query=YOUR_PROMQL_HERE' \
--data-urlencode 'start=UNIX_TIMESTAMP' \
--data-urlencode 'end=UNIX_TIMESTAMP' \
--data-urlencode 'step=60' \
"$GRAFANA_URL/api/datasources/proxy/uid/$GRAFANA_PROMETHEUS_UID/api/v1/query_range" \
| python3 -m json.tool
```
### List all metric names
```bash
curl -s -u "$GRAFANA_USER:$GRAFANA_PASSWORD" \
"$GRAFANA_URL/api/datasources/proxy/uid/$GRAFANA_PROMETHEUS_UID/api/v1/label/__name__/values" \
| python3 -c "import json,sys; [print(n) for n in json.load(sys.stdin)['data']]"
```
### Filter metric names
```bash
# Find all RPC-related metrics
curl -s -u "$GRAFANA_USER:$GRAFANA_PASSWORD" \
"$GRAFANA_URL/api/datasources/proxy/uid/$GRAFANA_PROMETHEUS_UID/api/v1/label/__name__/values" \
| python3 -c "import json,sys; [print(n) for n in json.load(sys.stdin)['data'] if 'rpc' in n.lower()]"
```
---
## Key Metrics
### Application metrics (handy-server)
| Metric | Type | Description |
|--------|------|-------------|
| `rpc_calls_total` | counter | RPC calls by method and result (success, not_available, target_disconnected, timeout) |
| `rpc_call_duration_seconds_bucket` | histogram | RPC call duration by method |
| `rpc_lookup_retries_bucket` | histogram | Number of retries per socket lookup by method |
| `rpc_fetchsockets_timeouts_total` | counter | fetchSockets timeout count by context (lookup, presence) |
| `websocket_connections_total` | gauge | Active WebSocket connections by type |
| `websocket_events_total` | counter | WebSocket events by type |
| `http_requests_total` | counter | HTTP requests by method, route, status |
| `http_request_duration_seconds_bucket` | histogram | HTTP request duration by route |
| `session_cache_operations_total` | counter | Session cache hits/misses by operation |
| `session_alive_events_total` | counter | Session keepalive events |
| `machine_alive_events_total` | counter | Machine keepalive events |
| `database_records_total` | gauge | Record counts by table |
| `database_updates_skipped_total` | counter | Skipped DB updates by type |
### Useful PromQL queries
```promql
# RPC success rate by method
sum by(method) (rate(rpc_calls_total{result="success"}[5m]))
/ (sum by(method) (rate(rpc_calls_total[5m])))
# RPC failures by method and reason
sum by (method, result) (rate(rpc_calls_total{result!="success"}[5m]))
# RPC failures by type only
sum by (result) (rate(rpc_calls_total{result!="success"}[5m]))
# RPC P95 latency by method
histogram_quantile(0.95, sum by (method, le) (rate(rpc_call_duration_seconds_bucket[5m])))
# Socket lookup retry distribution (P95)
histogram_quantile(0.95, sum by (method, le) (rate(rpc_lookup_retries_bucket[5m])))
# fetchSockets timeout rate by context
sum by (context) (rate(rpc_fetchsockets_timeouts_total[5m]))
# HTTP error rate
sum(rate(http_requests_total{status=~"5.."}[5m])) / sum(rate(http_requests_total[5m]))
# Top routes by request rate
topk(10, sum by(method, route) (rate(http_requests_total[5m])))
```
---
## Dashboards
### Happy Server Application Metrics
- **ID:** `470da978-91f7-4721-be2c-cc451bf074a2`
- **Tags:** happy-server, application, websocket, http, database
- **Panels:** WebSocket connections, session cache, alive events, HTTP metrics, database stats, RPC metrics
### Adding a panel
When adding panels to a dashboard JSON, follow this pattern:
1. Use the Prometheus datasource: `{"type": "prometheus", "uid": "$GRAFANA_PROMETHEUS_UID"}`
2. Pick the next available `id` (check existing panels for max id)
3. Position with `gridPos`: `h` = height (8 standard), `w` = width (12 half, 24 full), `x` = column (0 or 12), `y` = row
4. Common panel types: `stat`, `timeseries`, `piechart`, `bargauge`, `table`
5. Set appropriate `unit`: `percentunit`, `ops`, `s`, `short`, `reqps`
---
## Tips
- Always pull before editing to get the latest state
- Use `--dry-run` on push to preview changes
- The `--omit-manager-fields` flag is essential for hybrid CLI+UI workflows
- Range queries need Unix timestamps — use `date +%s` to get current time
- When investigating metrics, start with instant queries for current state, then use range queries for trends
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!