package collectors import ( "encoding/json" "fmt" "os" "strconv" "strings" "time" cclog "github.com/ClusterCockpit/cc-metric-collector/internal/ccLogger" lp "github.com/ClusterCockpit/cc-metric-collector/internal/ccMetric" ) // // 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 load_matches []string load_skips []bool proc_matches []string proc_skips []bool config struct { ExcludeMetrics []string `json:"exclude_metrics,omitempty"` } } func (m *LoadavgCollector) Init(config json.RawMessage) error { m.name = "LoadavgCollector" m.parallel = true m.setup() if len(config) > 0 { err := json.Unmarshal(config, &m.config) if err != nil { return err } } m.meta = map[string]string{ "source": m.name, "group": "LOAD"} m.tags = map[string]string{"type": "node"} m.load_matches = []string{ "load_one", "load_five", "load_fifteen"} m.load_skips = make([]bool, len(m.load_matches)) m.proc_matches = []string{ "proc_run", "proc_total"} m.proc_skips = make([]bool, len(m.proc_matches)) for i, name := range m.load_matches { _, m.load_skips[i] = stringArrayContains(m.config.ExcludeMetrics, name) } for i, name := range m.proc_matches { _, m.proc_skips[i] = stringArrayContains(m.config.ExcludeMetrics, name) } m.init = true return nil } func (m *LoadavgCollector) Read(interval time.Duration, output chan lp.CCMetric) { if !m.init { return } buffer, err := os.ReadFile(LOADAVGFILE) if err != nil { if err != nil { cclog.ComponentError( m.name, fmt.Sprintf("Read(): Failed to read file '%s': %v", LOADAVGFILE, err)) } return } now := time.Now() // Load metrics ls := strings.Split(string(buffer), ` `) for i, name := range m.load_matches { 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.load_skips[i] { continue } y, err := lp.New(name, m.tags, m.meta, map[string]interface{}{"value": x}, now) if err == nil { output <- y } } // Process metrics lv := strings.Split(ls[3], `/`) for i, name := range m.proc_matches { x, err := strconv.ParseInt(lv[i], 10, 64) if err != nil { cclog.ComponentError( m.name, fmt.Sprintf("Read(): Failed to convert '%s' to float64: %v", lv[i], err)) continue } if m.proc_skips[i] { continue } y, err := lp.New(name, m.tags, m.meta, map[string]interface{}{"value": x}, now) if err == nil { output <- y } } } func (m *LoadavgCollector) Close() { m.init = false }