feat(infra): auto-commit con 6 cambios
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
---
|
||||
name: parse_nats_monitor
|
||||
kind: function
|
||||
lang: go
|
||||
domain: infra
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "func ParseNatsMonitor(node string, varz, connz, jsz []byte) ([]PromSample, error)"
|
||||
description: "Convierte las respuestas JSON del endpoint de monitoring HTTP embebido de un nats-server (puerto 8222, loopback) en una serie de PromSample lista para empujar a VictoriaMetrics. Hermana de ParseUnibusHealth pero para las métricas server-level de NATS/JetStream: msgs/s, bytes, conexiones, slow consumers, memoria RSS, start epoch (proxy de reinicios), streams/messages/bytes/memory/storage de JetStream, y por stream nats_stream_messages/bytes, nats_jetstream_raft_leader y kv_bucket_msgs para los buckets KV_. Adjunta labels node e instance a cada serie. varz es el core (error si no parsea); connz y jsz son best-effort (se omiten sin abortar). La consume el unibus_exporter de fleet_monitoring como scraper local por nodo."
|
||||
tags: [prometheus, metrics, nats, jetstream, monitoring, varz, connz, jsz, kv, raft, fleet-metrics, infra]
|
||||
uses_functions: []
|
||||
uses_types: ["PromSample_go_infra"]
|
||||
returns: []
|
||||
returns_optional: true
|
||||
error_type: "error_go_core"
|
||||
imports: ["encoding/json", "fmt", "strings", "time"]
|
||||
params:
|
||||
- name: node
|
||||
desc: "nombre lógico del nodo (p.ej. \"magnus\"); se adjunta como labels node e instance a CADA serie y se compara con cluster.leader de cada stream para nats_jetstream_raft_leader"
|
||||
- name: varz
|
||||
desc: "cuerpo JSON crudo de GET http://127.0.0.1:8222/varz; core de la función (in_msgs, out_msgs, in_bytes, out_bytes, connections, slow_consumers, subscriptions, mem, start). Si no parsea, la función devuelve error"
|
||||
- name: connz
|
||||
desc: "cuerpo JSON crudo de GET http://127.0.0.1:8222/connz; best-effort (num_connections). Si vacío o inválido, nats_connections cae a varz.connections sin abortar"
|
||||
- name: jsz
|
||||
desc: "cuerpo JSON crudo de GET http://127.0.0.1:8222/jsz?streams=1; best-effort (streams, messages, bytes, memory, storage y account_details[].stream_detail[]). Si vacío o inválido, se omiten sus series sin abortar. Necesita ?streams=1 para traer stream_detail"
|
||||
output: "slice de PromSample con labels base {node,instance}: nats_msgs_in/out_total, nats_bytes_in/out_total, nats_connections, nats_slow_consumers, nats_mem_bytes, nats_subscriptions, nats_server_start_seconds (omitida si start no parsea), nats_jetstream_streams/messages/bytes/memory_bytes/storage_bytes; y por stream nats_stream_messages{stream}, nats_stream_bytes{stream}, nats_jetstream_raft_leader{stream} (1 si cluster.leader==node) y, para streams KV_, kv_bucket_msgs{bucket} con el prefijo KV_ recortado. Error solo si varz no es JSON válido."
|
||||
tested: true
|
||||
test_file_path: "functions/infra/parse_nats_monitor_test.go"
|
||||
tests:
|
||||
- "TestParseNatsMonitorGolden"
|
||||
- "TestParseNatsMonitorEmptyJsz"
|
||||
- "TestParseNatsMonitorInvalidConnz"
|
||||
- "TestParseNatsMonitorInvalidVarz"
|
||||
---
|
||||
|
||||
# parse_nats_monitor
|
||||
|
||||
Función de transformación (clasificada `impure` porque devuelve `error` al fallar el
|
||||
unmarshal del core; no hace I/O ni red por sí misma) que traduce las métricas
|
||||
server-level de un **nats-server** a series Prometheus. Es la hermana de
|
||||
`parse_unibus_health_go_infra`: aquella lee el `/healthz` de `membershipd` (posture),
|
||||
esta lee el monitoring embebido de NATS (puerto 8222) para las métricas profundas que
|
||||
`/healthz` no expone: msgs/s, conexiones, RAFT leader por stream, memoria, KV buckets.
|
||||
|
||||
Pertenece al grupo de capacidad `fleet-metrics`: se compone con
|
||||
`format_prom_exposition_go_infra` (serializar) y `push_prom_remote_go_infra` (empujar a
|
||||
VictoriaMetrics). La consume el `unibus_exporter` de `fleet_monitoring` en modo scraper
|
||||
local por nodo, que hace los tres GET y le pasa los cuerpos crudos.
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"fn-registry/functions/infra"
|
||||
)
|
||||
|
||||
func get(url string) []byte {
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
return nil // best-effort: connz/jsz pueden faltar
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
b, _ := io.ReadAll(resp.Body)
|
||||
return b
|
||||
}
|
||||
|
||||
func main() {
|
||||
base := "http://127.0.0.1:8222"
|
||||
varz := get(base + "/varz")
|
||||
connz := get(base + "/connz")
|
||||
jsz := get(base + "/jsz?streams=1")
|
||||
|
||||
samples, err := infra.ParseNatsMonitor("magnus", varz, connz, jsz)
|
||||
if err != nil {
|
||||
panic(err) // varz es el core: sin él no hay métricas
|
||||
}
|
||||
fmt.Print(infra.FormatPromExposition(samples, time.Now().UnixMilli()))
|
||||
// nats_msgs_in_total{instance="magnus",node="magnus"} 17 ...
|
||||
// kv_bucket_msgs{bucket="UNIBUS_users",instance="magnus",node="magnus"} 2 ...
|
||||
// nats_jetstream_raft_leader{instance="magnus",node="magnus",stream="KV_UNIBUS_users"} 1 ...
|
||||
}
|
||||
```
|
||||
|
||||
## Cuando usarla
|
||||
|
||||
Úsala dentro de un exporter que monitoriza un nats-server con el monitoring HTTP
|
||||
embebido activado (`http: 127.0.0.1:8222` en la config de NATS): tras hacer
|
||||
`GET /varz`, `GET /connz` y `GET /jsz?streams=1` contra loopback, pasa los tres cuerpos
|
||||
crudos a esta función para obtener todas las series server-level del nodo. Llámala como
|
||||
scraper local por nodo (cada nodo expone su 8222 solo en loopback), no centralizado.
|
||||
|
||||
## Gotchas
|
||||
|
||||
- **Impura por contrato**: solo devuelve `error` si `varz` no es JSON válido (es el core).
|
||||
`connz` y `jsz` son **best-effort**: si vienen vacíos o no parsean, sus series se omiten
|
||||
sin abortar. Esto hace al scraper resistente a que un endpoint falle de forma puntual.
|
||||
- **Monitoring loopback-only sin auth**: el puerto 8222 de NATS no tiene autenticación; por
|
||||
eso debe bindearse a `127.0.0.1` y scrapearse localmente en cada nodo, nunca exponerse a
|
||||
la red. El push agregado a VictoriaMetrics lo hace el exporter, no esta función.
|
||||
- **`/jsz` necesita `?streams=1`** para traer `account_details[].stream_detail[]`. Sin ese
|
||||
parámetro el cuerpo trae los totales pero no el detalle por stream, y entonces no salen
|
||||
`nats_stream_*`, `nats_jetstream_raft_leader` ni `kv_bucket_msgs`.
|
||||
- **`nats_connections`**: prefiere `connz.num_connections`; si `connz` no parsea, cae a
|
||||
`varz.connections` para no perder la serie.
|
||||
- **RAFT leader en standalone**: en un nats-server sin clúster, el objeto `cluster` puede
|
||||
faltar o `leader` venir vacío; en ese caso `nats_jetstream_raft_leader` sale 0 salvo que
|
||||
`cluster.leader == node`. Es esperado: en standalone no hay quorum RAFT real.
|
||||
- **`kv_bucket_msgs`** solo se emite para streams cuyo nombre empieza por `KV_`, recortando
|
||||
el prefijo (stream `KV_UNIBUS_users` → bucket `UNIBUS_users`).
|
||||
- **`nats_server_start_seconds`** es el epoch Unix del campo `start` (RFC3339): sirve como
|
||||
proxy de reinicios (un cambio de valor = el server reinició). Si el campo no parsea como
|
||||
fecha válida, la serie se omite en lugar de abortar.
|
||||
Reference in New Issue
Block a user