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 NetworkIn float64 NetworkOut float64 ActiveConns int HlsReaders int ViewerCount int FrameDrops int PacketLoss int MediaAvailable bool } 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 { metrics.NetworkIn = float64(curIn-m.prevNetIn) * 8 / elapsed / 1e6 metrics.NetworkOut = float64(curOut-m.prevNetOut) * 8 / elapsed / 1e6 metrics.Bandwidth = metrics.NetworkIn + metrics.NetworkOut } 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 } func (m *Monitor) SetMediaMetrics(media MediaMetrics) { m.mu.Lock() m.current.ViewerCount = media.ViewerCount m.current.HlsReaders = media.ViewerCount m.current.FrameDrops = media.FrameDrops m.current.PacketLoss = media.PacketLoss m.current.MediaAvailable = media.Available m.mu.Unlock() }