Files
CleanMM/cmd/status/metrics_health.go

169 lines
4.0 KiB
Go
Raw Normal View History

2025-12-01 19:26:03 +08:00
package main
import (
"fmt"
"strings"
)
// Health score weights and thresholds.
2025-12-01 19:26:03 +08:00
const (
// Weights.
2025-12-01 19:26:03 +08:00
healthCPUWeight = 30.0
healthMemWeight = 25.0
healthDiskWeight = 20.0
healthThermalWeight = 15.0
healthIOWeight = 10.0
// CPU.
2025-12-01 19:26:03 +08:00
cpuNormalThreshold = 30.0
cpuHighThreshold = 70.0
// Memory.
2025-12-01 19:26:03 +08:00
memNormalThreshold = 50.0
memHighThreshold = 80.0
memPressureWarnPenalty = 5.0
memPressureCritPenalty = 15.0
// Disk.
2025-12-01 19:26:03 +08:00
diskWarnThreshold = 70.0
diskCritThreshold = 90.0
// Thermal.
2025-12-01 19:26:03 +08:00
thermalNormalThreshold = 60.0
thermalHighThreshold = 85.0
// Disk IO (MB/s).
2025-12-01 19:26:03 +08:00
ioNormalThreshold = 50.0
ioHighThreshold = 150.0
)
func calculateHealthScore(cpu CPUStatus, mem MemoryStatus, disks []DiskStatus, diskIO DiskIOStatus, thermal ThermalStatus) (int, string) {
score := 100.0
issues := []string{}
// CPU penalty.
2025-12-01 19:26:03 +08:00
cpuPenalty := 0.0
if cpu.Usage > cpuNormalThreshold {
if cpu.Usage > cpuHighThreshold {
cpuPenalty = healthCPUWeight * (cpu.Usage - cpuNormalThreshold) / cpuHighThreshold
} else {
cpuPenalty = (healthCPUWeight / 2) * (cpu.Usage - cpuNormalThreshold) / (cpuHighThreshold - cpuNormalThreshold)
}
}
score -= cpuPenalty
if cpu.Usage > cpuHighThreshold {
issues = append(issues, "High CPU")
}
// Memory penalty.
2025-12-01 19:26:03 +08:00
memPenalty := 0.0
if mem.UsedPercent > memNormalThreshold {
if mem.UsedPercent > memHighThreshold {
memPenalty = healthMemWeight * (mem.UsedPercent - memNormalThreshold) / memNormalThreshold
} else {
memPenalty = (healthMemWeight / 2) * (mem.UsedPercent - memNormalThreshold) / (memHighThreshold - memNormalThreshold)
}
}
score -= memPenalty
if mem.UsedPercent > memHighThreshold {
issues = append(issues, "High Memory")
}
2026-01-08 15:26:35 +08:00
// Memory pressure penalty.
switch mem.Pressure {
case "warn":
2025-12-01 19:26:03 +08:00
score -= memPressureWarnPenalty
issues = append(issues, "Memory Pressure")
2026-01-08 15:26:35 +08:00
case "critical":
2025-12-01 19:26:03 +08:00
score -= memPressureCritPenalty
issues = append(issues, "Critical Memory")
}
// Disk penalty.
2025-12-01 19:26:03 +08:00
diskPenalty := 0.0
if len(disks) > 0 {
diskUsage := disks[0].UsedPercent
if diskUsage > diskWarnThreshold {
if diskUsage > diskCritThreshold {
diskPenalty = healthDiskWeight * (diskUsage - diskWarnThreshold) / (100 - diskWarnThreshold)
} else {
diskPenalty = (healthDiskWeight / 2) * (diskUsage - diskWarnThreshold) / (diskCritThreshold - diskWarnThreshold)
}
}
score -= diskPenalty
if diskUsage > diskCritThreshold {
issues = append(issues, "Disk Almost Full")
}
}
// Thermal penalty.
2025-12-01 19:26:03 +08:00
thermalPenalty := 0.0
if thermal.CPUTemp > 0 {
if thermal.CPUTemp > thermalNormalThreshold {
if thermal.CPUTemp > thermalHighThreshold {
thermalPenalty = healthThermalWeight
issues = append(issues, "Overheating")
} else {
thermalPenalty = healthThermalWeight * (thermal.CPUTemp - thermalNormalThreshold) / (thermalHighThreshold - thermalNormalThreshold)
}
}
score -= thermalPenalty
}
// Disk IO penalty.
2025-12-01 19:26:03 +08:00
ioPenalty := 0.0
totalIO := diskIO.ReadRate + diskIO.WriteRate
if totalIO > ioNormalThreshold {
if totalIO > ioHighThreshold {
ioPenalty = healthIOWeight
issues = append(issues, "Heavy Disk IO")
} else {
ioPenalty = healthIOWeight * (totalIO - ioNormalThreshold) / (ioHighThreshold - ioNormalThreshold)
}
}
score -= ioPenalty
// Clamp score.
2025-12-01 19:26:03 +08:00
if score < 0 {
score = 0
}
if score > 100 {
score = 100
}
// Build message.
2026-01-08 15:26:35 +08:00
var msg string
switch {
case score >= 90:
2025-12-01 19:26:03 +08:00
msg = "Excellent"
case score >= 75:
2025-12-01 19:26:03 +08:00
msg = "Good"
case score >= 60:
2025-12-01 19:26:03 +08:00
msg = "Fair"
case score >= 40:
2025-12-01 19:26:03 +08:00
msg = "Poor"
default:
2025-12-01 19:26:03 +08:00
msg = "Critical"
}
if len(issues) > 0 {
msg = msg + ": " + strings.Join(issues, ", ")
}
return int(score), msg
}
func formatUptime(secs uint64) string {
days := secs / 86400
hours := (secs % 86400) / 3600
mins := (secs % 3600) / 60
if days > 0 {
// Only show days and hours when uptime is over 1 day (skip minutes for brevity)
return fmt.Sprintf("%dd %dh", days, hours)
2025-12-01 19:26:03 +08:00
}
if hours > 0 {
return fmt.Sprintf("%dh %dm", hours, mins)
}
return fmt.Sprintf("%dm", mins)
}