feat(monitor): 自 go-hua 迁入系统资源监控
- Start 后台每秒采集(CPU/内存/磁盘/网络/进程/连接/主机), GetInfo 取快照 - 基于 gopsutil, 附测试、examples/monitor 例程与包 README
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
# monitor
|
||||
|
||||
系统资源监控:CPU/内存/磁盘/网络/进程/TCP·UDP 连接/主机信息,基于 `gopsutil`。
|
||||
`Start` 后台每秒采集(主机/连接每 5 秒),`GetInfo` 随时取最新快照。
|
||||
|
||||
> 迁移自 go-hua/monitor,代码保持原样。
|
||||
|
||||
## 用法
|
||||
|
||||
```go
|
||||
import (
|
||||
"time"
|
||||
|
||||
"git.zeroonesoft.cn/golib/zogo/monitor"
|
||||
)
|
||||
|
||||
// 启动后台采集
|
||||
monitor.Start("1.0.0")
|
||||
time.Sleep(6 * time.Second) // 等采集几轮
|
||||
|
||||
info := monitor.GetInfo() // MonInfo 全量快照
|
||||
fmt.Println(info.Cpu.UsedPercent, info.Mem.UsedPercent)
|
||||
|
||||
// 精简版快照(面向上报的轻量结构)
|
||||
lite := monitor.ToLiteInfo(info)
|
||||
|
||||
// 单项直查(不经后台采集)
|
||||
procs, err := monitor.GetProcessInfo()
|
||||
```
|
||||
|
||||
完整可运行例程:[examples/monitor/main.go](../examples/monitor/main.go)
|
||||
|
||||
## 注意
|
||||
|
||||
- `Start` 起后台 goroutine,进程退出无需显式停止;重复调用会起多份采集。
|
||||
- CPU 使用率首次采样为 0(需两个采样窗口),取值前至少等一个采集周期。
|
||||
@@ -0,0 +1,93 @@
|
||||
package monitor
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/shirou/gopsutil/v3/cpu"
|
||||
)
|
||||
|
||||
// CPU CPU 采集信息
|
||||
type CPU struct {
|
||||
CPU int32 `json:"cpu"`
|
||||
VendorID string `json:"vendorId"`
|
||||
ModelName string `json:"modelName"`
|
||||
Mhz float64 `json:"mhz"`
|
||||
Cores int32 `json:"cores"`
|
||||
}
|
||||
|
||||
// CpuBaseInfo CPU 基本信息(型号/核心数/频率等,gopsutil cpu.Info)
|
||||
type CpuBaseInfo struct {
|
||||
Core int `json:"core"`
|
||||
Logic int `json:"logic"`
|
||||
Info []CPU `json:"info"`
|
||||
}
|
||||
|
||||
// CpuInfo 每逻辑核心使用率
|
||||
type CpuInfo struct {
|
||||
CpuBaseInfo
|
||||
CpuUsedInfo
|
||||
}
|
||||
|
||||
// CpuUsedInfo CPU 使用率明细(含用户/系统/空闲等占比)
|
||||
type CpuUsedInfo struct {
|
||||
UsedPercent float64 `json:"usedPercent"`
|
||||
Used []float64 `json:"used"`
|
||||
}
|
||||
|
||||
// CpuInfoLite 精简版 CPU 信息
|
||||
type CpuInfoLite struct {
|
||||
Core int `json:"core"`
|
||||
Logic int `json:"logic"`
|
||||
Name []string `json:"name"`
|
||||
UsedPercent float64 `json:"usedPercent"`
|
||||
Used []float64 `json:"used"`
|
||||
}
|
||||
|
||||
// getCpuInfo 采集 CPU 基础信息与每核心使用率
|
||||
func getCpuInfo() (CpuInfo, error) {
|
||||
info := CpuInfo{}
|
||||
cpuInfo, err := cpu.Info()
|
||||
if err != nil {
|
||||
return info, err
|
||||
}
|
||||
info.Info = make([]CPU, len(cpuInfo))
|
||||
for i, v := range cpuInfo {
|
||||
info.Info[i].CPU = v.CPU
|
||||
info.Info[i].VendorID = v.VendorID
|
||||
info.Info[i].ModelName = v.ModelName
|
||||
info.Info[i].Mhz = v.Mhz
|
||||
info.Info[i].Cores = v.Cores
|
||||
}
|
||||
|
||||
core, err := cpu.Counts(false)
|
||||
if err != nil {
|
||||
return info, err
|
||||
}
|
||||
logic, err := cpu.Counts(true)
|
||||
if err != nil {
|
||||
return info, err
|
||||
}
|
||||
|
||||
info.Core = core
|
||||
info.Logic = logic
|
||||
return info, nil
|
||||
}
|
||||
|
||||
// getCpuUsed 采集 CPU 使用率明细
|
||||
func getCpuUsed() (CpuUsedInfo, error) {
|
||||
info := CpuUsedInfo{}
|
||||
percent, err := cpu.Percent(0, true)
|
||||
if err != nil {
|
||||
return info, err
|
||||
}
|
||||
var usedPercent float64 = 0.0
|
||||
for i, v := range percent {
|
||||
percent[i], _ = strconv.ParseFloat(fmt.Sprintf("%.1f", v), 64)
|
||||
usedPercent += v
|
||||
}
|
||||
usedPercent, _ = strconv.ParseFloat(fmt.Sprintf("%.1f", usedPercent/float64(len(percent))), 64)
|
||||
info.Used = percent
|
||||
info.UsedPercent = usedPercent
|
||||
return info, nil
|
||||
}
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
package monitor
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/shirou/gopsutil/v3/disk"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// DiskInfo 磁盘分区信息(空间占用 + IO 统计)
|
||||
type DiskInfo struct {
|
||||
Device string `json:"device"`
|
||||
MountPoint string `json:"mountPoint"`
|
||||
FsType string `json:"fsType"`
|
||||
Path string `json:"path"`
|
||||
Total uint64 `json:"total"`
|
||||
Free uint64 `json:"free"`
|
||||
Used uint64 `json:"used"`
|
||||
UsedPercent float64 `json:"usedPercent"`
|
||||
UsedVolume string `json:"usedVolume"`
|
||||
ReadCount uint64 `json:"readCount"`
|
||||
WriteCount uint64 `json:"writeCount"`
|
||||
ReadBytes uint64 `json:"readBytes"`
|
||||
WriteBytes uint64 `json:"writeBytes"`
|
||||
ReadTime uint64 `json:"readTime"`
|
||||
WriteTime uint64 `json:"writeTime"`
|
||||
TotalReadCount uint64 `json:"totalReadCount"`
|
||||
TotalWriteCount uint64 `json:"totalWriteCount"`
|
||||
TotalReadBytes uint64 `json:"totalReadBytes"`
|
||||
TotalWriteBytes uint64 `json:"totalWriteBytes"`
|
||||
TotalReadTime uint64 `json:"totalReadTime"`
|
||||
TotalWriteTime uint64 `json:"totalWriteTime"`
|
||||
}
|
||||
|
||||
// DiskInfoLite 精简版磁盘信息
|
||||
type DiskInfoLite struct {
|
||||
Device string `json:"device"`
|
||||
MountPoint string `json:"mountPoint"`
|
||||
UsedPercent float64 `json:"usedPercent"`
|
||||
UsedVolume string `json:"usedVolume"`
|
||||
ReadBytes uint64 `json:"readBytes"`
|
||||
WriteBytes uint64 `json:"writeBytes"`
|
||||
}
|
||||
|
||||
// getDiskInfo 采集所有磁盘分区信息
|
||||
func getDiskInfo() ([]DiskInfo, error) {
|
||||
|
||||
partitions, err := disk.Partitions(false)
|
||||
// fmt.Printf("disk partitions:%v\n\n", partitions)
|
||||
if err != nil {
|
||||
// fmt.Printf("get disk partitions err:%s\n", err.Error())
|
||||
// return nil, err
|
||||
}
|
||||
|
||||
names := make([]string, len(partitions))
|
||||
// fmt.Println(len(names))
|
||||
for i, _ := range partitions {
|
||||
names[i] = partitions[i].Device
|
||||
}
|
||||
// fmt.Println(names)
|
||||
|
||||
io, err := disk.IOCounters(names...)
|
||||
// fmt.Printf("disk io:%v\n\n", io)
|
||||
if err != nil {
|
||||
//return nil, err
|
||||
// fmt.Printf("get disk IOCounters err:%s\n", err.Error())
|
||||
}
|
||||
|
||||
disks := make([]DiskInfo, len(partitions))
|
||||
for i, _ := range disks {
|
||||
disks[i].Device = partitions[i].Device
|
||||
disks[i].MountPoint = partitions[i].Mountpoint
|
||||
disks[i].FsType = partitions[i].Fstype
|
||||
|
||||
// 使用量 用路径获取
|
||||
usage, err := disk.Usage(partitions[i].Mountpoint)
|
||||
if err != nil {
|
||||
// fmt.Printf("get disk Usage err:%s\n", err.Error())
|
||||
break
|
||||
}
|
||||
|
||||
disks[i].Path = usage.Path
|
||||
disks[i].Total = usage.Total
|
||||
disks[i].Free = usage.Total
|
||||
disks[i].Used = usage.Used
|
||||
disks[i].UsedPercent, _ = strconv.ParseFloat(fmt.Sprintf("%.2f", usage.UsedPercent), 64)
|
||||
disks[i].UsedVolume = formatFileSize(usage.Used) + "/" + formatFileSize(usage.Total)
|
||||
name := partitions[i].Device
|
||||
if runtime.GOOS == "linux" {
|
||||
name = filepath.Base(name)
|
||||
}
|
||||
|
||||
//fmt.Println(name, partitions[i].Device)
|
||||
disks[i].TotalReadCount = io[name].ReadCount
|
||||
disks[i].TotalWriteCount = io[name].WriteCount
|
||||
disks[i].TotalReadBytes = io[name].ReadBytes
|
||||
disks[i].TotalWriteBytes = io[name].WriteBytes
|
||||
disks[i].TotalReadTime = io[name].ReadTime
|
||||
disks[i].TotalWriteTime = io[name].WriteTime
|
||||
|
||||
// FIXME:分区会变化,应该用name获取
|
||||
if len(monInfo.Disk) > i {
|
||||
disks[i].ReadCount = io[name].ReadCount - monInfo.Disk[i].TotalReadCount
|
||||
disks[i].WriteCount = io[name].WriteCount - monInfo.Disk[i].TotalWriteCount
|
||||
disks[i].ReadBytes = io[name].ReadBytes - monInfo.Disk[i].TotalReadBytes
|
||||
disks[i].WriteBytes = io[name].WriteBytes - monInfo.Disk[i].TotalWriteBytes
|
||||
disks[i].ReadTime = io[name].ReadTime - monInfo.Disk[i].TotalReadTime
|
||||
disks[i].WriteTime = io[name].WriteTime - monInfo.Disk[i].TotalWriteTime
|
||||
}
|
||||
|
||||
}
|
||||
if err != nil {
|
||||
return disks, err
|
||||
}
|
||||
return disks, nil
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package monitor
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/shirou/gopsutil/v3/host"
|
||||
)
|
||||
|
||||
// HostInfo 主机信息(主机名/系统/开机时长等,gopsutil host.InfoStat)
|
||||
type HostInfo struct {
|
||||
HostName string `json:"hostName"`
|
||||
Uptime uint64 `json:"uptime"`
|
||||
BootTime uint64 `json:"bootTime"`
|
||||
Procs uint64 `json:"procs"`
|
||||
Os string `json:"os"`
|
||||
Platform string `json:"platform"`
|
||||
}
|
||||
|
||||
// getHostInfo 采集主机信息
|
||||
func getHostInfo() (HostInfo, error) {
|
||||
info := HostInfo{}
|
||||
h, err := host.Info()
|
||||
|
||||
if err != nil {
|
||||
return info, err
|
||||
}
|
||||
info.HostName = h.Hostname
|
||||
info.Uptime = h.Uptime
|
||||
info.BootTime = h.BootTime
|
||||
info.Procs = h.Procs
|
||||
info.Os = h.OS
|
||||
info.Platform = strings.Trim(strings.Replace(h.Platform, "Microsoft", "", -1), " ")
|
||||
return info, nil
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package monitor
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/shirou/gopsutil/v3/load"
|
||||
)
|
||||
|
||||
// LoadInfo 系统负载信息(1/5/15 分钟均值)
|
||||
type LoadInfo struct {
|
||||
Load1 float64 `json:"load1"`
|
||||
Load5 float64 `json:"load5"`
|
||||
Load15 float64 `json:"load15"`
|
||||
}
|
||||
|
||||
// getLoadInfo 采集系统负载
|
||||
func getLoadInfo() (LoadInfo, error) {
|
||||
info := LoadInfo{}
|
||||
v, err := load.Avg()
|
||||
if err != nil {
|
||||
return info, err
|
||||
}
|
||||
info.Load1, _ = strconv.ParseFloat(fmt.Sprintf("%.2f", v.Load1), 64)
|
||||
info.Load5, _ = strconv.ParseFloat(fmt.Sprintf("%.2f", v.Load5), 64)
|
||||
info.Load15, _ = strconv.ParseFloat(fmt.Sprintf("%.2f", v.Load15), 64)
|
||||
//fmt.Printf("%v\n", info)
|
||||
return info, nil
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package monitor
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/shirou/gopsutil/v3/mem"
|
||||
)
|
||||
|
||||
// SwapDevice 交换分区设备信息
|
||||
type SwapDevice struct {
|
||||
Name string `json:"name"`
|
||||
UsedBytes uint64 `json:"usedBytes"`
|
||||
FreeBytes uint64 `json:"freeBytes"`
|
||||
}
|
||||
|
||||
// MemInfo 内存信息(总量/已用/可用/使用率等,gopsutil mem.VirtualMemoryStat)
|
||||
type MemInfo struct {
|
||||
Total uint64 `json:"total"`
|
||||
Available uint64 `json:"available"`
|
||||
Used uint64 `json:"used"`
|
||||
Free uint64 `json:"free"`
|
||||
UsedPercent float64 `json:"usedPercent"`
|
||||
SwapTotal uint64 `json:"swapTotal"`
|
||||
SwapAvailable uint64 `json:"SwapAvailable"`
|
||||
SwapUsed uint64 `json:"swapUsed"`
|
||||
SwapFree uint64 `json:"swapFree"`
|
||||
SwapUsedPercent float64 `json:"swapUsedPercent"`
|
||||
SwapDevices []SwapDevice `json:"swapDevices"`
|
||||
}
|
||||
|
||||
// getMemInfo 采集内存与交换区信息
|
||||
func getMemInfo() (MemInfo, error) {
|
||||
|
||||
info := MemInfo{}
|
||||
|
||||
v, err := mem.VirtualMemory()
|
||||
if err != nil {
|
||||
return info, err
|
||||
}
|
||||
info.Total = v.Total
|
||||
info.Used = v.Used
|
||||
info.Free = v.Free
|
||||
info.UsedPercent, _ = strconv.ParseFloat(fmt.Sprintf("%.1f", v.UsedPercent), 64)
|
||||
|
||||
s, err := mem.SwapMemory()
|
||||
if err != nil {
|
||||
return info, err
|
||||
}
|
||||
info.SwapTotal = s.Total
|
||||
info.SwapUsed = s.Used
|
||||
info.SwapFree = s.Free
|
||||
info.SwapUsedPercent, _ = strconv.ParseFloat(fmt.Sprintf("%.1f", s.UsedPercent), 64)
|
||||
|
||||
devices, err := mem.SwapDevices()
|
||||
if err != nil {
|
||||
return info, err
|
||||
}
|
||||
for _, device := range devices {
|
||||
info.SwapDevices = append(info.SwapDevices, SwapDevice{
|
||||
Name: device.Name,
|
||||
UsedBytes: device.UsedBytes,
|
||||
FreeBytes: device.FreeBytes,
|
||||
})
|
||||
}
|
||||
return info, err
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
// Package monitor 提供系统资源监控(CPU/内存/磁盘/网络/进程/连接/主机),后台定时采集,GetInfo 随时取最新快照。
|
||||
package monitor
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// MonInfo 全量监控信息快照
|
||||
type MonInfo struct {
|
||||
Host HostInfo `json:"host"`
|
||||
Cpu CpuInfo `json:"cpu"`
|
||||
Mem MemInfo `json:"mem"`
|
||||
Load LoadInfo `json:"load"`
|
||||
Process []ProcessInfo `json:"process"`
|
||||
Disk []DiskInfo `json:"disk"`
|
||||
Net []NetInfo `json:"net"`
|
||||
Conn NetConnInfo `json:"conn"`
|
||||
Ver string `json:"ver"`
|
||||
}
|
||||
|
||||
// MonInfoSimple 精简版监控信息(适合定时上报)
|
||||
type MonInfoSimple struct {
|
||||
Host HostInfo `json:"host"`
|
||||
Cpu CpuInfoLite `json:"cpu"`
|
||||
Mem MemInfo `json:"mem"`
|
||||
Load LoadInfo `json:"load"`
|
||||
Conn NetConnInfo `json:"conn"`
|
||||
Net NetInfoLite `json:"net"`
|
||||
Disk []DiskInfoLite `json:"disk"`
|
||||
Ver string `json:"ver"`
|
||||
}
|
||||
|
||||
var (
|
||||
interval int = 1
|
||||
monInfo MonInfo
|
||||
mux sync.Mutex
|
||||
one sync.Once
|
||||
)
|
||||
|
||||
// Start 启动后台采集
|
||||
// 普通信息每秒刷新,主机/连接信息每 5 秒刷新一次;ver 为程序版本号
|
||||
func Start(ver string) {
|
||||
monInfo.Ver = ver
|
||||
one.Do(proc)
|
||||
}
|
||||
|
||||
// GetInfo 获取最新的监控信息快照
|
||||
func GetInfo() MonInfo {
|
||||
mux.Lock()
|
||||
info := monInfo
|
||||
mux.Unlock()
|
||||
return info
|
||||
}
|
||||
|
||||
// proc 监控采集主循环
|
||||
func proc() {
|
||||
|
||||
go func() {
|
||||
var i uint64 = 0
|
||||
// CPU信息应该不会变,CPU热插拨除外
|
||||
{
|
||||
cpuBaseVal, _ := getCpuInfo()
|
||||
monInfo.Cpu.Core = cpuBaseVal.Core
|
||||
monInfo.Cpu.Logic = cpuBaseVal.Logic
|
||||
monInfo.Cpu.Info = cpuBaseVal.Info
|
||||
monInfo.Cpu.Core = cpuBaseVal.Core
|
||||
}
|
||||
|
||||
// 循环获取信息
|
||||
for {
|
||||
i = i + uint64(interval)
|
||||
|
||||
//每次都获取
|
||||
diskVal, err := getDiskInfo()
|
||||
if err != nil {
|
||||
fmt.Println("获取硬盘信出错:", err.Error())
|
||||
}
|
||||
cpuUsedVal, _ := getCpuUsed()
|
||||
memVal, _ := getMemInfo()
|
||||
netVal, _ := getNetInfo()
|
||||
loadVal, _ := getLoadInfo()
|
||||
process, _ := GetProcessInfo()
|
||||
|
||||
mux.Lock()
|
||||
//只获取CPU使用率,CPU基本信息一般不会发生改变
|
||||
monInfo.Cpu.UsedPercent = cpuUsedVal.UsedPercent
|
||||
monInfo.Cpu.Used = cpuUsedVal.Used
|
||||
monInfo.Load = loadVal
|
||||
monInfo.Disk = diskVal
|
||||
monInfo.Mem = memVal
|
||||
monInfo.Net = netVal
|
||||
monInfo.Process = process
|
||||
mux.Unlock()
|
||||
|
||||
// 间隔5次调用一次 // 建议60秒获取一次,例如更改计算机名,导致获取不准确
|
||||
if i%5 == 0 {
|
||||
hostVal, _ := getHostInfo()
|
||||
connVal, _ := getNetConnInfo()
|
||||
|
||||
mux.Lock()
|
||||
monInfo.Host = hostVal
|
||||
monInfo.Conn = connVal
|
||||
mux.Unlock()
|
||||
}
|
||||
|
||||
time.Sleep(time.Second * time.Duration(interval))
|
||||
}
|
||||
}()
|
||||
|
||||
}
|
||||
|
||||
// ToLiteInfo 将全量信息转换为精简版
|
||||
func ToLiteInfo(info MonInfo) MonInfoSimple {
|
||||
lite := MonInfoSimple{}
|
||||
|
||||
lite.Host = info.Host
|
||||
lite.Load = info.Load
|
||||
lite.Mem = info.Mem
|
||||
lite.Conn = info.Conn
|
||||
|
||||
lite.Cpu.Core = info.Cpu.Core
|
||||
lite.Cpu.Logic = info.Cpu.Logic
|
||||
lite.Cpu.UsedPercent = info.Cpu.UsedPercent
|
||||
lite.Cpu.Logic = info.Cpu.Logic
|
||||
|
||||
lite.Cpu.Name = make([]string, len(info.Cpu.Info))
|
||||
for index, c := range info.Cpu.Info {
|
||||
lite.Cpu.Name[index] = c.ModelName
|
||||
}
|
||||
|
||||
lite.Disk = make([]DiskInfoLite, len(info.Disk))
|
||||
for index, d := range info.Disk {
|
||||
lite.Disk[index].Device = d.Device
|
||||
lite.Disk[index].MountPoint = d.MountPoint
|
||||
lite.Disk[index].UsedPercent = d.UsedPercent
|
||||
lite.Disk[index].UsedVolume = d.UsedVolume
|
||||
lite.Disk[index].ReadBytes = d.ReadBytes
|
||||
lite.Disk[index].WriteBytes = d.WriteBytes
|
||||
}
|
||||
|
||||
lite.Net = NetInfoLite{}
|
||||
for _, n := range info.Net {
|
||||
lite.Net.BytesSent += n.BytesSent
|
||||
lite.Net.BytesRecv += n.BytesRecv
|
||||
lite.Net.PacketsSent += n.PacketsSent
|
||||
lite.Net.PacketsRecv += n.PacketsRecv
|
||||
lite.Net.TotalBytesSent += n.TotalBytesSent
|
||||
lite.Net.TotalBytesRecv += n.TotalBytesRecv
|
||||
}
|
||||
lite.Ver = info.Ver
|
||||
return lite
|
||||
}
|
||||
|
||||
// 字节的单位转换 保留两位小数
|
||||
func formatFileSize(fileSize uint64) (size string) {
|
||||
if fileSize < 1024 {
|
||||
//return strconv.FormatInt(fileSize, 10) + "B"
|
||||
return fmt.Sprintf("%.2fB", float64(fileSize)/float64(1))
|
||||
} else if fileSize < (1024 * 1024) {
|
||||
return fmt.Sprintf("%.2fKB", float64(fileSize)/float64(1024))
|
||||
} else if fileSize < (1024 * 1024 * 1024) {
|
||||
return fmt.Sprintf("%.2fMB", float64(fileSize)/float64(1024*1024))
|
||||
} else if fileSize < (1024 * 1024 * 1024 * 1024) {
|
||||
return fmt.Sprintf("%.2fGB", float64(fileSize)/float64(1024*1024*1024))
|
||||
} else if fileSize < (1024 * 1024 * 1024 * 1024 * 1024) {
|
||||
return fmt.Sprintf("%.2fTB", float64(fileSize)/float64(1024*1024*1024*1024))
|
||||
} else { //if fileSize < (1024 * 1024 * 1024 * 1024 * 1024 * 1024)
|
||||
return fmt.Sprintf("%.2fEB", float64(fileSize)/float64(1024*1024*1024*1024*1024))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package monitor
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestToLiteInfo(t *testing.T) {
|
||||
info := MonInfo{
|
||||
Ver: "1.0.0",
|
||||
Cpu: CpuInfo{
|
||||
CpuBaseInfo: CpuBaseInfo{
|
||||
Core: 4,
|
||||
Logic: 8,
|
||||
Info: []CPU{{CPU: 0, ModelName: "TestCPU"}},
|
||||
},
|
||||
CpuUsedInfo: CpuUsedInfo{UsedPercent: 55.5},
|
||||
},
|
||||
Mem: MemInfo{Total: 16 * 1024 * 1024 * 1024, UsedPercent: 60},
|
||||
Load: LoadInfo{Load1: 0.5},
|
||||
Net: []NetInfo{
|
||||
{Name: "eth0", BytesSent: 10, BytesRecv: 20, PacketsSent: 1, PacketsRecv: 2, TotalBytesSent: 100, TotalBytesRecv: 200},
|
||||
{Name: "eth1", BytesSent: 30, BytesRecv: 40, PacketsSent: 3, PacketsRecv: 4, TotalBytesSent: 300, TotalBytesRecv: 400},
|
||||
},
|
||||
Disk: []DiskInfo{
|
||||
{Device: "C:", MountPoint: "C:\\", UsedPercent: 80, UsedVolume: "80GB", ReadBytes: 5, WriteBytes: 6},
|
||||
},
|
||||
}
|
||||
|
||||
lite := ToLiteInfo(info)
|
||||
|
||||
if lite.Ver != "1.0.0" {
|
||||
t.Errorf("Ver = %q", lite.Ver)
|
||||
}
|
||||
if lite.Cpu.Core != 4 || lite.Cpu.Logic != 8 {
|
||||
t.Errorf("Cpu Core/Logic = %d/%d, want 4/8", lite.Cpu.Core, lite.Cpu.Logic)
|
||||
}
|
||||
if lite.Cpu.UsedPercent != 55.5 {
|
||||
t.Errorf("Cpu.UsedPercent = %v", lite.Cpu.UsedPercent)
|
||||
}
|
||||
if len(lite.Cpu.Name) != 1 || lite.Cpu.Name[0] != "TestCPU" {
|
||||
t.Errorf("Cpu.Name = %v", lite.Cpu.Name)
|
||||
}
|
||||
if lite.Mem.Total != 16*1024*1024*1024 {
|
||||
t.Errorf("Mem.Total = %d", lite.Mem.Total)
|
||||
}
|
||||
if lite.Load.Load1 != 0.5 {
|
||||
t.Errorf("Load1 = %v", lite.Load.Load1)
|
||||
}
|
||||
// Net 多网卡应累加
|
||||
if lite.Net.BytesSent != 40 || lite.Net.BytesRecv != 60 {
|
||||
t.Errorf("Net.BytesSent/Recv = %d/%d, want 40/60", lite.Net.BytesSent, lite.Net.BytesRecv)
|
||||
}
|
||||
if lite.Net.TotalBytesSent != 400 || lite.Net.TotalBytesRecv != 600 {
|
||||
t.Errorf("Net.Total = %d/%d, want 400/600", lite.Net.TotalBytesSent, lite.Net.TotalBytesRecv)
|
||||
}
|
||||
if len(lite.Disk) != 1 || lite.Disk[0].Device != "C:" || lite.Disk[0].ReadBytes != 5 {
|
||||
t.Errorf("Disk = %+v", lite.Disk)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormatFileSize(t *testing.T) {
|
||||
cases := []struct {
|
||||
in uint64
|
||||
want string
|
||||
}{
|
||||
{512, "512.00B"},
|
||||
{1536, "1.50KB"},
|
||||
{1024 * 1024, "1.00MB"},
|
||||
{1024 * 1024 * 1024, "1.00GB"},
|
||||
}
|
||||
for _, c := range cases {
|
||||
if got := formatFileSize(c.in); got != c.want {
|
||||
t.Errorf("formatFileSize(%d) = %q, want %q", c.in, got, c.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetInfoZeroValue(t *testing.T) {
|
||||
// 未调用 Start 时 GetInfo 返回零值快照且不 panic
|
||||
info := GetInfo()
|
||||
if info.Ver != "" {
|
||||
t.Errorf("未启动时 Ver 应为空: %q", info.Ver)
|
||||
}
|
||||
}
|
||||
+217
@@ -0,0 +1,217 @@
|
||||
package monitor
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"github.com/shirou/gopsutil/v3/net"
|
||||
)
|
||||
|
||||
// NetInfo 网卡信息(gopsutil net.InterfaceStat)
|
||||
type NetInfo struct {
|
||||
Name string `json:"name"` // interface name
|
||||
BytesSent uint64 `json:"bytesSent"` // number of bytes sent
|
||||
BytesRecv uint64 `json:"bytesRecv"` // number of bytes received
|
||||
PacketsSent uint64 `json:"packetsSent"` // number of packets sent
|
||||
PacketsRecv uint64 `json:"packetsRecv"` // number of packets received
|
||||
TotalBytesSent uint64 `json:"totalBytesSent"` // number of bytes sent
|
||||
TotalBytesRecv uint64 `json:"totalBytesRecv"` // number of bytes received
|
||||
TotalPacketsSent uint64 `json:"totalPacketsSent"` // number of packets sent
|
||||
TotalPacketsRecv uint64 `json:"totalPacketsRecv"` // number of packets received
|
||||
}
|
||||
|
||||
// NetInfoLite 精简版网卡信息
|
||||
type NetInfoLite struct {
|
||||
BytesSent uint64 `json:"bytesSent"` // number of bytes sent
|
||||
BytesRecv uint64 `json:"bytesRecv"` // number of bytes received
|
||||
PacketsSent uint64 `json:"packetsSent"` // number of packets sent
|
||||
PacketsRecv uint64 `json:"packetsRecv"` // number of packets received
|
||||
TotalBytesSent uint64 `json:"totalBytesSent"` // number of bytes sent
|
||||
TotalBytesRecv uint64 `json:"totalBytesRecv"` // number of bytes received
|
||||
}
|
||||
|
||||
// Addr 网卡地址
|
||||
type Addr struct {
|
||||
Ip string `json:"ip"`
|
||||
Port uint32 `json:"port"`
|
||||
}
|
||||
|
||||
// ConnInfo 连接汇总:TCP/UDP 计数、监听端口列表与每进程连接计数
|
||||
type ConnInfo struct {
|
||||
Fd uint32 `json:"fd"`
|
||||
Family uint32 `json:"family"`
|
||||
Type uint32 `json:"type"`
|
||||
LocalAddr Addr `json:"localAddr"`
|
||||
RemoteAddr Addr `json:"remoteAddr"`
|
||||
Status string `json:"status"`
|
||||
Pid int32 `json:"pid"`
|
||||
}
|
||||
|
||||
// NetListen 监听端口详情
|
||||
type NetListen struct {
|
||||
Addr string `json:"addr"`
|
||||
Port uint32 `json:"port"`
|
||||
Conn int `json:"conn"`
|
||||
ConnInfo
|
||||
}
|
||||
|
||||
// ProcConn 每进程连接计数
|
||||
type ProcConn struct {
|
||||
Pid int `json:"pid"`
|
||||
Name string `json:"name"`
|
||||
Conn int `json:"conn"`
|
||||
}
|
||||
|
||||
// NetConnInfo 单条网络连接信息(gopsutil net.ConnectionStat)
|
||||
type NetConnInfo struct {
|
||||
Tcp int `json:"tcp"`
|
||||
Udp int `json:"udp"`
|
||||
Listen []NetListen `json:"listen"`
|
||||
}
|
||||
|
||||
// getNetInfo 采集网卡列表
|
||||
func getNetInfo() ([]NetInfo, error) {
|
||||
|
||||
netIo, err := net.IOCounters(true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nets := make([]NetInfo, len(netIo))
|
||||
for i, v := range netIo {
|
||||
nets[i].Name = netIo[i].Name
|
||||
nets[i].TotalBytesSent = v.BytesSent
|
||||
nets[i].TotalBytesRecv = v.BytesRecv
|
||||
nets[i].TotalPacketsSent = v.PacketsSent
|
||||
nets[i].TotalPacketsRecv = v.PacketsRecv
|
||||
|
||||
// 应该使用网卡名 未处理
|
||||
if len(monInfo.Net) > i {
|
||||
nets[i].BytesSent = v.BytesSent - monInfo.Net[i].TotalBytesSent
|
||||
nets[i].BytesRecv = v.BytesRecv - monInfo.Net[i].TotalBytesRecv
|
||||
nets[i].PacketsSent = v.PacketsSent - monInfo.Net[i].TotalPacketsSent
|
||||
nets[i].PacketsRecv = v.PacketsRecv - monInfo.Net[i].TotalPacketsRecv
|
||||
}
|
||||
}
|
||||
return nets, err
|
||||
}
|
||||
|
||||
// getNetConnInfo 采集 TCP/UDP 连接计数与监听端口汇总
|
||||
func getNetConnInfo() (NetConnInfo, error) {
|
||||
|
||||
// linux TcpStatus
|
||||
/* {
|
||||
"01": "ESTABLISHED",
|
||||
"02": "SYN_SENT",
|
||||
"03": "SYN_RECV",
|
||||
"04": "FIN_WAIT1",
|
||||
"05": "FIN_WAIT2",
|
||||
"06": "TIME_WAIT",
|
||||
"07": "CLOSE",
|
||||
"08": "CLOSE_WAIT",
|
||||
"09": "LAST_ACK",
|
||||
"0A": "LISTEN",
|
||||
"0B": "CLOSING",
|
||||
} */
|
||||
|
||||
// windows TcpStatus
|
||||
/* {
|
||||
1: "CLOSED",
|
||||
2: "LISTEN",
|
||||
3: "SYN_SENT",
|
||||
4: "SYN_RECEIVED",
|
||||
5: "ESTABLISHED",
|
||||
6: "FIN_WAIT_1",
|
||||
7: "FIN_WAIT_2",
|
||||
8: "CLOSE_WAIT",
|
||||
9: "CLOSING",
|
||||
10: "LAST_ACK",
|
||||
11: "TIME_WAIT",
|
||||
12: "DELETE",
|
||||
} */
|
||||
|
||||
/* {
|
||||
"all": {kindTCP4, kindTCP6, kindUDP4, kindUDP6},
|
||||
"tcp": {kindTCP4, kindTCP6},
|
||||
"tcp4": {kindTCP4},
|
||||
"tcp6": {kindTCP6},
|
||||
"udp": {kindUDP4, kindUDP6},
|
||||
"udp4": {kindUDP4},
|
||||
"udp6": {kindUDP6},
|
||||
"inet": {kindTCP4, kindTCP6, kindUDP4, kindUDP6},
|
||||
"inet4": {kindTCP4, kindUDP4},
|
||||
"inet6": {kindTCP6, kindUDP6},
|
||||
} */
|
||||
|
||||
info := NetConnInfo{}
|
||||
tcp, err := net.Connections("tcp")
|
||||
if err != nil {
|
||||
return info, nil
|
||||
}
|
||||
udp, err := net.Connections("udp")
|
||||
if err != nil {
|
||||
return info, nil
|
||||
}
|
||||
|
||||
//logger.Infof("net conn tcp:%d udp:%d", info.Tcp, info.Udp)
|
||||
|
||||
// 监听列表
|
||||
listen := make([]NetListen, 0)
|
||||
for _, v := range tcp {
|
||||
if v.Status == "LISTEN" {
|
||||
inPort := false
|
||||
for _, l := range listen {
|
||||
if l.Port == v.Laddr.Port {
|
||||
inPort = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !inPort {
|
||||
listen = append(listen, NetListen{
|
||||
Addr: v.Laddr.IP,
|
||||
Port: v.Laddr.Port,
|
||||
Conn: 0,
|
||||
ConnInfo: ConnInfo{
|
||||
Fd: v.Fd,
|
||||
Family: v.Family,
|
||||
Type: v.Type,
|
||||
LocalAddr: Addr{
|
||||
Ip: v.Laddr.IP,
|
||||
Port: v.Laddr.Port,
|
||||
},
|
||||
RemoteAddr: Addr{
|
||||
Ip: v.Raddr.IP,
|
||||
Port: v.Raddr.Port,
|
||||
},
|
||||
Status: v.Status,
|
||||
Pid: v.Pid,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 连接计数
|
||||
count := 0
|
||||
for _, v := range tcp {
|
||||
if v.Status == "ESTABLISHED" {
|
||||
for index, l := range listen {
|
||||
if l.Port == v.Laddr.Port {
|
||||
listen[index].Conn++
|
||||
break
|
||||
}
|
||||
}
|
||||
count++
|
||||
}
|
||||
}
|
||||
|
||||
//排序
|
||||
sort.Slice(listen, func(i, j int) bool {
|
||||
return listen[i].Conn > listen[j].Conn
|
||||
})
|
||||
|
||||
// TCP和UDP连接数
|
||||
info.Tcp = count //len(tcp)
|
||||
info.Udp = len(udp)
|
||||
|
||||
info.Listen = listen
|
||||
return info, nil
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package monitor
|
||||
|
||||
import (
|
||||
"github.com/shirou/gopsutil/v3/process"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// MemoryInfoStat 进程内存信息(RSS/虚拟内存)
|
||||
type MemoryInfoStat struct {
|
||||
Rss uint64 `json:"res"`
|
||||
Vms uint64 `json:"vms"`
|
||||
}
|
||||
|
||||
// ProcessInfo 进程信息(PID/路径/CPU/内存占用与启动时间)
|
||||
type ProcessInfo struct {
|
||||
Pid int32 `json:"pid"`
|
||||
Name string `json:"name"`
|
||||
ExE string `json:"exe"`
|
||||
Cmd string `json:"cmd"`
|
||||
Cpu float64 `json:"cpu"`
|
||||
Mem MemoryInfoStat `json:"mem"`
|
||||
CreateTime int64 `json:"createTime"`
|
||||
}
|
||||
|
||||
// GetProcessInfo 获取所有进程信息
|
||||
func GetProcessInfo() ([]ProcessInfo, error) {
|
||||
|
||||
v, err := process.Processes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
info := make([]ProcessInfo, len(v))
|
||||
|
||||
for i, p := range v {
|
||||
|
||||
name := ""
|
||||
exe, _ := p.Exe()
|
||||
cmd, _ := p.Cmdline()
|
||||
// 先从执行文件中获取进程名
|
||||
// 如果执行文件为空,再调用 Name() 方式,Name()方式很占用CPU
|
||||
if exe == "" {
|
||||
name, _ = p.Name()
|
||||
} else {
|
||||
name = filepath.Base(exe)
|
||||
}
|
||||
|
||||
mem, _ := p.MemoryInfo()
|
||||
cpu, _ := p.CPUPercent()
|
||||
createTime, _ := p.CreateTime()
|
||||
p.Times()
|
||||
// 不获取线程数 太占用用CPU
|
||||
// p.NumThreads()
|
||||
|
||||
info[i].Pid = p.Pid
|
||||
info[i].Name = name
|
||||
info[i].ExE = exe
|
||||
info[i].Cmd = cmd
|
||||
info[i].Cpu = cpu
|
||||
if mem != nil {
|
||||
info[i].Mem = MemoryInfoStat{
|
||||
Rss: mem.RSS,
|
||||
Vms: mem.VMS,
|
||||
}
|
||||
}
|
||||
info[i].CreateTime = createTime
|
||||
|
||||
if name == "QQ.exe" {
|
||||
//fmt.Printf("Pid:%d name:%s exe:%s\n", p.Pid, name, exe)
|
||||
//fmt.Printf("工作集:%d 提交大小:%d\n", mem.RSS, mem.VMS)
|
||||
//fmt.Printf("CPU:%.2f%% \n", cpu)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return info, nil
|
||||
}
|
||||
Reference in New Issue
Block a user