185 lines
4.6 KiB
Go
185 lines
4.6 KiB
Go
|
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 {} // 阻止主进程退出
|
||
|
}
|