From 02f5fdf62617da14d0d0262958a641c8d81f757d Mon Sep 17 00:00:00 2001 From: samwaf Date: Fri, 25 Jul 2025 15:25:21 +0800 Subject: [PATCH] feat:show system cpu memory disk percent #418 --- api/entrance.go | 3 + api/waf_system_monitor.go | 22 +++ go.mod | 6 +- go.sum | 16 ++- model/response/waf_system_monitor.go | 37 +++++ router/entrance.go | 1 + router/waf_system_monitor.go | 16 +++ service/waf_service/waf_system_monitor.go | 157 ++++++++++++++++++++++ wafmangeweb/localserver.go | 1 + 9 files changed, 256 insertions(+), 3 deletions(-) create mode 100644 api/waf_system_monitor.go create mode 100644 model/response/waf_system_monitor.go create mode 100644 router/waf_system_monitor.go create mode 100644 service/waf_service/waf_system_monitor.go diff --git a/api/entrance.go b/api/entrance.go index 2a8e381..4ada1e6 100644 --- a/api/entrance.go +++ b/api/entrance.go @@ -43,6 +43,7 @@ type APIGroup struct { WafTunnelApi WafVpConfigApi WafFileApi + WafSystemMonitorApi } var APIGroupAPP = new(APIGroup) @@ -99,4 +100,6 @@ var ( wafPrivateGroupService = waf_service.WafPrivateGroupServiceApp wafCacheRuleService = waf_service.WafCacheRuleServiceApp wafTunnelService = waf_service.WafTunnelServiceApp + + wafMonitorService = waf_service.WafSystemMonitorServiceApp ) diff --git a/api/waf_system_monitor.go b/api/waf_system_monitor.go new file mode 100644 index 0000000..de8d507 --- /dev/null +++ b/api/waf_system_monitor.go @@ -0,0 +1,22 @@ +package api + +import ( + "SamWaf/model/common/response" + "SamWaf/service/waf_service" + "github.com/gin-gonic/gin" +) + +type WafSystemMonitorApi struct { +} + +var wafSystemMonitorService = waf_service.WafSystemMonitorServiceApp + +// GetSystemMonitorApi 获取系统监控信息 +func (w *WafSystemMonitorApi) GetSystemMonitorApi(c *gin.Context) { + systemInfo, err := wafSystemMonitorService.GetSystemMonitorInfo() + if err != nil { + response.FailWithMessage("获取系统监控信息失败: "+err.Error(), c) + return + } + response.OkWithDetailed(systemInfo, "获取成功", c) +} diff --git a/go.mod b/go.mod index 4b76395..10129a0 100644 --- a/go.mod +++ b/go.mod @@ -26,6 +26,7 @@ require ( github.com/samwafgo/go-wxsqlite3 v1.0.1 github.com/samwafgo/sqlitedriver v1.0.2 github.com/shirou/gopsutil v3.21.11+incompatible + github.com/shirou/gopsutil/v3 v3.24.5 github.com/spf13/viper v1.18.2 github.com/stretchr/testify v1.10.0 github.com/twmb/franz-go v1.18.0 @@ -86,6 +87,7 @@ require ( github.com/klauspost/compress v1.17.8 // indirect github.com/klauspost/cpuid/v2 v2.2.9 // indirect github.com/leodido/go-urn v1.4.0 // indirect + github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/magefile/mage v1.15.1-0.20231118170541-2385abb49a1f // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mattn/go-isatty v0.0.20 // indirect @@ -101,12 +103,14 @@ require ( github.com/pierrec/lz4/v4 v4.1.21 // indirect github.com/pjbgf/sha1cd v0.3.0 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect github.com/richardlehane/mscfb v1.0.4 // indirect github.com/richardlehane/msoleps v1.0.4 // indirect github.com/robfig/cron/v3 v3.0.1 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/sergi/go-diff v1.1.0 // indirect + github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/skeema/knownhosts v1.2.1 // indirect github.com/sourcegraph/conc v0.3.0 // indirect @@ -129,7 +133,7 @@ require ( github.com/xanzy/ssh-agent v0.3.3 // indirect github.com/xuri/efp v0.0.0-20240408161823-9ad904a10d6d // indirect github.com/xuri/nfp v0.0.0-20240318013403-ab9948c2c4a7 // indirect - github.com/yusufpapurcu/wmi v1.2.3 // indirect + github.com/yusufpapurcu/wmi v1.2.4 // indirect go.mongodb.org/mongo-driver v1.13.1 // indirect go.uber.org/multierr v1.10.0 // indirect golang.org/x/arch v0.12.0 // indirect diff --git a/go.sum b/go.sum index 8812674..cd3eb1d 100644 --- a/go.sum +++ b/go.sum @@ -144,6 +144,7 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= @@ -198,6 +199,8 @@ github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/lionsoul2014/ip2region/binding/golang v0.0.0-20220907060842-b2ba5d58e48d h1:E7Qr3vakQIk9KTpn6m4yhsEhOdo4g51EqE4cSMy2LCw= github.com/lionsoul2014/ip2region/binding/golang v0.0.0-20220907060842-b2ba5d58e48d/go.mod h1:bChUKvbKVC3zL/lLLIcu6alhQaL8uWD/DA+jRdyggdI= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= github.com/magefile/mage v1.15.1-0.20231118170541-2385abb49a1f h1:iiLWLoibjCL0XND6inF7bs2nc20lU/FYkiR//VIOLUc= github.com/magefile/mage v1.15.1-0.20231118170541-2385abb49a1f/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= @@ -238,6 +241,8 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/pquerna/otp v1.4.0 h1:wZvl1TIVxKRThZIBiwOOHOGP/1+nZyWBil9Y2XNEDzg= github.com/pquerna/otp v1.4.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -266,6 +271,12 @@ github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= +github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI= +github.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk= +github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= +github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= +github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= +github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= @@ -347,8 +358,8 @@ github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZ github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= -github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= +github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= go.mongodb.org/mongo-driver v1.13.1 h1:YIc7HTYsKndGK4RFzJ3covLz1byri52x0IoMB0Pt/vk= go.mongodb.org/mongo-driver v1.13.1/go.mod h1:wcDf1JBCXy2mOW0bWHwO/IOYqdca1MPCwDtFu/Z9+eo= go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= @@ -426,6 +437,7 @@ golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/model/response/waf_system_monitor.go b/model/response/waf_system_monitor.go new file mode 100644 index 0000000..6eb6da5 --- /dev/null +++ b/model/response/waf_system_monitor.go @@ -0,0 +1,37 @@ +package response + +// 系统监控响应结构体 +type WafSystemMonitor struct { + CPU WafCPUInfo `json:"cpu"` // CPU信息 + Memory WafMemoryInfo `json:"memory"` // 内存信息 + Disk []WafDiskInfo `json:"disk"` // 磁盘信息 +} + +// CPU信息 +type WafCPUInfo struct { + ModelName string `json:"model_name"` // CPU型号 + Cores int32 `json:"cores"` // 核心数 + UsagePercent float64 `json:"usage_percent"` // 使用率百分比 + PhysicalCnt int `json:"physical_cnt"` // 物理核心数 + LogicalCnt int `json:"logical_cnt"` // 逻辑核心数 +} + +// 内存信息 +type WafMemoryInfo struct { + Total string `json:"total"` // 总内存 + Available string `json:"available"` // 可用内存 + Used string `json:"used"` // 已用内存 + UsagePercent float64 `json:"usage_percent"` // 使用率百分比 + JVMUsed string `json:"jvm_used"` // JVM使用内存(这里用Go程序内存) + JVMPercent float64 `json:"jvm_percent"` // JVM使用率 +} + +// 磁盘信息 +type WafDiskInfo struct { + FileSystem string `json:"file_system"` // 文件系统 + MountPoint string `json:"mount_point"` // 挂载点 + Total string `json:"total"` // 总容量 + Available string `json:"available"` // 可用容量 + Used string `json:"used"` // 已用容量 + UsagePercent float64 `json:"usage_percent"` // 使用率百分比 +} diff --git a/router/entrance.go b/router/entrance.go index f28ef48..bc8f49c 100644 --- a/router/entrance.go +++ b/router/entrance.go @@ -41,6 +41,7 @@ type ApiGroup struct { WafTunnelRouter WafVpConfigRouter WafFileRouter + WafSystemMonitorRouter } type PublicApiGroup struct { LoginRouter diff --git a/router/waf_system_monitor.go b/router/waf_system_monitor.go new file mode 100644 index 0000000..76fe13b --- /dev/null +++ b/router/waf_system_monitor.go @@ -0,0 +1,16 @@ +package router + +import ( + "SamWaf/api" + "github.com/gin-gonic/gin" +) + +type WafSystemMonitorRouter struct { +} + +func (s *WafSystemMonitorRouter) InitWafSystemMonitorRouter(group *gin.RouterGroup) { + wafSystemMonitorApi := api.APIGroupAPP.WafSystemMonitorApi + router := group.Group("") + router.GET("/samwaf/monitor/system_info", wafSystemMonitorApi.GetSystemMonitorApi) // 获取系统监控信息 + +} diff --git a/service/waf_service/waf_system_monitor.go b/service/waf_service/waf_system_monitor.go new file mode 100644 index 0000000..1664c22 --- /dev/null +++ b/service/waf_service/waf_system_monitor.go @@ -0,0 +1,157 @@ +package waf_service + +import ( + "SamWaf/model/response" + "fmt" + "math" + "runtime" + "time" + + "github.com/shirou/gopsutil/v3/cpu" + "github.com/shirou/gopsutil/v3/disk" + "github.com/shirou/gopsutil/v3/mem" +) + +type WafSystemMonitorService struct { +} + +var WafSystemMonitorServiceApp = &WafSystemMonitorService{} + +// GetSystemMonitorInfo 获取系统监控信息 +func (receiver *WafSystemMonitorService) GetSystemMonitorInfo() (response.WafSystemMonitor, error) { + var result response.WafSystemMonitor + + // 获取CPU信息 + cpuInfo, err := receiver.getCPUInfo() + if err != nil { + return result, fmt.Errorf("获取CPU信息失败: %v", err) + } + result.CPU = cpuInfo + + // 获取内存信息 + memInfo, err := receiver.getMemoryInfo() + if err != nil { + return result, fmt.Errorf("获取内存信息失败: %v", err) + } + result.Memory = memInfo + + // 获取磁盘信息 + diskInfo, err := receiver.getDiskInfo() + if err != nil { + return result, fmt.Errorf("获取磁盘信息失败: %v", err) + } + result.Disk = diskInfo + + return result, nil +} + +// getCPUInfo 获取CPU信息 +func (receiver *WafSystemMonitorService) getCPUInfo() (response.WafCPUInfo, error) { + var cpuInfo response.WafCPUInfo + + // 获取CPU基本信息 + cpuInfos, err := cpu.Info() + if err != nil { + return cpuInfo, err + } + + if len(cpuInfos) > 0 { + cpuInfo.ModelName = cpuInfos[0].ModelName + cpuInfo.Cores = cpuInfos[0].Cores + } + + // 获取CPU使用率 + cpuPercent, err := cpu.Percent(time.Second, false) + if err != nil { + return cpuInfo, err + } + if len(cpuPercent) > 0 { + cpuInfo.UsagePercent = receiver.roundToTwoDecimal(cpuPercent[0]) + } + + // 获取CPU核心数 + physicalCnt, _ := cpu.Counts(false) + logicalCnt, _ := cpu.Counts(true) + cpuInfo.PhysicalCnt = physicalCnt + cpuInfo.LogicalCnt = logicalCnt + + return cpuInfo, nil +} + +// getMemoryInfo 获取内存信息 +func (receiver *WafSystemMonitorService) getMemoryInfo() (response.WafMemoryInfo, error) { + var memInfo response.WafMemoryInfo + + // 获取系统内存信息 + vmStat, err := mem.VirtualMemory() + if err != nil { + return memInfo, err + } + + memInfo.Total = receiver.formatBytes(vmStat.Total) + memInfo.Available = receiver.formatBytes(vmStat.Available) + memInfo.Used = receiver.formatBytes(vmStat.Used) + memInfo.UsagePercent = receiver.roundToTwoDecimal(vmStat.UsedPercent) + + // 获取Go程序内存使用情况 + var m runtime.MemStats + runtime.ReadMemStats(&m) + memInfo.JVMUsed = receiver.formatBytes(m.Alloc) + if vmStat.Total > 0 { + jvmPercent := float64(m.Alloc) / float64(vmStat.Total) * 100 + memInfo.JVMPercent = receiver.roundToTwoDecimal(jvmPercent) + } + + return memInfo, nil +} + +// getDiskInfo 获取磁盘信息 +func (receiver *WafSystemMonitorService) getDiskInfo() ([]response.WafDiskInfo, error) { + var diskInfos []response.WafDiskInfo + + // 获取磁盘分区信息 + partitions, err := disk.Partitions(false) + if err != nil { + return diskInfos, err + } + + for _, partition := range partitions { + // 获取磁盘使用情况 + usage, err := disk.Usage(partition.Mountpoint) + if err != nil { + continue // 跳过无法访问的分区 + } + + diskInfo := response.WafDiskInfo{ + FileSystem: partition.Device, + MountPoint: partition.Mountpoint, + Total: receiver.formatBytes(usage.Total), + Available: receiver.formatBytes(usage.Free), + Used: receiver.formatBytes(usage.Used), + UsagePercent: receiver.roundToTwoDecimal(usage.UsedPercent), + } + + diskInfos = append(diskInfos, diskInfo) + } + + return diskInfos, nil +} + +// roundToTwoDecimal 将浮点数四舍五入到两位小数 +func (receiver *WafSystemMonitorService) roundToTwoDecimal(value float64) float64 { + return math.Round(value*100) / 100 +} + +// formatBytes 格式化字节数为可读格式 +func (receiver *WafSystemMonitorService) formatBytes(bytes uint64) string { + const unit = 1024 + if bytes < unit { + return fmt.Sprintf("%d B", bytes) + } + div, exp := int64(unit), 0 + for n := bytes / unit; n >= unit; n /= unit { + div *= unit + exp++ + } + return fmt.Sprintf("%.1f %cB", float64(bytes)/float64(div), "KMGTPE"[exp]) +} diff --git a/wafmangeweb/localserver.go b/wafmangeweb/localserver.go index 2230d9a..bc3adff 100644 --- a/wafmangeweb/localserver.go +++ b/wafmangeweb/localserver.go @@ -75,6 +75,7 @@ func (web *WafWebManager) initRouter(r *gin.Engine) { router.ApiGroupApp.InitWafTunnelRouter(RouterGroup) router.ApiGroupApp.InitWafVpConfigRouter(RouterGroup) router.ApiGroupApp.InitWafFileRouter(RouterGroup) + router.ApiGroupApp.InitWafSystemMonitorRouter(RouterGroup) } if global.GWAF_RELEASE == "true" {