git.sophuwu.com > statlog
made cmd cooler or something idk
sophuwu sophie@sophuwu.com
Tue, 09 Sep 2025 17:47:49 +0200
commit

a9e189866cf29d772e2c694b759f1160dd3a0c4b

parent

b85e7e2f05a120255a85326813c345e1096c13b5

4 files changed, 110 insertions(+), 61 deletions(-)

jump to
M cmd/main.gocmd/main.go

@@ -1,16 +1,19 @@

package main import ( + "errors" "fmt" - "git.sophuwu.com/statlog" - "git.sophuwu.com/statlog/types" "os" "os/signal" + + "git.sophuwu.com/statlog" + "git.sophuwu.com/statlog/types" ) func fatal(e error) { if e != nil { fmt.Println("\033[?1049l\033[?25h") + fmt.Println("Error:", e) os.Exit(1) } }

@@ -19,6 +22,7 @@ func main() {

fmt.Println("\033[?25l\033[?1049h\033[2J") defer fmt.Println("\033[?1049l\033[?25h") hw := &statlog.HWInfo{} + hw.Update() ch := make(chan os.Signal, 1) bl := true go func() {

@@ -29,30 +33,67 @@ }()

var s string var ss string var e error - var fn func(w, h, val int) (string, error) - fn, e = types.Graph("CPU Load", 100) - w, _ := types.TermSize() + var grCpu func(w, h, val int) (string, error) + var grMem func(w, h, val int) (string, error) + grCpu, e = types.Graph("CPU Load", 100) fatal(e) + grMem, e = types.Graph("Memory (%)", 100) + fatal(e) + var w int + prnt := func() { + fmt.Printf("\033[2J\033[1;1H\r%s\n", s) + } for bl { + w, _ = types.TermSize() s = "" hw.Update() w, _ = types.TermSize() - ss, e = fn(w/2, 4, int(hw.CPU.LoadAvg)) + + // mem + ss, e = hw.MEM.Bar() + if errors.Is(e, types.ErrTooNarrow) { + s = "Terminal too narrow" + prnt() + continue + } + fatal(e) + s += "MEM: " + hw.MEM.String() + "\n" + ss + "\n" + ss, e = grMem(w, 5, int(hw.MEM.Percent.Used+hw.MEM.Percent.Buff)) + fatal(e) + s += ss + "\n" + + // cpu + + // print cpu info string + s += "CPU: " + hw.CPU.String() + "\n" + // make load graph + ss, e = grCpu(w, 5, int(hw.CPU.LoadAvg)) fatal(e) - s += ss + "\n\n" + // print load graph + s += ss + "\n" - // s += "MEM: " + hw.MEM.String() - // s += "\nCPU: " + hw.CPU.MHzAvg.String() + " MHz " + hw.CPU.Temp.String() + "\n\n" - // ss, e = hw.CPU.LoadAvg.Bar("CPU Avg", 0) - // fatal(e) - // s += ss + "\n\n" - // ss, e = hw.MEM.Bar() - // fatal(e) - // s += ss + "\n\nCPU CORES:\n\n" - // ss, e = hw.CPU.LoadBar() - // fatal(e) - // s += ss + "\n" + // cpu bar graphs + // make bar graph + ss, e = hw.CPU.LoadAvg.Bar("CPU Avg", w) + if errors.Is(e, types.ErrTooNarrow) { + s = "Terminal too narrow" + prnt() + continue + } + fatal(e) + // print bar graph + s += ss + "\n" + // make core bar graphs + ss, e = hw.CPU.LoadBar(w) + if errors.Is(e, types.ErrTooNarrow) { + s = "Terminal too narrow" + prnt() + continue + } + fatal(e) + // print core bar graphs + s += ss + "\n" - fmt.Printf("\033[2J\033[1;1H\r%s\n", s) + prnt() } }
M device/cpu.godevice/cpu.go

@@ -4,10 +4,11 @@ import (

"bytes" "encoding/json" "fmt" - "git.sophuwu.com/statlog/types" "os" "strings" "time" + + "git.sophuwu.com/statlog/types" ) type CPU struct {

@@ -18,9 +19,11 @@ MHzCore []types.MHz `json:"MHzCore"`

Temp types.Celsius `json:"Temp"` } -func (c *CPU) LoadBar() (s string, err error) { - w, _ := types.TermSize() - l := len(c.LoadCore) +func (cpu *CPU) LoadBar(w int) (s string, err error) { + if w == 0 { + w, _ = types.TermSize() + } + l := len(cpu.LoadCore) ss := "" div := 3 if w < 100 {

@@ -30,7 +33,7 @@ div = 4

} w = (w - 2) / div for i := 0; i < l; i++ { - if ss, err = c.LoadCore[i].Bar(fmt.Sprintf("%2d", i), w); err != nil { + if ss, err = cpu.LoadCore[i].Bar(fmt.Sprintf("%2d", i), w); err != nil { return "", err } s += ss

@@ -97,29 +100,29 @@ return

} cpu.Temp = (types.Celsius(b[0])-48)*10 + types.Celsius(b[1]) - 48 } -func (c *CPU) loadMHz() { - c.MHzAvg = 0 +func (cpu *CPU) loadMHz() { + cpu.MHzAvg = 0 b, err := os.ReadFile("/proc/cpuinfo") if err != nil { return } var ns types.NumSeeker - c.MHzCore = make([]types.MHz, 0) + cpu.MHzCore = make([]types.MHz, 0) tot, n := 0.0, 0.0 for _, v := range bytes.Split(b, []byte("\n")) { if bytes.HasPrefix(v, []byte("cpu MHz")) { ns.Init(v) n = ns.GetFloat() tot += n - c.MHzCore = append(c.MHzCore, types.MHz(n)) + cpu.MHzCore = append(cpu.MHzCore, types.MHz(n)) } } - if len(c.MHzCore) == 0 { - c.MHzAvg = 0 - c.MHzCore = nil + if len(cpu.MHzCore) == 0 { + cpu.MHzAvg = 0 + cpu.MHzCore = nil return } - c.MHzAvg = types.MHz(tot / float64(len(c.MHzCore))) + cpu.MHzAvg = types.MHz(tot / float64(len(cpu.MHzCore))) } type coreLoad struct {

@@ -142,7 +145,7 @@ }

return (c.v[1][0] - c.v[0][0]) / (c.v[1][1] - c.v[0][1]) } -func (c *CPU) loadUse() { +func (cpu *CPU) loadUse() { var cl []coreLoad loadVals := func(t int) error { i, j, k, n := 0, 0, 0, 0.0

@@ -213,26 +216,26 @@ err = loadVals(1)

if err != nil || len(cl) == 0 { goto onErr } - c.LoadAvg.FromFloat(cl[0].percent()) + cpu.LoadAvg.FromFloat(cl[0].percent()) cl = cl[1:] if len(cl) == 0 { goto onEmpty } - c.LoadCore = make([]types.Percent, len(cl)) + cpu.LoadCore = make([]types.Percent, len(cl)) for i, j := range cl { - c.LoadCore[i].FromFloat(j.percent()) + cpu.LoadCore[i].FromFloat(j.percent()) } return onErr: - c.LoadAvg = -1.0 + cpu.LoadAvg = -1.0 onEmpty: - c.LoadCore = nil + cpu.LoadCore = nil } -func (c *CPU) Update() { - c.loadMHz() - c.loadTemp() - c.loadUse() +func (cpu *CPU) Update() { + cpu.loadMHz() + cpu.loadTemp() + cpu.loadUse() } func numSize(n int) int {

@@ -250,22 +253,22 @@ n /= 10

} return i } -func (c *CPU) LoadCoreStr() (s string) { - if c.LoadCore == nil || len(c.LoadCore) == 0 { +func (cpu *CPU) LoadCoreStr() (s string) { + if cpu.LoadCore == nil || len(cpu.LoadCore) == 0 { return s } var i int - fmtstr := "cpu %." + fmt.Sprintf("%d", numSize(len(c.LoadCore))) + "d: %s" + fmtstr := "cpu %." + fmt.Sprintf("%d", numSize(len(cpu.LoadCore))) + "d: %s" fn := func() { - s += fmt.Sprintf(fmtstr, i, c.LoadCore[i]) + s += fmt.Sprintf(fmtstr, i, cpu.LoadCore[i]) } - if c.MHzCore != nil && len(c.LoadCore) == len(c.MHzCore) { + if cpu.MHzCore != nil && len(cpu.LoadCore) == len(cpu.MHzCore) { fmtstr += " %s" fn = func() { - s += fmt.Sprintf(fmtstr, i, c.LoadCore[i], c.MHzCore[i]) + s += fmt.Sprintf(fmtstr, i, cpu.LoadCore[i], cpu.MHzCore[i]) } } - for i = 0; i < len(c.LoadCore); i++ { + for i = 0; i < len(cpu.LoadCore); i++ { fn() if i%3 == 2 { s += "\n"

@@ -281,10 +284,10 @@ }

return } -func (c CPU) String() string { - return fmt.Sprintf("%s %s %s", c.LoadAvg, c.MHzAvg, c.Temp) +func (cpu CPU) String() string { + return fmt.Sprintf("%s %s %s", cpu.LoadAvg, cpu.MHzAvg, cpu.Temp) } -func (c *CPU) JSON() (string, error) { - b, e := json.Marshal(c) +func (cpu *CPU) JSON() (string, error) { + b, e := json.Marshal(cpu) return string(b), e }
M types/percent.gotypes/percent.go

@@ -49,5 +49,12 @@ *P = Percent(v)

} func (P *Percent) Bar(title string, w int) (s string, err error) { - return Bar(title, w, int(P.Value()), 100, P.Compact()) + v := int(P.Value() + 0.5) + if v < 0 { + v = 0 + } + if v > 100 { + v = 100 + } + return Bar(title, w, v, 100, P.Compact()) }
M types/types.gotypes/types.go

@@ -2,6 +2,7 @@ package types

import ( "fmt" + "golang.org/x/term" )

@@ -33,6 +34,8 @@ }

return } +var ErrTooNarrow = fmt.Errorf("terminal too narrow") + func Bar(title string, w, val, max int, vlbl string) (string, error) { if max <= 0 || val < 0 || val > max { return "", fmt.Errorf("invalid values: val=%d, max=%d", val, max)

@@ -44,7 +47,7 @@ s := title + " ["

w = w - len(s) - 2 - len(vlbl) if w < 10 { - return "", fmt.Errorf("terminal too narrow") + return "", ErrTooNarrow } val = int(float64(val) * float64(w) / float64(max))

@@ -99,12 +102,7 @@ vv = append(vv, vals[j]*h*len(gch)/max)

} var v int for i = 0; i < h; i++ { - s += "\033[1A" + func() string { - if i%2 == 0 { - return "\033[1;37m" - } - return "\033[1;97m" - }() + s += "\033[1A" for j, v = range vv { v -= i * len(gch) if v < len(gch) {