Files
nasmon/internal/collect/cpu.go
T
a.gritsenkoandClaude Sonnet 5 09159a5f31 Initial import: nasmon health monitor, split out of home_nas
Go service (nasmon/) plus nfpm packaging (packaging/), carried over from
the home_nas monorepo. Adds rpm packaging alongside the existing deb
build (Fedora/RHEL/Rocky dependency names via nfpm overrides, per-format
systemd unit path, dpkg+rpm-compatible maintainer scripts) and a Gitea
Actions workflow that builds both formats on every push.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-19 10:24:34 +03:00

76 lines
1.6 KiB
Go

// Package collect reads system state from /proc, /sys and CLI tools.
package collect
import (
"bufio"
"bytes"
"fmt"
"os"
"runtime"
"strconv"
"strings"
)
type CPU struct {
UsagePct float64 `json:"usage_pct"`
Cores int `json:"cores"`
Load [3]float64 `json:"load"`
}
type cpuTimes struct{ idle, total uint64 }
// CPUSampler keeps the previous /proc/stat reading to compute usage deltas.
type CPUSampler struct {
prev cpuTimes
init bool
}
func (s *CPUSampler) Sample() (CPU, error) {
b, err := os.ReadFile("/proc/stat")
if err != nil {
return CPU{}, err
}
cur, err := parseProcStat(b)
if err != nil {
return CPU{}, err
}
c := CPU{Cores: runtime.NumCPU()}
if s.init && cur.total > s.prev.total {
dt := float64(cur.total - s.prev.total)
di := float64(cur.idle - s.prev.idle)
c.UsagePct = 100 * (1 - di/dt)
}
s.prev, s.init = cur, true
if lb, err := os.ReadFile("/proc/loadavg"); err == nil {
f := strings.Fields(string(lb))
for i := 0; i < 3 && i < len(f); i++ {
c.Load[i], _ = strconv.ParseFloat(f[i], 64)
}
}
return c, nil
}
// parseProcStat returns idle and total jiffies from the aggregate "cpu" line.
func parseProcStat(b []byte) (cpuTimes, error) {
sc := bufio.NewScanner(bytes.NewReader(b))
for sc.Scan() {
f := strings.Fields(sc.Text())
if len(f) < 5 || f[0] != "cpu" {
continue
}
var t cpuTimes
for i, v := range f[1:] {
n, err := strconv.ParseUint(v, 10, 64)
if err != nil {
return t, err
}
t.total += n
if i == 3 || i == 4 { // idle + iowait
t.idle += n
}
}
return t, nil
}
return cpuTimes{}, fmt.Errorf("no cpu line in /proc/stat")
}