git.sophuwu.com > statlog
added stuff from quickstat. modified for better fit
sophuwu sophie@sophuwu.com
Tue, 01 Jul 2025 14:19:35 +0200
commit

51959276825c551837a7bb6c851ec87e9cf7ab8e

A .gitignore

@@ -0,0 +1,3 @@

+.idea +build +
A device/cpu.go

@@ -0,0 +1,141 @@

+package device + +import ( + "bytes" + "encoding/json" + "fmt" + "git.sophuwu.com/statlog/types" + "os" + "time" +) + +type CPU struct { + Load float64 + MHz float64 + Temp int +} + +func (cpu *CPU) loadTemp() { + if cpu.Temp == -100 { + return + } + var findCPUTypeNo = func(path string, fileName string, comp string) (string, error) { + var b = make([]byte, 100) + var i int = 0 + if fileName == "_label" { + i = 1 + } + var err error = nil + for ; err == nil && string(b[:len(comp)]) != comp; i++ { + b, err = os.ReadFile(path + string(i+48) + fileName) + } + if err != nil { + return "", err + } + return string(i + 48 - 1), nil + } + Err := func(err error) bool { + if err == nil { + return false + } + cpu.Temp = -100 + return true + } + var ( + b = make([]byte, 2) + thrmPath = "/sys/class/thermal/thermal_zone" + hwMonPath = "/sys/class/hwmon/hwmon" + ) + nStr, err := findCPUTypeNo(thrmPath, "/type", "x86_pkg_temp") + if err != nil { + nStr, err = findCPUTypeNo(hwMonPath, "/name", "k10temp") + if Err(err) { + return + } + nStrHW := nStr + nStr, err = findCPUTypeNo(hwMonPath+nStrHW+"/temp", "_label", "Tdie") + if Err(err) { + return + } + b, err = os.ReadFile(hwMonPath + nStrHW + "/temp" + nStr + "_input") + } else { + b, err = os.ReadFile(thrmPath + nStr + "/temp") + } + if Err(err) { + return + } + cpu.Temp = (int(b[0])-48)*10 + int(b[1]) - 48 +} +func (c *CPU) loadMHz() { + c.MHz = 0 + b, err := os.ReadFile("/proc/cpuinfo") + if err != nil { + return + } + var ns types.NumSeeker + n := 0.0 + for _, v := range bytes.Split(b, []byte("\n")) { + if bytes.HasPrefix(v, []byte("cpu MHz")) { + ns.Init(v) + c.MHz += ns.GetFloat() + n++ + } + } + c.MHz /= n +} +func (c *CPU) loadUsage() { + readStat := func(n *[4]float64) bool { + + b := make([]byte, 100) + f, err := os.Open("/proc/stat") + if err != nil { + return true + } + _, err = f.Read(b) + if err != nil { + return true + } + f.Close() + for i, j := 6, 0; j < 4; i++ { + if b[i] >= 48 && b[i] <= 57 { + n[j] = n[j]*10 + float64(b[i]) - 48 + } else if b[i] == ' ' { + j++ + } + } + return false + } + var a, b [4]float64 + if readStat(&a) { + return + } + time.Sleep(types.SampleDuration) + if readStat(&b) { + return + } + c.Load = ((b[0] + b[1] + b[2]) - (a[0] + a[1] + a[2])) / ((b[0] + b[1] + b[2] + b[3]) - (a[0] + a[1] + a[2] + a[3])) +} +func (c *CPU) update() { + c.loadMHz() + c.loadTemp() + c.loadUsage() +} +func (c *CPU) GHzStr() string { + return fmt.Sprintf("%.2f GHz", c.MHz/1000) +} +func (c *CPU) LoadStr() string { + return fmt.Sprintf("%.1f %c", c.Load*100, '%') +} +func (c *CPU) TempStr() string { + if c.Temp == -100 { + return "" + } + return fmt.Sprintf("%d C", c.Temp) +} +func (c *CPU) String() string { + return fmt.Sprintf("%6.6s %8.8s %5.5s", c.LoadStr(), c.GHzStr(), c.TempStr()) +} +func (c *CPU) JSON() (string, error) { + b, e := json.Marshal(c) + return string(b), e +}
A device/mem.go

@@ -0,0 +1,46 @@

+package device + +import ( + "encoding/json" + "fmt" + "git.sophuwu.com/statlog/types" + "os" +) + +type MEM struct { + Total types.Bytes + Free types.Bytes + Buffer types.Bytes + Used types.Bytes + Percent struct { + Used types.Percent + WithBuff types.Percent + } +} + +func (m *MEM) update() { + b := make([]byte, 140) + f, _ := os.Open("/proc/meminfo") + _, err := f.Read(b) + if err != nil { + return + } + f.Close() + var seeker types.NumSeeker + seeker.Init(b) + m.Total = types.Bytes(seeker.GetNum()) + m.Free = types.Bytes(seeker.GetNum()) + m.Used = m.Total - types.Bytes(seeker.GetNum()) + m.Buffer = types.Bytes(seeker.GetNum() + seeker.GetNum()) + m.Percent.Used.CalcBytes(m.Used, m.Total) + m.Percent.WithBuff.CalcBytes(m.Used+m.Buffer, m.Total) +} + +func (m *MEM) String() string { + return fmt.Sprintf("%s USED %s BUFF %s FREE", types.Bytes(m.Used), m.Buffer, m.Free) +} + +func (m *MEM) JSON() (string, error) { + b, e := json.Marshal(m) + return string(b), e +}
A go.mod

@@ -0,0 +1,3 @@

+module git.sophuwu.com/statlog + +go 1.24.2
A tmp.go

@@ -0,0 +1,49 @@

+package statlog + +import ( + "encoding/json" + "fmt" + "git.sophuwu.com/statlog/device" + "strings" + "time" +) + +type HWInfo struct { + Time int64 `json:"UnixMilli"` + CPU device.CPU `json:"CPU"` + MEM device.MEM `json:"MEM"` +} + +func (this *HWInfo) Update() { + done := make(chan bool) + go func() { + this.CPU.update() + done <- true + }() + go func() { + this.MEM.update() + done <- true + }() + <-done + <-done + this.Time = time.Now().UnixMilli() +} +func (i HWInfo) String() string { + if CONFIG.Json { + b, _ := json.Marshal(i) + return string(b) + } + s := i.MEM.String() + if SI[CONFIG.Unit] == '%' { + s = i.MEM.Percent().String() + } + return fmt.Sprintf("MEM: %s | CPU: %s", s, i.CPU.String()) +} + +// func main() { +// var hw HWInfo +// for do := true; do; do = CONFIG.Repeat { +// hw.Update() +// fmt.Println(hw) +// } +// }
A types/const.go

@@ -0,0 +1,5 @@

+package types + +import "time" + +const SampleDuration = time.Second
A types/numSeeker.go

@@ -0,0 +1,83 @@

+package types + +import ( + "fmt" + "math" +) + +type NumSeeker struct { + i int + b []byte +} + +func (n *NumSeeker) Init(b []byte) { + n.b = b + n.i = 0 +} +func (n *NumSeeker) End() bool { + return n.i >= len(n.b) +} +func (n *NumSeeker) Seek() { + n.i++ +} +func (n *NumSeeker) GetByte() uint8 { + return uint8(n.b[n.i]) +} +func (n *NumSeeker) GetOffset(off int) (uint8, error) { + off += n.i + if off < 0 || off >= len(n.b) { + return 0, fmt.Errorf("index out of range: %d", n) + } + return uint8(n.b[off]), nil +} + +func (n *NumSeeker) IsNum() bool { + e := n.GetByte() + return e >= 48 && e <= 57 +} +func (n *NumSeeker) SeekToNum() { + for ; !n.End(); n.Seek() { + if n.IsNum() { + break + } + } +} +func (n *NumSeeker) GetNum() uint64 { + var num uint64 = 0 + for n.SeekToNum(); !n.End(); n.Seek() { + if n.IsNum() { + num = num*uint64(10) + uint64(uint8(n.GetByte())-48) + } else { + return num + } + } + return num +} +func (n *NumSeeker) GetFloat() float64 { + n.SeekToNum() + if n.End() { + return 0 + } + var sign float64 = 1 + if b, _ := n.GetOffset(-1); b == '-' { + sign = -1 + n.Seek() + } + ipart := float64(n.GetNum()) + if n.GetByte() == '.' { + fpart := float64(n.GetNum()) + for i, j := 0.0, math.Log10(fpart); i < j; i++ { + fpart *= 0.1 + } + ipart += fpart + } + return ipart * sign +} + +func (n *NumSeeker) GetNums() []uint64 { + nums := make([]uint64, 0) + for ; !n.End(); n.Seek() { + nums = append(nums, n.GetNum()) + } + return nums +}
A types/types.go

@@ -0,0 +1,72 @@

+package types + +import ( + "fmt" + "git.sophuwu.com/statlog/types/units" +) + +type Bytes uint64 + +func (b Bytes) Human() string { + n := float64(b) + var i int + for i = units.B; i < units.GiB; i *= units.KiB { + if n < units.KiB { + break + } + n /= units.KiB + } + return fmt.Sprintf("%.2f %s", n, units.Labels[i]) +} + +func (b Bytes) HumanSI() string { + n := float64(b) + var i int + for i = units.B; i < units.GB; i *= units.KB { + if n < units.KB { + break + } + n /= units.KB + } + return fmt.Sprintf("%.2f %s", n, units.Labels[i]) +} + +func (b Bytes) Value() uint64 { + return uint64(b) +} + +func (b Bytes) String() string { + return b.Human() +} + +type Percent float64 + +func (p *Percent) String() string { + return fmt.Sprintf("%.1f%%", *p) +} +func (p *Percent) Value() float64 { + return float64(*p) +} +func (P *Percent) Clamp() { + if *P < 0 { + *P = 0 + } else if *P > 100 { + *P = 100 + } +} +func (P *Percent) CalcBytes(Vn, Vmax Bytes) { + P.Calc(float64(Vn), float64(Vmax)) +} +func (P *Percent) Calc(Vn, Vmax float64) { + if Vmax == 0 { + *P = 0 + return + } + *P = Percent(100 * Vn / Vmax) +} +func (P *Percent) SetValue(v float64) { + *P = Percent(v) +} +func (P *Percent) SetValueInt(v int) { + *P = Percent(v) +}
A types/units/units.go

@@ -0,0 +1,22 @@

+package units + +const ( + B = 1 + KiB = 1024 * B + MiB = 1024 * KiB + GiB = 1024 * MiB + + KB = 1000 * B + MB = 1000 * KB + GB = 1000 * MB +) + +var Labels = map[int]string{ + B: "B", + KiB: "KiB", + MiB: "MiB", + GiB: "GiB", + KB: "KB", + MB: "MB", + GB: "GB", +}