From 9a73095a8f71e2eb51bf41475c209782c3d1a35c Mon Sep 17 00:00:00 2001 From: ssongliu <73214554+ssongliu@users.noreply.github.com> Date: Thu, 30 Oct 2025 16:48:18 +0800 Subject: [PATCH] feat: System monitoring supports process viewing (#10821) Refs #3437 --- agent/app/dto/monitor.go | 9 + agent/app/model/monitor.go | 10 +- agent/app/service/monitor.go | 131 ++++++- agent/init/migration/migrate.go | 1 + agent/init/migration/migrations/init.go | 7 + frontend/src/lang/modules/en.ts | 4 +- frontend/src/lang/modules/es-es.ts | 4 +- frontend/src/lang/modules/ja.ts | 6 +- frontend/src/lang/modules/ko.ts | 4 + frontend/src/lang/modules/ms.ts | 4 + frontend/src/lang/modules/pt-br.ts | 6 +- frontend/src/lang/modules/ru.ts | 4 + frontend/src/lang/modules/tr.ts | 4 +- frontend/src/lang/modules/zh-Hant.ts | 2 + frontend/src/lang/modules/zh.ts | 2 + .../src/views/host/monitor/monitor/index.vue | 319 ++++++++++++------ 16 files changed, 404 insertions(+), 113 deletions(-) diff --git a/agent/app/dto/monitor.go b/agent/app/dto/monitor.go index de6ca6ce3..9fbf9d9a2 100644 --- a/agent/app/dto/monitor.go +++ b/agent/app/dto/monitor.go @@ -15,6 +15,15 @@ type MonitorData struct { Value []interface{} `json:"value"` } +type Process struct { + Name string `json:"name"` + Pid int32 `json:"pid"` + Percent float64 `json:"percent"` + Memory uint64 `json:"memory"` + Cmd string `json:"cmd"` + User string `json:"user"` +} + type MonitorSetting struct { MonitorStatus string `json:"monitorStatus"` MonitorStoreDays string `json:"monitorStoreDays"` diff --git a/agent/app/model/monitor.go b/agent/app/model/monitor.go index a53392e43..5a2c594b5 100644 --- a/agent/app/model/monitor.go +++ b/agent/app/model/monitor.go @@ -2,14 +2,18 @@ package model type MonitorBase struct { BaseModel - Cpu float64 `json:"cpu"` + Cpu float64 `json:"cpu"` + TopCPU string `json:"topCPU"` + TopCPUItems interface{} `gorm:"-" json:"topCPUItems"` + + Memory float64 `json:"memory"` + TopMem string `json:"topMem"` + TopMemItems interface{} `gorm:"-" json:"topMemItems"` LoadUsage float64 `json:"loadUsage"` CpuLoad1 float64 `json:"cpuLoad1"` CpuLoad5 float64 `json:"cpuLoad5"` CpuLoad15 float64 `json:"cpuLoad15"` - - Memory float64 `json:"memory"` } type MonitorIO struct { diff --git a/agent/app/service/monitor.go b/agent/app/service/monitor.go index 2a93ed27e..ca96d7bad 100644 --- a/agent/app/service/monitor.go +++ b/agent/app/service/monitor.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "fmt" + "sort" "strconv" "time" @@ -21,6 +22,7 @@ import ( "github.com/shirou/gopsutil/v4/load" "github.com/shirou/gopsutil/v4/mem" "github.com/shirou/gopsutil/v4/net" + "github.com/shirou/gopsutil/v4/process" ) type MonitorService struct { @@ -64,6 +66,18 @@ func (m *MonitorService) LoadMonitorData(req dto.MonitorSearch) ([]dto.MonitorDa itemData.Param = "base" for _, base := range bases { itemData.Date = append(itemData.Date, base.CreatedAt) + if req.Param == "all" || req.Param == "cpu" { + var processes []dto.Process + _ = json.Unmarshal([]byte(base.TopCPU), &processes) + base.TopCPUItems = processes + base.TopCPU = "" + } + if req.Param == "all" || req.Param == "mem" { + var processes []dto.Process + _ = json.Unmarshal([]byte(base.TopMem), &processes) + base.TopMemItems = processes + base.TopMem = "" + } itemData.Value = append(itemData.Value, base) } data = append(data, itemData) @@ -169,8 +183,14 @@ func (m *MonitorService) Run() { if len(totalPercent) == 1 { itemModel.Cpu = totalPercent[0] } + topCPU := m.loadTopCPU() + if len(topCPU) != 0 { + topItemCPU, err := json.Marshal(topCPU) + if err == nil { + itemModel.TopCPU = string(topItemCPU) + } + } cpuCount, _ := cpu.Counts(false) - loadInfo, _ := load.Avg() itemModel.CpuLoad1 = loadInfo.Load1 itemModel.CpuLoad5 = loadInfo.Load5 @@ -179,6 +199,13 @@ func (m *MonitorService) Run() { memoryInfo, _ := mem.VirtualMemory() itemModel.Memory = memoryInfo.UsedPercent + topMem := m.loadTopMem() + if len(topMem) != 0 { + topMemItem, err := json.Marshal(topMem) + if err == nil { + itemModel.TopMem = string(topMemItem) + } + } if err := settingRepo.CreateMonitorBase(itemModel); err != nil { global.LOG.Errorf("Insert basic monitoring data failed, err: %v", err) @@ -323,6 +350,108 @@ func (m *MonitorService) saveNetDataToDB(ctx context.Context, interval float64) } } +func (m *MonitorService) loadTopCPU() []dto.Process { + processes, err := process.Processes() + if err != nil { + return nil + } + + top5 := make([]dto.Process, 0, 5) + for _, p := range processes { + percent, err := p.CPUPercent() + if err != nil { + continue + } + minIndex := 0 + if len(top5) >= 5 { + minCPU := top5[0].Percent + for i := 1; i < len(top5); i++ { + if top5[i].Percent < minCPU { + minCPU = top5[i].Percent + minIndex = i + } + } + if percent < minCPU { + continue + } + } + name, err := p.Name() + if err != nil { + name = "undifine" + } + cmd, err := p.Cmdline() + if err != nil { + cmd = "undifine" + } + user, err := p.Username() + if err != nil { + user = "undifine" + } + if len(top5) == 5 { + top5[minIndex] = dto.Process{Percent: percent, Pid: p.Pid, User: user, Name: name, Cmd: cmd} + } else { + top5 = append(top5, dto.Process{Percent: percent, Pid: p.Pid, User: user, Name: name, Cmd: cmd}) + } + } + sort.Slice(top5, func(i, j int) bool { + return top5[i].Percent > top5[j].Percent + }) + + return top5 +} + +func (m *MonitorService) loadTopMem() []dto.Process { + processes, err := process.Processes() + if err != nil { + return nil + } + + top5 := make([]dto.Process, 0, 5) + for _, p := range processes { + stat, err := p.MemoryInfo() + if err != nil { + continue + } + memItem := stat.RSS + minIndex := 0 + if len(top5) >= 5 { + min := top5[0].Memory + for i := 1; i < len(top5); i++ { + if top5[i].Memory < min { + min = top5[i].Memory + minIndex = i + } + } + if memItem < min { + continue + } + } + name, err := p.Name() + if err != nil { + name = "undifine" + } + cmd, err := p.Cmdline() + if err != nil { + cmd = "undifine" + } + user, err := p.Username() + if err != nil { + user = "undifine" + } + percent, _ := p.MemoryPercent() + if len(top5) == 5 { + top5[minIndex] = dto.Process{Percent: float64(percent), Pid: p.Pid, User: user, Name: name, Cmd: cmd, Memory: memItem} + } else { + top5 = append(top5, dto.Process{Percent: float64(percent), Pid: p.Pid, User: user, Name: name, Cmd: cmd, Memory: memItem}) + } + } + + sort.Slice(top5, func(i, j int) bool { + return top5[i].Memory > top5[j].Memory + }) + return top5 +} + func StartMonitor(removeBefore bool, interval string) error { if removeBefore { monitorCancel() diff --git a/agent/init/migration/migrate.go b/agent/init/migration/migrate.go index a4a3ed2e2..2c3dba628 100644 --- a/agent/init/migration/migrate.go +++ b/agent/init/migration/migrate.go @@ -48,6 +48,7 @@ func InitAgentDB() { migrations.UpdateWebsiteSSLAddColumn, migrations.AddTensorRTLLMModel, migrations.UpdateMonitorInterval, + migrations.AddMonitorProcess, }) if err := m.Migrate(); err != nil { global.LOG.Error(err) diff --git a/agent/init/migration/migrations/init.go b/agent/init/migration/migrations/init.go index 3d178f77a..9790d6601 100644 --- a/agent/init/migration/migrations/init.go +++ b/agent/init/migration/migrations/init.go @@ -659,3 +659,10 @@ var UpdateMonitorInterval = &gormigrate.Migration{ return nil }, } + +var AddMonitorProcess = &gormigrate.Migration{ + ID: "20251030-add-monitor-process", + Migrate: func(tx *gorm.DB) error { + return global.MonitorDB.AutoMigrate(&model.MonitorBase{}) + }, +} diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts index 168bb18e0..43585e463 100644 --- a/frontend/src/lang/modules/en.ts +++ b/frontend/src/lang/modules/en.ts @@ -104,6 +104,7 @@ const message = { timeRange: 'To', dateStart: 'Date start', dateEnd: 'Date end', + date: 'Date', }, table: { all: 'All', @@ -1193,10 +1194,11 @@ const message = { readWriteTime: 'I/O latency', today: 'Today', yesterday: 'Yesterday', - lastNDay: 'Last {0} day(s)', + lastNDay: 'Last {0} days', lastNMonth: 'Last {0} months', lastHalfYear: 'Last half year', memory: 'Memory', + percent: 'Percentage', cache: 'Cache', disk: 'Disk', network: 'Network', diff --git a/frontend/src/lang/modules/es-es.ts b/frontend/src/lang/modules/es-es.ts index 3f9392534..38543c244 100644 --- a/frontend/src/lang/modules/es-es.ts +++ b/frontend/src/lang/modules/es-es.ts @@ -103,6 +103,7 @@ const message = { timeRange: 'Hasta', dateStart: 'Fecha de inicio', dateEnd: 'Fecha de fin', + date: 'Fecha', }, table: { all: 'Todo', @@ -1200,8 +1201,9 @@ const message = { yesterday: 'Ayer', lastNDay: 'Últimos {0} días', lastNMonth: 'Últimos {0} meses', - lastHalfYear: 'Últimos seis meses', + lastHalfYear: 'Último semestre', memory: 'Memoria', + percent: 'Porcentaje', cache: 'Caché', disk: 'Disco', network: 'Red', diff --git a/frontend/src/lang/modules/ja.ts b/frontend/src/lang/modules/ja.ts index eb9785727..d452acba7 100644 --- a/frontend/src/lang/modules/ja.ts +++ b/frontend/src/lang/modules/ja.ts @@ -101,6 +101,7 @@ const message = { timeRange: 'に', dateStart: '日付開始', dateEnd: '日付の終わり', + date: '日付', }, table: { all: '全て', @@ -1157,8 +1158,11 @@ const message = { readWriteTime: 'I/Oレイテンシ', today: '今日', yesterday: '昨日', - lastNDay: '最後の{0}日', + lastNDay: '過去 {0} 日間', + lastNMonth: '過去 {0} ヶ月間', + lastHalfYear: '過去半年間', memory: 'メモリ', + percent: '割合', cache: 'キャッシュ', disk: 'ディスク', network: 'ネットワーク', diff --git a/frontend/src/lang/modules/ko.ts b/frontend/src/lang/modules/ko.ts index 52a902589..2efa5f564 100644 --- a/frontend/src/lang/modules/ko.ts +++ b/frontend/src/lang/modules/ko.ts @@ -101,6 +101,7 @@ const message = { timeRange: '부터', dateStart: '시작 날짜', dateEnd: '종료 날짜', + date: '날짜', }, table: { all: '전체', @@ -1150,7 +1151,10 @@ const message = { today: '오늘', yesterday: '어제', lastNDay: '최근 {0}일', + lastNMonth: '최근 {0}개월', + lastHalfYear: '최근 반년', memory: '메모리', + percent: '비율', cache: '캐시', disk: '디스크', network: '네트워크', diff --git a/frontend/src/lang/modules/ms.ts b/frontend/src/lang/modules/ms.ts index 16b925f96..1f2cb4f4c 100644 --- a/frontend/src/lang/modules/ms.ts +++ b/frontend/src/lang/modules/ms.ts @@ -101,6 +101,7 @@ const message = { timeRange: 'Hingga', dateStart: 'Tarikh mula', dateEnd: 'Tarikh tamat', + date: 'Tarikh', }, table: { all: 'Semua', @@ -1190,7 +1191,10 @@ const message = { today: 'Hari ini', yesterday: 'Semalam', lastNDay: '{0} hari terakhir', + lastNMonth: '{0} bulan terakhir', + lastHalfYear: 'Setengah tahun terakhir', memory: 'Memori', + percent: 'Peratusan', cache: 'Cache', disk: 'Cakera', network: 'Rangkaian', diff --git a/frontend/src/lang/modules/pt-br.ts b/frontend/src/lang/modules/pt-br.ts index d722e86c3..b90796c96 100644 --- a/frontend/src/lang/modules/pt-br.ts +++ b/frontend/src/lang/modules/pt-br.ts @@ -101,6 +101,7 @@ const message = { timeRange: 'Até', dateStart: 'Data inicial', dateEnd: 'Data final', + date: 'Data', }, table: { all: 'Todos', @@ -1181,8 +1182,11 @@ const message = { readWriteTime: 'Latência de I/O', today: 'Hoje', yesterday: 'Ontem', - lastNDay: 'Últimos {0} dia(s)', + lastNDay: 'Últimos {0} dias', + lastNMonth: 'Últimos {0} meses', + lastHalfYear: 'Último semestre', memory: 'Memória', + percent: 'Percentual', cache: 'Cache', disk: 'Disco', network: 'Rede', diff --git a/frontend/src/lang/modules/ru.ts b/frontend/src/lang/modules/ru.ts index 58d94fae1..858fc1bd0 100644 --- a/frontend/src/lang/modules/ru.ts +++ b/frontend/src/lang/modules/ru.ts @@ -101,6 +101,7 @@ const message = { timeRange: 'До', dateStart: 'Дата начала', dateEnd: 'Дата окончания', + date: 'Дата', }, table: { all: 'Все', @@ -1186,7 +1187,10 @@ const message = { today: 'Сегодня', yesterday: 'Вчера', lastNDay: 'Последние {0} дней', + lastNMonth: 'Последние {0} месяцев', + lastHalfYear: 'Последние полгода', memory: 'Память', + percent: 'Процент', cache: 'Кэш', disk: 'Диск', network: 'Сеть', diff --git a/frontend/src/lang/modules/tr.ts b/frontend/src/lang/modules/tr.ts index 97d998694..74f35fea8 100644 --- a/frontend/src/lang/modules/tr.ts +++ b/frontend/src/lang/modules/tr.ts @@ -104,6 +104,7 @@ const message = { timeRange: 'İle', dateStart: 'Başlangıç tarihi', dateEnd: 'Bitiş tarihi', + date: 'Tarih', }, table: { all: 'Tümü', @@ -1209,8 +1210,9 @@ const message = { yesterday: 'Dün', lastNDay: 'Son {0} gün', lastNMonth: 'Son {0} ay', - lastHalfYear: 'Son yarı yıl', + lastHalfYear: 'Son yarım yıl', memory: 'Bellek', + percent: 'Yüzde', cache: 'Önbellek', disk: 'Disk', network: 'Ağ', diff --git a/frontend/src/lang/modules/zh-Hant.ts b/frontend/src/lang/modules/zh-Hant.ts index 42c7aba00..18d96cd96 100644 --- a/frontend/src/lang/modules/zh-Hant.ts +++ b/frontend/src/lang/modules/zh-Hant.ts @@ -104,6 +104,7 @@ const message = { timeRange: '至', dateStart: '開始日期', dateEnd: '結束日期', + date: '日期', }, table: { all: '所有', @@ -1134,6 +1135,7 @@ const message = { lastNMonth: '近 {0} 月', lastHalfYear: '近半年', memory: '記憶體', + percent: '佔比', cache: '快取', disk: '磁碟', network: '網路', diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index 3f1948127..0698863fc 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -104,6 +104,7 @@ const message = { timeRange: '至', dateStart: '开始日期', dateEnd: '结束日期', + date: '日期', }, table: { all: '所有', @@ -1136,6 +1137,7 @@ const message = { lastNMonth: '近 {0} 月', lastHalfYear: '近半年', memory: '内存', + percent: '占比', cache: '缓存', disk: '磁盘', network: '网络', diff --git a/frontend/src/views/host/monitor/monitor/index.vue b/frontend/src/views/host/monitor/monitor/index.vue index 2283395da..273784a0a 100644 --- a/frontend/src/views/host/monitor/monitor/index.vue +++ b/frontend/src/views/host/monitor/monitor/index.vue @@ -220,7 +220,7 @@