diff --git a/cpu.go b/cpu.go index 705ec04..53919cd 100644 --- a/cpu.go +++ b/cpu.go @@ -1,6 +1,7 @@ package systats import ( + "errors" "regexp" "strconv" "strings" @@ -12,13 +13,16 @@ import ( // CPU holds information on CPU and CPU usage type CPU struct { - LoadAvg int `json:"loadAvg"` - CoreAvg []int `json:"coreAvg"` - Model string `json:"model"` - NoOfCores int `json:"noOfCores"` - Freq string `json:"freq"` - Cache string `json:"cache"` - Time int64 `json:"time"` + LoadAvg int `json:"loadAvg"` + CoreAvg []int `json:"coreAvg"` + Load1 float64 `json:"load1"` + Load5 float64 `json:"load5"` + Load15 float64 `json:"load15"` + Model string `json:"model"` + NoOfCores int `json:"noOfCores"` + Freq string `json:"freq"` + Cache string `json:"cache"` + Time int64 `json:"time"` } func getCPU(systats *SyStats, milliseconds int) (CPU, error) { @@ -43,9 +47,31 @@ func getCPU(systats *SyStats, milliseconds int) (CPU, error) { processCPUInfoFileContent(&output, &cpuinfoStr) + loadAvgStr, err := fileops.ReadFileWithError(systats.LoadAvgPath) + if err != nil { + return output, err + } + if err := processLoadAvgFileContent(&output, &loadAvgStr); err != nil { + return output, err + } + return output, nil } +// processLoadAvgFileContent parses /proc/loadavg's 1/5/15-minute load +// averages - the traditional Unix "load average" (uptime/top/w), distinct +// from LoadAvg/CoreAvg above which are CPU utilization percentages. +func processLoadAvgFileContent(output *CPU, content *string) error { + fields := strings.Fields(*content) + if len(fields) < 3 { + return errors.New("unexpected loadavg format") + } + output.Load1 = strops.ToFloat64(fields[0]) + output.Load5 = strops.ToFloat64(fields[1]) + output.Load15 = strops.ToFloat64(fields[2]) + return nil +} + func processCPUInfoFileContent(output *CPU, content *string) { split := strings.Split(*content, "\n") diff --git a/systats.go b/systats.go index ee0f856..3a3090d 100644 --- a/systats.go +++ b/systats.go @@ -30,6 +30,7 @@ type SyStats struct { EtcPath string UptimePath string MountsPath string + LoadAvgPath string // ProcessCPUMode selects how GetTopProcesses computes CPU usage: // CPUUsageInstant (default, used when left empty) or CPUUsageAverage. ProcessCPUMode string @@ -44,6 +45,7 @@ func New() SyStats { EtcPath: "/etc/", UptimePath: "/proc/uptime", MountsPath: "/proc/mounts", + LoadAvgPath: "/proc/loadavg", ProcessCPUMode: CPUUsageInstant, } } diff --git a/systats_test.go b/systats_test.go index 2d539e8..bbde408 100644 --- a/systats_test.go +++ b/systats_test.go @@ -145,6 +145,7 @@ func TestGetCPU(t *testing.T) { syStats := systats.SyStats{ CPUinfoFilePath: "./test_files/cpuinfo.txt", StatFilePath: "/proc/stat", + LoadAvgPath: "./test_files/loadavg.txt", } cpu, err := syStats.GetCPU() if err != nil { @@ -160,6 +161,21 @@ func TestGetCPU(t *testing.T) { t.Errorf("Got invalid value. got: %d, want: %d", cpu.NoOfCores, 4) return } + + if cpu.Load1 != 0.52 { + t.Errorf("Got invalid value. got: %f, want: %f", cpu.Load1, 0.52) + return + } + + if cpu.Load5 != 0.58 { + t.Errorf("Got invalid value. got: %f, want: %f", cpu.Load5, 0.58) + return + } + + if cpu.Load15 != 0.59 { + t.Errorf("Got invalid value. got: %f, want: %f", cpu.Load15, 0.59) + return + } } func TestGetSystem(t *testing.T) { diff --git a/test_files/loadavg.txt b/test_files/loadavg.txt new file mode 100644 index 0000000..e844c6a --- /dev/null +++ b/test_files/loadavg.txt @@ -0,0 +1 @@ +0.52 0.58 0.59 2/391 12345