mirror of
https://github.com/RuoJi6/ARTEX.git
synced 2026-10-30 10:03:50 +08:00
63 lines
3.4 KiB
Go
63 lines
3.4 KiB
Go
//go:build linux
|
|
|
|
package server
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"runtime"
|
|
"strconv"
|
|
"strings"
|
|
"syscall"
|
|
"time"
|
|
)
|
|
|
|
func systemMetricsSnapshot(s *Server) systemMetricsResponse {
|
|
use, limit, cpuOK := cgroupCPU()
|
|
cpuPct := 0.0
|
|
if cpuOK {
|
|
now := time.Now()
|
|
s.metricsMu.Lock()
|
|
if !s.lastCPUAt.IsZero() && use >= s.lastCPUUse && limit > 0 {
|
|
elapsed := now.Sub(s.lastCPUAt).Seconds()
|
|
if elapsed > 0 { cpuPct = float64(use-s.lastCPUUse) / 1e6 / elapsed / limit * 100 }
|
|
}
|
|
s.lastCPUUse, s.lastCPUAt = use, now
|
|
s.metricsMu.Unlock()
|
|
}
|
|
memUsed, memTotal, memOK := cgroupMemory()
|
|
root, data := diskMetric("/"), diskMetric(s.dataDir)
|
|
if data.TotalBytes == 0 { data = root }
|
|
return systemMetricsResponse{Scope: "container", Supported: true, Timestamp: time.Now().UTC(), Container: cpuOK || memOK,
|
|
CPU: metricValue{Percent: clampPercent(cpuPct)}, Memory: metricValue{UsedBytes: memUsed, TotalBytes: memTotal, Percent: percent(memUsed, memTotal)}, Disk: root, DataDisk: data}
|
|
}
|
|
|
|
func cgroupCPU() (uint64, float64, bool) {
|
|
for _, dir := range []string{"/sys/fs/cgroup", "/sys/fs/cgroup/cpu"} {
|
|
b, err := os.ReadFile(filepath.Join(dir, "cpu.stat")); if err != nil { continue }
|
|
var use uint64
|
|
for _, line := range strings.Split(string(b), "\n") { f := strings.Fields(line); if len(f) == 2 && f[0] == "usage_usec" { use, _ = strconv.ParseUint(f[1], 10, 64) } }
|
|
limit := float64(runtime.NumCPU())
|
|
if q, err := os.ReadFile(filepath.Join(dir, "cpu.max")); err == nil { f := strings.Fields(string(q)); if len(f) == 2 && f[0] != "max" { quota, _ := strconv.ParseFloat(f[0], 64); period, _ := strconv.ParseFloat(f[1], 64); if period > 0 { limit = quota / period } } }
|
|
if use > 0 { return use, limit, true }
|
|
}
|
|
return 0, 0, false
|
|
}
|
|
|
|
func cgroupMemory() (uint64, uint64, bool) {
|
|
for _, dir := range []string{"/sys/fs/cgroup", "/sys/fs/cgroup/memory"} {
|
|
cur, ce := readUint(filepath.Join(dir, "memory.current")); max, me := readUint(filepath.Join(dir, "memory.max"))
|
|
if ce == nil && me == nil { return cur, max, max > 0 }
|
|
if ce == nil { if b, err := os.ReadFile(filepath.Join(dir, "memory.max")); err == nil && strings.TrimSpace(string(b)) == "max" { return cur, hostMemoryTotal(), true } }
|
|
cur, ce = readUint(filepath.Join(dir, "memory.usage_in_bytes")); max, me = readUint(filepath.Join(dir, "memory.limit_in_bytes"))
|
|
if ce == nil && me == nil && max < ^uint64(0)/2 { return cur, max, true }
|
|
}
|
|
return 0, 0, false
|
|
}
|
|
|
|
func hostMemoryTotal() uint64 { b, err := os.ReadFile("/proc/meminfo"); if err != nil { return 0 }; for _, line := range strings.Split(string(b), "\n") { f := strings.Fields(line); if len(f) >= 2 && f[0] == "MemTotal:" { n, _ := strconv.ParseUint(f[1], 10, 64); return n * 1024 } }; return 0 }
|
|
func readUint(path string) (uint64, error) { b, err := os.ReadFile(path); if err != nil { return 0, err }; return strconv.ParseUint(strings.TrimSpace(string(b)), 10, 64) }
|
|
func diskMetric(path string) metricValue { var st syscall.Statfs_t; if path == "" || syscall.Statfs(path, &st) != nil { return metricValue{} }; total, free := st.Blocks*uint64(st.Bsize), st.Bavail*uint64(st.Bsize); used := total-free; return metricValue{UsedBytes: used, TotalBytes: total, Percent: percent(used, total)} }
|
|
func percent(used, total uint64) float64 { if total == 0 { return 0 }; return clampPercent(float64(used)/float64(total)*100) }
|
|
func clampPercent(v float64) float64 { if v < 0 { return 0 }; if v > 100 { return 100 }; return v }
|