Gráficos de métricas de Grafana (Charting Grafana metrics)
Resumen
Renderiza un gráfico de líneas PNG con tema oscuro, estilo Grafana, a partir de una consulta PromQL obtenida a través del proxy de datasource de Grafana (sin necesidad de acceso directo a Prometheus, basta con el token SA de Grafana). Reproducible y automatizable, a diferencia de una captura de pantalla manual. Puede adjuntar el resultado directamente a un ticket de Jira.
Haz primero una captura de pantalla si ya existe un panel que muestre los datos. Las pilas de Grafana Cloud incluyen renderizado del lado del servidor, por lo que gcx dashboards snapshot devuelve el panel real como PNG. Esta habilidad es para lo que una captura no puede ofrecer: una comparación lado a lado entre consultas o rangos de tiempo que ningún panel codifica, e instancias auto-gestionadas sin el plugin Image Renderer, donde la captura falla.
Requisitos previos
Cada llamada a Grafana aquí pasa por gcx. Verifícalo una vez por sesión:
command -v gcx >/dev/null || { echo "instala: brew install gcx"; exit 1; }
gcx config check || { echo "no listo: gcx login"; exit 1; }
Asegúrate de que la consulta sea correcta antes de graficar. Esta habilidad renderiza cualquier PromQL que le pases, y una consulta errónea es peor aquí que en un panel: el PNG termina en un ticket como evidencia congelada que nadie reproduce, y tiene apariencia oficial. CONOCIMIENTOS REQUERIDOS para la consulta: usa la habilidad grafana-dashboards, secciones Fundamentos de PromQL, Trampas de agregación y Lectura de distribuciones y colas. Errores comunes en gráficos comparativos: agregar después de rate, no antes; envolver ambos lados con max by (...) cuando un despliegue deja dos series por pod; nunca usar histogram_quantile sobre un histograma exponencial base 2; y dar a cada subconsulta un paso (step) explícito.
Cuándo usarlo
- Comparar 2+ series: versión A vs B, canario vs control, antes/después de un cambio.
- Adjuntar evidencia a un ticket de Jira, MR o informe de incidente.
- Fallo al renderizar un panel de Grafana (
gcx dashboards snapshotda error: no hay Image Renderer en ese contexto auto-gestionado).
No para: exploración interactiva (usa Grafana), datos de valor único/tabla, o captura de pantalla de un panel existente en Grafana Cloud (gcx dashboards snapshot).
Configuración (una vez)
Se requiere matplotlib. Si falta, usa un entorno virtual aislado (no contaminar el Python del sistema):
python3 -m venv ${CLAUDE_SKILL_DIR}/.venv
${CLAUDE_SKILL_DIR}/.venv/bin/pip install -q matplotlib
Referencia rápida
| Necesidad | Indicador |
|---|---|
| Qué datasource | --datasource-uid (usa gcx datasources list o consultas de paneles) |
| La consulta | --expr 'PromQL' |
| Ventana de tiempo | --start now-6h --end now --step 60 |
| Fuente de la etiqueta de serie | --legend-key instance (por defecto; cualquier etiqueta de métrica) |
| Nombres más bonitos | --rename '{"raw-value":"Nombre Bonito"}' |
| Media/mín/máx por serie | se imprime en stdout después de guardar (sin necesidad de consulta adicional) |
| Anotación en el pico | --annotate-max "texto" |
| Qué Grafana | --grafana-url https://<host>: usa el contexto de gcx cuyo servidor tenga ese host (por defecto: el contexto actual de gcx) |
| Qué contexto gcx | --gcx-context <nombre> (por defecto $GCX_CONTEXT); para una instancia nombrada sin URL, elígelo de gcx config list-contexts |
| Adjuntar a Jira | --attach-jira ABC-123 (ver las variables de entorno y la puerta de aprobación en Flujo de trabajo) |
Entorno
Nada está codificado: sin host, sin dirección. Establece estas en tu perfil de shell.
| Variable | Necesaria para | Notas |
|---|---|---|
GRAFANA_URL + GRAFANA_TOKEN | token estático | el token solo se envía al host de GRAFANA_URL (luego GRAFANA_SERVICE_ACCOUNT_TOKEN, GTOK) |
GCX_CONTEXT | gcx | contexto gcx a forzar, igual que --gcx-context |
JIRA_API_TOKEN JIRA_EMAIL JIRA_BASE | solo --attach-jira | los tres, sin valor predeterminado |
Orden de resolución, idéntico en todas las habilidades de Grafana: --token; --gcx-context / $GCX_CONTEXT; --grafana-url (el token de entorno para el host de $GRAFANA_URL, o el contexto gcx que sirve ese host, o salir con el gcx login a ejecutar); el contexto actual de gcx; el token de entorno cuando falta gcx. A través de gcx, su propio refresco OAuth se aplica y este script nunca lee ni almacena en caché una credencial. El solucionador se duplica en cada habilidad a propósito, para que funcione instalada sola.
Flujo de trabajo
- Obtén el UID del datasource y la PromQL del panel (
gcx api /api/dashboards/uid/<uid>, leepanels[*].targets[*].expr) o escribe la consulta tú mismo. - Ejecuta el script (con el python del entorno virtual). Los colores se asignan automáticamente de la paleta de Grafana en orden de serie; la leyenda muestra la media/máx por serie.
- Revisa el PNG visualmente antes de compartir.
- Opcionalmente, adjunta a Jira con
--attach-jira. Es una escritura en el rastreador visible para todo el equipo: sigue la puerta de aprobación de la habilidadticket-conventionsprimero (muestra el ID del ticket y el PNG, espera un sí explícito). Una subida fallida sale con código distinto de cero.
Ejemplo (el canónico: memoria de nodo HAProxy 3.2 vs 2.7)
VENV=${CLAUDE_SKILL_DIR}/.venv/bin/python
SKILL=${CLAUDE_SKILL_DIR}
export JIRA_API_TOKEN=$(zsh -l -c 'echo $JIRA_API_TOKEN') # solo si adjuntas
export [email protected] JIRA_BASE=https://ejemplo.atlassian.net
DS_UID=abc123XYZ # de `gcx datasources list`
$VENV $SKILL/plot_grafana.py \
--datasource-uid $DS_UID \
--expr '(1 - (node_memory_MemAvailable_bytes{instance=~"lb-edge-dc1-(1|4)"} / node_memory_MemTotal_bytes{instance=~"lb-edge-dc1-(1|4)"})) * 100' \
--start now-6h --step 60 \
--title "lb-edge - % memoria usada nodo · 3.2 vs 2.7 (dc1, últ 6h)" \
--ylabel "% memoria usada" --ymin 0 --ymax 45 \
--rename '{"lb-edge-dc1-1":"HAProxy 3.2.20 (dc1-1)","lb-edge-dc1-4":"HAProxy 2.7.11 (dc1-4, control)"}' \
--annotate-max "recarga: worker antiguo+nuevo coexisten → memoria nodo ~2x" \
--grafana-url https://grafana.ejemplo.com \
--out ./mem-3.2-vs-2.7.png \
--attach-jira ABC-123
Errores comunes
- Gráfico dibujado a partir de una consulta que nadie verificó → ver la indicación de PromQL arriba; un número incorrecto pero plausible es el modo de fallo, no un error.
- 401 del proxy → con un token estático, no pertenece a ese host; a través de gcx, el token de actualización expiró (aproximadamente mensual), ejecuta
gcx login <contexto>. - Usar
python3del sistema →ModuleNotFoundError: matplotlib. Usa el python del entorno virtual. - Línea vacía/plana donde esperas varias → la expresión regular de
--exprcoincidió con una sola serie; amplía el filtro de etiquetas. - Series sin nombre / todo "series" →
--legend-keyapunta a una etiqueta que la métrica no tiene; elige una que sí tenga (verifica el resultado de la consulta cruda). :9100(o cualquier:puerto) en nombres de leyenda → se elimina automáticamente del nombre de serie derivado, así que--renameapunta al host limpio (ej.lb-edge-dc1-1, nolb-edge-dc1-1:9100). No es necesario listar ambas formas.- Los tiempos relativos toman un número y una unidad, s/m/h/d (
now-90m,now-6h,now-2d). Compuestos (now-1h30m), semanas (now-1w) y redondeos (now/d) no son compatibles: conviértelos (now-90m,now-7d).