package metrics import ( "sort" dto "github.com/prometheus/client_model/go" ) // PathCount 单个路由模板的累计请求数 type PathCount struct { Path string `json:"path"` Count float64 `json:"count"` } // Summary 面板自监控快照:从 registry 聚合出的精简结构(/api/v1/system/metrics-summary) type Summary struct { HTTP struct { Total float64 `json:"total"` ByStatus map[string]float64 `json:"byStatus"` InFlight float64 `json:"inFlight"` TopPaths []PathCount `json:"topPaths"` } `json:"http"` DB struct { SizeBytes float64 `json:"sizeBytes"` Saves float64 `json:"saves"` SaveFailures float64 `json:"saveFailures"` Records map[string]float64 `json:"records"` } `json:"db"` Alerts struct { Evaluations map[string]float64 `json:"evaluations"` } `json:"alerts"` Loki struct { Queries map[string]float64 `json:"queries"` } `json:"loki"` Runtime struct { Goroutines float64 `json:"goroutines"` GoVersion string `json:"goVersion"` Version string `json:"version"` } `json:"runtime"` } // Snapshot 聚合当前指标为面板结构(聚合失败返回零值,监控页降级展示) func Snapshot() Summary { s := Summary{} s.HTTP.ByStatus = map[string]float64{} s.DB.Records = map[string]float64{} s.Alerts.Evaluations = map[string]float64{} s.Loki.Queries = map[string]float64{} families, err := registry.Gather() if err != nil { return s } byPath := map[string]float64{} for _, mf := range families { for _, m := range mf.GetMetric() { labels := labelMap(m) switch mf.GetName() { case "lokistack_http_requests_total": v := m.GetCounter().GetValue() s.HTTP.Total += v s.HTTP.ByStatus[labels["status"]] += v byPath[labels["path"]] += v case "lokistack_http_in_flight_requests": s.HTTP.InFlight = m.GetGauge().GetValue() case "lokistack_db_size_bytes": s.DB.SizeBytes = m.GetGauge().GetValue() case "lokistack_db_save_total": v := m.GetCounter().GetValue() s.DB.Saves += v if labels["result"] != "success" { s.DB.SaveFailures += v } case "lokistack_db_records": s.DB.Records[labels["collection"]] = m.GetGauge().GetValue() case "lokistack_alert_evaluations_total": s.Alerts.Evaluations[labels["result"]] += m.GetCounter().GetValue() case "lokistack_loki_queries_total": s.Loki.Queries[labels["result"]] += m.GetCounter().GetValue() case "go_goroutines": s.Runtime.Goroutines = m.GetGauge().GetValue() case "lokistack_build_info": s.Runtime.GoVersion = labels["go_version"] s.Runtime.Version = labels["version"] } } } paths := make([]PathCount, 0, len(byPath)) for p, c := range byPath { paths = append(paths, PathCount{Path: p, Count: c}) } sort.Slice(paths, func(i, j int) bool { return paths[i].Count > paths[j].Count }) if len(paths) > 8 { paths = paths[:8] } s.HTTP.TopPaths = paths return s } func labelMap(m *dto.Metric) map[string]string { out := map[string]string{} for _, lp := range m.GetLabel() { out[lp.GetName()] = lp.GetValue() } return out }