// Package collect 实现主机指标采集与文件日志 tail 采集。 package collect import ( "fmt" "github.com/shirou/gopsutil/v4/cpu" "github.com/shirou/gopsutil/v4/disk" "github.com/shirou/gopsutil/v4/load" "github.com/shirou/gopsutil/v4/mem" "github.com/shirou/gopsutil/v4/net" "github.com/shirou/gopsutil/v4/process" ) // 以下结构是对 gopsutil 原始返回值的抽象,采集层不直接暴露 gopsutil 类型, // 便于单元测试注入 fake 数据。 // DiskUsageStat 描述一个挂载点的磁盘使用率。 type DiskUsageStat struct { Mountpoint string UsedPercent float64 } // DiskIOCounter 描述一个块设备的累计读写字节数。 type DiskIOCounter struct { ReadBytes uint64 WriteBytes uint64 } // NetIOCounter 描述一个网卡的累计收发/丢包计数。 type NetIOCounter struct { BytesSent uint64 BytesRecv uint64 DropOut uint64 DropIn uint64 } // ProcessStat 描述一个进程的关键资源占用。 type ProcessStat struct { PID int32 Name string CPUPercent float64 MemPercent float64 MemBytes uint64 } // System 抽象主机系统信息读取。生产环境使用 GopsutilSystem,测试注入 fake。 type System interface { CPUUsage() (float64, error) PerCPUUsage() ([]float64, error) MemUsedPercent() (float64, error) Load1() (float64, error) DiskUsages() ([]DiskUsageStat, error) DiskIOCounters() (map[string]DiskIOCounter, error) NetIOCounters() (map[string]NetIOCounter, error) Processes() ([]ProcessStat, error) } // GopsutilSystem 是 System 的 gopsutil/v4 实现。 type GopsutilSystem struct{} func (GopsutilSystem) CPUUsage() (float64, error) { ps, err := cpu.Percent(0, false) if err != nil { return 0, err } if len(ps) == 0 { return 0, fmt.Errorf("cpu.Percent returned no samples") } return ps[0], nil } func (GopsutilSystem) PerCPUUsage() ([]float64, error) { return cpu.Percent(0, true) } func (GopsutilSystem) MemUsedPercent() (float64, error) { vm, err := mem.VirtualMemory() if err != nil { return 0, err } return vm.UsedPercent, nil } func (GopsutilSystem) Load1() (float64, error) { avg, err := load.Avg() if err != nil { return 0, err } return avg.Load1, nil } func (GopsutilSystem) DiskUsages() ([]DiskUsageStat, error) { parts, err := disk.Partitions(false) if err != nil { return nil, err } out := make([]DiskUsageStat, 0, len(parts)) for _, p := range parts { if p.Mountpoint == "" { continue } usage, err := disk.Usage(p.Mountpoint) if err != nil { continue } out = append(out, DiskUsageStat{Mountpoint: p.Mountpoint, UsedPercent: usage.UsedPercent}) } return out, nil } func (GopsutilSystem) DiskIOCounters() (map[string]DiskIOCounter, error) { raw, err := disk.IOCounters() if err != nil { return nil, err } out := make(map[string]DiskIOCounter, len(raw)) for name, v := range raw { out[name] = DiskIOCounter{ReadBytes: v.ReadBytes, WriteBytes: v.WriteBytes} } return out, nil } func (GopsutilSystem) NetIOCounters() (map[string]NetIOCounter, error) { raw, err := net.IOCounters(true) if err != nil { return nil, err } out := make(map[string]NetIOCounter, len(raw)) for _, v := range raw { out[v.Name] = NetIOCounter{ BytesSent: v.BytesSent, BytesRecv: v.BytesRecv, DropOut: v.Dropout, DropIn: v.Dropin, } } return out, nil } func (GopsutilSystem) Processes() ([]ProcessStat, error) { raw, err := process.Processes() if err != nil { return nil, err } out := make([]ProcessStat, 0, len(raw)) for _, p := range raw { stat := ProcessStat{PID: p.Pid} if name, err := p.Name(); err == nil { stat.Name = name } if cpuPercent, err := p.CPUPercent(); err == nil { stat.CPUPercent = cpuPercent } if memPercent, err := p.MemoryPercent(); err == nil { stat.MemPercent = float64(memPercent) } if memInfo, err := p.MemoryInfo(); err == nil { stat.MemBytes = memInfo.RSS } out = append(out, stat) } return out, nil }