package main import ( "encoding/json" "fmt" "io" "log" "net/http" "os" "os/exec" "path/filepath" "strings" "time" ) // ProcessConfig 进程配置结构体 type ProcessConfig struct { ProcessName string `json:"process_name"` FileName string `json:"file_name"` // 文件名 KumaURL string `json:"kuma_url"` CheckInterval int `json:"check_interval"` } // Config 整体配置结构体 type Config struct { Processes []ProcessConfig `json:"processes"` } // ProcessMonitor 进程监控结构体 type ProcessMonitor struct { config ProcessConfig httpClient *http.Client status string logDir string } // NewProcessMonitor 创建新的进程监控 func NewProcessMonitor(config ProcessConfig, logDir string) *ProcessMonitor { return &ProcessMonitor{ config: config, httpClient: &http.Client{}, status: "down", logDir: logDir, } } // LoadConfig 加载配置文件 func LoadConfig(filename string) (*Config, error) { file, err := os.Open(filename) if err != nil { return nil, err } defer file.Close() var config Config if err := json.NewDecoder(file).Decode(&config); err != nil { return nil, err } return &config, nil } // InitLogFile 初始化日志文件 func InitLogFile(logDir string) (*os.File, error) { if err := os.MkdirAll(logDir, 0755); err != nil { return nil, fmt.Errorf("failed to create log directory: %v", err) } logFile := filepath.Join(logDir, time.Now().Format("2006-01-02")+".log") file, err := os.OpenFile(logFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666) if err != nil { return nil, fmt.Errorf("failed to open log file: %v", err) } return file, nil } // IsProcessRunning 检查进程是否正在运行 func (pm *ProcessMonitor) IsProcessRunning() bool { output, err := exec.Command("tasklist").Output() if err != nil { log.Printf("Error retrieving process list: %v", err) return false } lines := string(output) for _, line := range strings.Split(lines, "\n") { if strings.Contains(line, pm.config.ProcessName) { // 如果指定了文件名,则检查文件名 if pm.config.FileName == "" || strings.Contains(line, pm.config.FileName) { return true } } } return false } // PushStatusToKuma 向 Uptime Kuma 推送状态 func (pm *ProcessMonitor) PushStatusToKuma() error { response, err := pm.httpClient.Get(fmt.Sprintf("%s?status=%s", pm.config.KumaURL, pm.status)) if err != nil { return err } defer response.Body.Close() log.Printf("Pushed status '%s' for process '%s' fileName '%s' to Uptime Kuma: %s", pm.status, pm.config.ProcessName, pm.config.FileName, response.Status) return nil } // Monitor 监控进程状态 func (pm *ProcessMonitor) Monitor() { ticker := time.NewTicker(time.Duration(pm.config.CheckInterval) * time.Second) defer ticker.Stop() pm.checkAndPushStatus() // 启动时立即检查一次 for { select { case <-ticker.C: pm.checkAndPushStatus() case <-time.After(24 * time.Hour): // 每天执行日志清理 pm.cleanOldLogs() } } } // checkAndPushStatus 检查进程状态并推送到 Uptime Kuma func (pm *ProcessMonitor) checkAndPushStatus() { pm.status = "down" if pm.IsProcessRunning() { pm.status = "up" } if err := pm.PushStatusToKuma(); err != nil { log.Printf("Error pushing status for process '%s' fileName '%s': %v", pm.config.ProcessName, pm.config.FileName, err) } } // cleanOldLogs 清理旧日志 func (pm *ProcessMonitor) cleanOldLogs() { logFiles, err := os.ReadDir(pm.logDir) if err != nil { log.Printf("Error reading log directory: %v", err) return } threshold := time.Now().AddDate(0, 0, -30) // 30天前的日期 for _, logFile := range logFiles { if logFile.Type().IsRegular() { if fileInfo, err := logFile.Info(); err == nil { if fileInfo.ModTime().Before(threshold) { if err := os.Remove(filepath.Join(pm.logDir, logFile.Name())); err != nil { log.Printf("Error deleting old log file '%s': %v", logFile.Name(), err) } else { log.Printf("Deleted old log file: '%s'", logFile.Name()) } } } } } } func main() { logDir := "logs" logFile, err := InitLogFile(logDir) if err != nil { log.Fatalf("Error initializing log file: %v\n", err) } defer logFile.Close() // 设置日志同时输出到控制台和文件 multiWriter := io.MultiWriter(logFile, os.Stdout) log.SetOutput(multiWriter) config, err := LoadConfig("config.json") if err != nil { log.Fatalf("Error loading config: %v", err) } // 启动监控 for _, processConfig := range config.Processes { pm := NewProcessMonitor(processConfig, logDir) go pm.Monitor() } select {} // 阻止主进程退出 }