package collectors import ( "encoding/json" "fmt" "os" "strconv" "strings" "time" lp "github.com/ClusterCockpit/cc-lib/ccMessage" cclog "github.com/ClusterCockpit/cc-metric-collector/pkg/ccLogger" ) // LoadavgCollector collects: // * load average of last 1, 5 & 15 minutes // * number of processes currently runnable // * total number of processes in system // // See: https://www.kernel.org/doc/html/latest/filesystems/proc.html const LOADAVGFILE = "/proc/loadavg" type LoadavgCollector struct { metricCollector tags map[string]string config struct { ExcludeMetrics []string `json:"exclude_metrics,omitempty"` OnlyMetrics []string `json:"only_metrics,omitempty"` } } func (m *LoadavgCollector) shouldOutput(metricName string) bool { if len(m.config.OnlyMetrics) > 0 { for _, n := range m.config.OnlyMetrics { if n == metricName { return true } } return false } for _, n := range m.config.ExcludeMetrics { if n == metricName { return false } } return true } func (m *LoadavgCollector) Init(config json.RawMessage) error { m.name = "LoadavgCollector" m.parallel = true m.setup() if len(config) > 0 { if err := json.Unmarshal(config, &m.config); err != nil { return err } } m.meta = map[string]string{"source": m.name, "group": "LOAD"} m.tags = map[string]string{"type": "node"} m.init = true return nil } func (m *LoadavgCollector) Read(interval time.Duration, output chan lp.CCMessage) { if !m.init { return } buffer, err := os.ReadFile(LOADAVGFILE) if err != nil { cclog.ComponentError(m.name, fmt.Sprintf("Read(): Failed to read file '%s': %v", LOADAVGFILE, err)) return } now := time.Now() ls := strings.Split(string(buffer), " ") // Load metrics loadMetrics := []string{"load_one", "load_five", "load_fifteen"} for i, name := range loadMetrics { x, err := strconv.ParseFloat(ls[i], 64) if err != nil { cclog.ComponentError(m.name, fmt.Sprintf("Read(): Failed to convert '%s' to float64: %v", ls[i], err)) continue } if m.shouldOutput(name) { y, err := lp.NewMessage(name, m.tags, m.meta, map[string]interface{}{"value": x}, now) if err == nil { output <- y } } } // Process metrics lv := strings.Split(ls[3], `/`) procMetrics := []string{"proc_run", "proc_total"} for i, name := range procMetrics { x, err := strconv.ParseInt(lv[i], 10, 64) if err != nil { cclog.ComponentError(m.name, fmt.Sprintf("Read(): Failed to convert '%s' to int64: %v", lv[i], err)) continue } if m.shouldOutput(name) { y, err := lp.NewMessage(name, m.tags, m.meta, map[string]interface{}{"value": x}, now) if err == nil { output <- y } } } } func (m *LoadavgCollector) Close() { m.init = false }