package agent import ( "log" "sync" "time" "github.com/shirou/gopsutil/v3/cpu" "github.com/shirou/gopsutil/v3/mem" "github.com/shirou/gopsutil/v3/net" ) type Metrics struct { CPU float64 Memory float64 Bandwidth float64 ActiveConns int HlsReaders int } type Monitor struct { mu sync.RWMutex current Metrics prevNetIn uint64 prevNetOut uint64 prevNetTime time.Time } func NewMonitor() *Monitor { return &Monitor{} } func (m *Monitor) Tick() { metrics := Metrics{} if pct, err := cpu.Percent(0, false); err == nil && len(pct) > 0 { metrics.CPU = pct[0] } if v, err := mem.VirtualMemory(); err == nil { metrics.Memory = v.UsedPercent } ioCounters, err := net.IOCounters(false) if err == nil && len(ioCounters) > 0 { now := time.Now() curIn := ioCounters[0].BytesRecv curOut := ioCounters[0].BytesSent if m.prevNetTime.IsZero() { m.prevNetIn = curIn m.prevNetOut = curOut m.prevNetTime = now } else { elapsed := now.Sub(m.prevNetTime).Seconds() if elapsed > 0 { bytesTotal := (curIn - m.prevNetIn) + (curOut - m.prevNetOut) metrics.Bandwidth = float64(bytesTotal) * 8 / elapsed / 1e6 } m.prevNetIn = curIn m.prevNetOut = curOut m.prevNetTime = now } } conns, _ := net.Connections("tcp") metrics.ActiveConns = len(conns) metrics.HlsReaders = 0 m.mu.Lock() m.current = metrics m.mu.Unlock() log.Printf("Metrics: CPU=%.1f%% MEM=%.1f%% BW=%.1fMbps CONN=%d", metrics.CPU, metrics.Memory, metrics.Bandwidth, metrics.ActiveConns) } func (m *Monitor) Snapshot() Metrics { m.mu.RLock() defer m.mu.RUnlock() return m.current }