Add homelab service and system health monitoring
This commit is contained in:
84
system_health.go
Normal file
84
system_health.go
Normal file
@@ -0,0 +1,84 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type SystemIssue struct {
|
||||
Time int64 `json:"time"`
|
||||
Unit string `json:"unit"`
|
||||
Priority string `json:"priority"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
type SystemHealthMetrics struct {
|
||||
Available bool `json:"available"`
|
||||
FailedUnits int `json:"failedUnits"`
|
||||
KernelErrors int `json:"kernelErrors"`
|
||||
SystemErrors int `json:"systemErrors"`
|
||||
Issues []SystemIssue `json:"issues"`
|
||||
CheckedAt int64 `json:"checkedAt"`
|
||||
}
|
||||
type systemHealthCollector struct {
|
||||
mu sync.RWMutex
|
||||
value SystemHealthMetrics
|
||||
}
|
||||
|
||||
func newSystemHealthCollector() *systemHealthCollector {
|
||||
c := &systemHealthCollector{}
|
||||
go c.run()
|
||||
return c
|
||||
}
|
||||
func (c *systemHealthCollector) collect() SystemHealthMetrics {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
return c.value
|
||||
}
|
||||
func (c *systemHealthCollector) run() {
|
||||
c.refresh()
|
||||
t := time.NewTicker(time.Minute)
|
||||
defer t.Stop()
|
||||
for range t.C {
|
||||
c.refresh()
|
||||
}
|
||||
}
|
||||
func (c *systemHealthCollector) refresh() {
|
||||
m := SystemHealthMetrics{CheckedAt: time.Now().Unix()}
|
||||
if _, err := exec.LookPath("journalctl"); err != nil {
|
||||
c.mu.Lock()
|
||||
c.value = m
|
||||
c.mu.Unlock()
|
||||
return
|
||||
}
|
||||
m.Available = true
|
||||
if out, err := exec.Command("systemctl", "--failed", "--no-legend", "--plain").Output(); err == nil {
|
||||
for _, line := range strings.Split(strings.TrimSpace(string(out)), "\n") {
|
||||
if strings.TrimSpace(line) != "" {
|
||||
m.FailedUnits++
|
||||
}
|
||||
}
|
||||
}
|
||||
out, _ := exec.Command("journalctl", "--since", "24 hours ago", "-p", "0..3", "--no-pager", "-o", "short-unix", "-n", "100").Output()
|
||||
for _, line := range strings.Split(strings.TrimSpace(string(out)), "\n") {
|
||||
f := strings.Fields(line)
|
||||
if len(f) < 2 {
|
||||
continue
|
||||
}
|
||||
raw := strings.TrimSuffix(f[0], ":")
|
||||
sec, _ := strconv.ParseFloat(raw, 64)
|
||||
unit := strings.TrimSuffix(f[1], ":")
|
||||
message := strings.Join(f[2:], " ")
|
||||
issue := SystemIssue{Time: int64(sec), Unit: unit, Priority: "error", Message: message}
|
||||
m.Issues = append(m.Issues, issue)
|
||||
m.SystemErrors++
|
||||
if strings.Contains(strings.ToLower(unit), "kernel") {
|
||||
m.KernelErrors++
|
||||
}
|
||||
}
|
||||
c.mu.Lock()
|
||||
c.value = m
|
||||
c.mu.Unlock()
|
||||
}
|
||||
Reference in New Issue
Block a user