package metrics import ( "bufio" "fmt" "os" "path/filepath" "strconv" "strings" "zlh-agent/internal/state" "zlh-agent/internal/system" ) type ProcessSection struct { PID int `json:"pid"` Status string `json:"status"` UptimeSeconds *int64 `json:"uptime_seconds"` RestartCount int `json:"restart_count"` } type MemorySection struct { RSSBytes int64 `json:"rss_bytes"` VMSBytes int64 `json:"vms_bytes"` } type ProcessMetricsResponse struct { Process ProcessSection `json:"process"` Memory MemorySection `json:"memory"` } func ProcessMetrics() (ProcessMetricsResponse, bool, error) { pid, ok := system.GetServerPID() if !ok { return ProcessMetricsResponse{ Process: ProcessSection{ PID: 0, Status: "stopped", RestartCount: state.GetCrashCount(), }, }, true, nil } rss, vms, err := readMemoryFromStatus(pid) if err != nil { return ProcessMetricsResponse{}, false, err } return ProcessMetricsResponse{ Process: ProcessSection{ PID: pid, Status: "running", UptimeSeconds: nil, RestartCount: state.GetCrashCount(), }, Memory: MemorySection{ RSSBytes: rss, VMSBytes: vms, }, }, false, nil } func readMemoryFromStatus(pid int) (int64, int64, error) { statusPath := filepath.Join("/proc", strconv.Itoa(pid), "status") f, err := os.Open(statusPath) if err != nil { return 0, 0, err } defer f.Close() var rssKB int64 var vmsKB int64 s := bufio.NewScanner(f) for s.Scan() { line := s.Text() if strings.HasPrefix(line, "VmRSS:") { rssKB = parseKBLine(line) } if strings.HasPrefix(line, "VmSize:") { vmsKB = parseKBLine(line) } } if err := s.Err(); err != nil { return 0, 0, err } if rssKB == 0 && vmsKB == 0 { return 0, 0, fmt.Errorf("missing VmRSS/VmSize in %s", statusPath) } return rssKB * 1024, vmsKB * 1024, nil } func parseKBLine(line string) int64 { fields := strings.Fields(line) if len(fields) < 2 { return 0 } n, err := strconv.ParseInt(fields[1], 10, 64) if err != nil { return 0 } return n }