commit 685abe69ca5902fdb8499b6f044658e450a75e8f Author: tinkyo Date: Sat Nov 16 16:27:05 2024 +0800 init diff --git a/config.json b/config.json new file mode 100644 index 0000000..6e3b37d --- /dev/null +++ b/config.json @@ -0,0 +1,17 @@ +{ + "processes": [ + { + "process_name": "chrome", + "file_name": "", + "kuma_url": "http://192.168.123.10:3001/api/push/AkN14NLK1G?status=up&msg=OK&ping=", + "check_interval": 60 + }, + { + "process_name": "process-monitor", + "file_name": "process-monitor.exe", + "kuma_url": "http://192.168.123.10:3001/api/push/AkN14NLK1G?status=up&msg=OK&ping=", + "check_interval": 60 + } + ] + } + \ No newline at end of file diff --git a/main.go b/main.go new file mode 100644 index 0000000..ae3e7e1 --- /dev/null +++ b/main.go @@ -0,0 +1,184 @@ +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 {} // 阻止主进程退出 +}