feat: Prometheus metrics endpoint for knox nodes
Refs #2 - /metrics served on a dedicated port (KNOX_METRICS_ADDR, default localhost:8932) via prometheus/client_golang, with Go runtime + process collectors - DB-derived gauges refreshed per scrape: observations by source, last-24h observations, entries, projects, sessions, pending reflections, threads by status, peers, observations by origin node, knowledge vector (max hcl per node) - live gossip counters (pulls/pushes, observations pulled/pushed, errors) incremented during the anti-entropy sweep; Run accepts an optional metrics handle (nil for one-shot CLI) - knox_node_info{node_id,name} for scrape identification - internal/metrics package + db MetricsSnapshot; tests for snapshot, scrape output, and counter increments
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
package db
|
||||
|
||||
// MetricsSnapshot holds the scrape-time gauges derived from the database.
|
||||
type MetricsSnapshot struct {
|
||||
Observations int
|
||||
ObservationsLast24h int
|
||||
BySource map[string]int
|
||||
Entries int
|
||||
Projects int
|
||||
Sessions int
|
||||
PendingReflections int
|
||||
ThreadsByStatus map[string]int
|
||||
Peers int
|
||||
ByOriginNode map[string]int // node_id → observation count
|
||||
KnowledgeVector map[string]int64 // node_id → max hcl
|
||||
EarliestObservation string
|
||||
}
|
||||
|
||||
// MetricsSnapshot computes database-derived gauges for Prometheus scraping.
|
||||
// All queries are cheap aggregations; nothing is stored or mutated.
|
||||
func (k *KnoxDB) MetricsSnapshot() (*MetricsSnapshot, error) {
|
||||
s := &MetricsSnapshot{
|
||||
BySource: make(map[string]int),
|
||||
ThreadsByStatus: make(map[string]int),
|
||||
ByOriginNode: make(map[string]int),
|
||||
}
|
||||
|
||||
if err := k.db.QueryRow("SELECT COUNT(*) FROM observations").Scan(&s.Observations); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := k.db.QueryRow("SELECT COUNT(*) FROM observations WHERE collected_at > datetime('now', '-1 day')").Scan(&s.ObservationsLast24h); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := k.db.QueryRow("SELECT COUNT(*) FROM entries").Scan(&s.Entries); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := k.db.QueryRow("SELECT COUNT(DISTINCT project) FROM entries").Scan(&s.Projects); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := k.db.QueryRow("SELECT COUNT(*) FROM sessions").Scan(&s.Sessions); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := k.db.QueryRow("SELECT COUNT(*) FROM sessions WHERE indexed=0").Scan(&s.PendingReflections); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := k.db.QueryRow("SELECT COUNT(*) FROM peers").Scan(&s.Peers); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := k.db.QueryRow("SELECT COALESCE(MIN(collected_at),'') FROM observations").Scan(&s.EarliestObservation); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rows, err := k.db.Query("SELECT COALESCE(source_id,'unknown'), COUNT(*) FROM observations GROUP BY source_id")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
var src string
|
||||
var n int
|
||||
if err := rows.Scan(&src, &n); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.BySource[src] = n
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rows2, err := k.db.Query("SELECT COALESCE(status,'active'), COUNT(*) FROM threads GROUP BY status")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows2.Close()
|
||||
for rows2.Next() {
|
||||
var st string
|
||||
var n int
|
||||
if err := rows2.Scan(&st, &n); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.ThreadsByStatus[st] = n
|
||||
}
|
||||
if err := rows2.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rows3, err := k.db.Query("SELECT node_id, COUNT(*) FROM observations WHERE node_id<>'' GROUP BY node_id")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows3.Close()
|
||||
for rows3.Next() {
|
||||
var nid string
|
||||
var n int
|
||||
if err := rows3.Scan(&nid, &n); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.ByOriginNode[nid] = n
|
||||
}
|
||||
if err := rows3.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if s.KnowledgeVector, err = k.KnowledgeVector(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
Reference in New Issue
Block a user