Files
HY2XS_flamy/apps/service/metrics_collector.go
T

142 lines
3.7 KiB
Go

package service
import (
"hy2xs-admin/dao"
"hy2xs-admin/model/entity"
"hy2xs-admin/model/vo"
"hy2xs-admin/util"
"sync"
"time"
)
const hysteriaVersionCacheTTL = 15 * time.Minute
type metricsSnapshot struct {
CollectedAt int64
System vo.DashboardSystemVo
Hysteria vo.DashboardHysteriaVo
OnlinePeers int64
OnlineDevices int64
CollectorState vo.DataHealthVo
HysteriaState vo.DataHealthVo
}
var metricsStore = struct {
sync.RWMutex
snapshot metricsSnapshot
lastSuccessAt int64
version string
versionAt time.Time
}{}
func getCachedHysteriaVersion(now time.Time) string {
metricsStore.RLock()
if metricsStore.version != "" && now.Sub(metricsStore.versionAt) < hysteriaVersionCacheTTL {
v := metricsStore.version
metricsStore.RUnlock()
return v
}
metricsStore.RUnlock()
content, err := util.Exec(util.GetHysteria2BinPath() + " version")
if err != nil {
metricsStore.RLock()
v := metricsStore.version
metricsStore.RUnlock()
if v != "" {
return v
}
return "-"
}
metricsStore.Lock()
metricsStore.version = content
metricsStore.versionAt = now
metricsStore.Unlock()
return content
}
func CollectMetricsSnapshot() {
nowMs := time.Now().UnixMilli()
s := metricsSnapshot{
CollectedAt: nowMs,
CollectorState: vo.DataHealthVo{Status: "ok", LastSuccessAt: nowMs},
HysteriaState: vo.DataHealthVo{Status: "ok", LastSuccessAt: nowMs},
}
if cpuPercent, err := util.GetCpuPercent(); err == nil {
s.System.CpuPercent = cpuPercent
}
if memPercent, err := util.GetMemPercent(); err == nil {
s.System.MemPercent = memPercent
}
if memInfo, err := util.GetMemInfo(); err == nil {
s.System.MemUsedBytes = memInfo.Used
s.System.MemTotalBytes = memInfo.Total
}
if diskPercent, err := util.GetDiskPercent(); err == nil {
s.System.DiskPercent = diskPercent
}
if diskInfo, err := util.GetDiskInfo(); err == nil {
s.System.DiskUsedBytes = diskInfo.Used
s.System.DiskTotalBytes = diskInfo.Total
}
s.Hysteria.Running = Hysteria2IsRunning()
s.Hysteria.Version = getCachedHysteriaVersion(time.Now())
if onlineMap, err := Hysteria2Online(); err == nil {
s.Hysteria.ApiReachable = true
s.OnlinePeers = int64(len(onlineMap))
for _, c := range onlineMap {
s.OnlineDevices += c
}
} else {
s.Hysteria.ApiReachable = false
s.Hysteria.LastApiError = "dashboard.error.trafficApiUnavailable"
s.HysteriaState.Status = "error"
s.HysteriaState.MessageKey = "dashboard.error.trafficApiUnavailable"
}
metricsStore.Lock()
metricsStore.snapshot = s
metricsStore.lastSuccessAt = nowMs
metricsStore.Unlock()
running := int64(0)
if s.Hysteria.Running {
running = 1
}
diskPath := "/"
if configured := util.GetEnvDiskPath(); configured != "" {
diskPath = configured
}
metric := entity.MetricSample{
SampledAt: &nowMs,
CpuPercent: &s.System.CpuPercent,
Load1: func() *float64 { v := float64(0); return &v }(),
MemUsedBytes: func() *int64 { v := int64(s.System.MemUsedBytes); return &v }(),
MemTotalBytes: func() *int64 { v := int64(s.System.MemTotalBytes); return &v }(),
MemPercent: &s.System.MemPercent,
DiskPath: &diskPath,
DiskUsedBytes: func() *int64 { v := int64(s.System.DiskUsedBytes); return &v }(),
DiskTotalBytes: func() *int64 { v := int64(s.System.DiskTotalBytes); return &v }(),
DiskPercent: &s.System.DiskPercent,
HysteriaRunning: &running,
OnlinePeers: &s.OnlinePeers,
OnlineDevices: &s.OnlineDevices,
}
_ = dao.SaveMetricSample(metric)
}
func CleanupMetricsRetention() {
cutoff := time.Now().Add(-72 * time.Hour).UnixMilli()
_ = dao.CleanupMetricSample(cutoff)
}
func DashboardSnapshot() metricsSnapshot {
metricsStore.RLock()
defer metricsStore.RUnlock()
return metricsStore.snapshot
}