|
|
@@ -17,6 +17,7 @@ import (
|
|
|
"AI-Status-Light/internal/database"
|
|
|
"AI-Status-Light/internal/discovery"
|
|
|
"AI-Status-Light/internal/event"
|
|
|
+ "AI-Status-Light/internal/logger"
|
|
|
"AI-Status-Light/internal/monitor"
|
|
|
mqttcli "AI-Status-Light/internal/mqtt"
|
|
|
)
|
|
|
@@ -62,16 +63,29 @@ func runServe(args []string) {
|
|
|
fs := flag.NewFlagSet("serve", flag.ExitOnError)
|
|
|
addr := fs.String("addr", ":8080", "监听地址")
|
|
|
dbPath := fs.String("db", defaultDBPath, "数据库路径")
|
|
|
+ logFile := fs.String("log-file", "./logs", "日志文件路径(默认 ./logs/monitor.log)")
|
|
|
+ logLevel := fs.String("log-level", "info", "日志级别 (debug/info/warn/error)")
|
|
|
fs.Parse(args)
|
|
|
|
|
|
+ logger.SetLevel(logger.ParseLevel(*logLevel))
|
|
|
+ if err := logger.InitFileLog(*logFile); err != nil {
|
|
|
+ fmt.Printf("初始化日志文件失败: %v\n", err)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ defer logger.Close()
|
|
|
+
|
|
|
db, err := database.New(*dbPath)
|
|
|
if err != nil {
|
|
|
+ logger.Error("打开数据库失败: %v", err)
|
|
|
fmt.Printf("打开数据库失败: %v\n", err)
|
|
|
return
|
|
|
}
|
|
|
defer db.Close()
|
|
|
+ logger.Info("数据库已连接: %s", *dbPath)
|
|
|
|
|
|
server := api.New(db, *addr)
|
|
|
+ logger.Info("API 服务已启动: %s", *addr)
|
|
|
+
|
|
|
fmt.Printf("API 服务已启动: %s\n", *addr)
|
|
|
fmt.Println("接口文档:")
|
|
|
fmt.Println(" GET /api/health - 健康检查")
|
|
|
@@ -82,6 +96,7 @@ func runServe(args []string) {
|
|
|
fmt.Println(" DELETE /api/mqtt/:id - 删除配置")
|
|
|
|
|
|
if err := server.Start(); err != nil {
|
|
|
+ logger.Error("服务启动失败: %v", err)
|
|
|
fmt.Printf("服务启动失败: %v\n", err)
|
|
|
}
|
|
|
}
|
|
|
@@ -94,8 +109,17 @@ func runMonitor(args []string) {
|
|
|
intervalFlag := fs.Int("interval", 5, "动态扫描间隔(秒), 默认5")
|
|
|
dbPath := fs.String("db", defaultDBPath, "数据库路径")
|
|
|
apiAddr := fs.String("api-addr", "", "API 服务地址 (如: :8080)")
|
|
|
+ logFile := fs.String("log-file", "./logs", "日志文件路径(默认 ./logs/monitor.log)")
|
|
|
+ logLevel := fs.String("log-level", "info", "日志级别 (debug/info/warn/error)")
|
|
|
fs.Parse(args)
|
|
|
|
|
|
+ logger.SetLevel(logger.ParseLevel(*logLevel))
|
|
|
+ if err := logger.InitFileLog(*logFile); err != nil {
|
|
|
+ fmt.Printf("初始化日志文件失败: %v\n", err)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ defer logger.Close()
|
|
|
+
|
|
|
var scanRange *[2]int
|
|
|
if *scanFlag != "" {
|
|
|
parts := strings.Split(*scanFlag, "-")
|
|
|
@@ -116,32 +140,41 @@ func runMonitor(args []string) {
|
|
|
|
|
|
db, err := database.New(*dbPath)
|
|
|
if err != nil {
|
|
|
+ logger.Error("打开数据库失败: %v", err)
|
|
|
fmt.Printf("打开数据库失败: %v\n", err)
|
|
|
return
|
|
|
}
|
|
|
defer db.Close()
|
|
|
+ logger.Info("数据库已连接: %s", *dbPath)
|
|
|
|
|
|
var mqttClient *mqttcli.Client
|
|
|
cfg, err := db.GetMQTTConfig()
|
|
|
if err != nil {
|
|
|
+ logger.Error("读取 MQTT 配置失败: %v", err)
|
|
|
fmt.Printf("读取 MQTT 配置失败: %v\n", err)
|
|
|
} else if cfg != nil {
|
|
|
mqttClient = mqttcli.NewFromConfig(cfg)
|
|
|
if err := mqttClient.Connect(); err != nil {
|
|
|
+ logger.Error("MQTT 连接失败: %v", err)
|
|
|
fmt.Printf("MQTT 连接失败: %v\n", err)
|
|
|
mqttClient = nil
|
|
|
} else {
|
|
|
defer mqttClient.Disconnect()
|
|
|
+ logger.Info("MQTT 已连接: %s (主题: %s)", cfg.Broker, cfg.Topic)
|
|
|
fmt.Printf("MQTT 已连接: %s (主题: %s)\n", cfg.Broker, cfg.Topic)
|
|
|
}
|
|
|
+ } else {
|
|
|
+ logger.Info("未配置 MQTT,跳过 MQTT 连接")
|
|
|
}
|
|
|
|
|
|
var apiServer *api.Server
|
|
|
if *apiAddr != "" {
|
|
|
apiServer = api.New(db, *apiAddr)
|
|
|
go func() {
|
|
|
+ logger.Info("API 服务已启动: %s", *apiAddr)
|
|
|
fmt.Printf("API 服务已启动: %s\n", *apiAddr)
|
|
|
if err := apiServer.Start(); err != nil {
|
|
|
+ logger.Error("API 服务失败: %v", err)
|
|
|
fmt.Printf("API 服务失败: %v\n", err)
|
|
|
}
|
|
|
}()
|
|
|
@@ -172,24 +205,53 @@ func runConfig(args []string) {
|
|
|
}
|
|
|
|
|
|
dbPath := defaultDBPath
|
|
|
+ logFile := "./logs"
|
|
|
+ logLevel := "info"
|
|
|
for i, arg := range args {
|
|
|
if arg == "--db" && i+1 < len(args) {
|
|
|
dbPath = args[i+1]
|
|
|
- break
|
|
|
+ }
|
|
|
+ if arg == "--log-file" && i+1 < len(args) {
|
|
|
+ logFile = args[i+1]
|
|
|
+ }
|
|
|
+ if arg == "--log-level" && i+1 < len(args) {
|
|
|
+ logLevel = args[i+1]
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ logger.SetLevel(logger.ParseLevel(logLevel))
|
|
|
+ if err := logger.InitFileLog(logFile); err != nil {
|
|
|
+ fmt.Printf("初始化日志文件失败: %v\n", err)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ defer logger.Close()
|
|
|
+
|
|
|
+ // 过滤全局选项,避免传递给子命令的 FlagSet
|
|
|
+ var filtered []string
|
|
|
+ for i := 0; i < len(args); i++ {
|
|
|
+ switch args[i] {
|
|
|
+ case "--db", "--log-file", "--log-level":
|
|
|
+ i++ // 跳过值
|
|
|
+ default:
|
|
|
+ filtered = append(filtered, args[i])
|
|
|
}
|
|
|
}
|
|
|
+ args = filtered
|
|
|
|
|
|
db, err := database.New(dbPath)
|
|
|
if err != nil {
|
|
|
+ logger.Error("打开数据库失败: %v", err)
|
|
|
fmt.Printf("打开数据库失败: %v\n", err)
|
|
|
return
|
|
|
}
|
|
|
defer db.Close()
|
|
|
+ logger.Info("数据库已连接: %s", dbPath)
|
|
|
|
|
|
switch args[0] {
|
|
|
case "list":
|
|
|
configs, err := db.ListMQTTConfigs()
|
|
|
if err != nil {
|
|
|
+ logger.Error("查询配置失败: %v", err)
|
|
|
fmt.Printf("查询失败: %v\n", err)
|
|
|
return
|
|
|
}
|
|
|
@@ -197,6 +259,7 @@ func runConfig(args []string) {
|
|
|
fmt.Println("未配置 MQTT")
|
|
|
return
|
|
|
}
|
|
|
+ logger.Info("查询到 %d 条 MQTT 配置", len(configs))
|
|
|
for _, cfg := range configs {
|
|
|
status := "禁用"
|
|
|
if cfg.Enabled {
|
|
|
@@ -234,9 +297,11 @@ func runConfig(args []string) {
|
|
|
}
|
|
|
|
|
|
if err := db.SaveMQTTConfig(cfg); err != nil {
|
|
|
+ logger.Error("保存 MQTT 配置失败: %v", err)
|
|
|
fmt.Printf("保存失败: %v\n", err)
|
|
|
return
|
|
|
}
|
|
|
+ logger.Info("MQTT 配置已保存: %s (主题: %s)", cfg.Broker, cfg.Topic)
|
|
|
fmt.Println("配置已保存")
|
|
|
|
|
|
case "delete":
|
|
|
@@ -246,13 +311,16 @@ func runConfig(args []string) {
|
|
|
}
|
|
|
id, err := strconv.Atoi(args[1])
|
|
|
if err != nil {
|
|
|
+ logger.Warn("无效的配置 ID: %s", args[1])
|
|
|
fmt.Println("无效的 ID")
|
|
|
return
|
|
|
}
|
|
|
if err := db.DeleteMQTTConfig(id); err != nil {
|
|
|
+ logger.Error("删除配置失败: id=%d, %v", id, err)
|
|
|
fmt.Printf("删除失败: %v\n", err)
|
|
|
return
|
|
|
}
|
|
|
+ logger.Info("MQTT 配置已删除: id=%d", id)
|
|
|
fmt.Println("配置已删除")
|
|
|
|
|
|
default:
|
|
|
@@ -291,6 +359,7 @@ func createCallback(mqttClient *mqttcli.Client, apiServer *api.Server) monitor.E
|
|
|
"timestamp": time.Now().Format(time.RFC3339),
|
|
|
}
|
|
|
if err := mqttClient.PublishRaw(mqttClient.GetTopic(), payload); err != nil {
|
|
|
+ logger.Error("MQTT 发送失败: %v", err)
|
|
|
fmt.Printf("MQTT 发送失败: %v\n", err)
|
|
|
}
|
|
|
}
|
|
|
@@ -376,6 +445,7 @@ func runFixedMode(ctx context.Context, host string, portsFlag string, callback m
|
|
|
return
|
|
|
}
|
|
|
|
|
|
+ logger.Info("固定模式启动,监控端口: %v", ports)
|
|
|
fmt.Printf("监控端口: %v\n", ports)
|
|
|
fmt.Println("Ctrl+C 停止")
|
|
|
fmt.Println(strings.Repeat("-", 40))
|
|
|
@@ -385,14 +455,18 @@ func runFixedMode(ctx context.Context, host string, portsFlag string, callback m
|
|
|
wg.Add(1)
|
|
|
go func(p int) {
|
|
|
defer wg.Done()
|
|
|
+ logger.Info("开始监控端口: %d", p)
|
|
|
m := monitor.New(host, p, callback)
|
|
|
m.Run(ctx)
|
|
|
+ logger.Info("端口 %d 监控已停止", p)
|
|
|
}(port)
|
|
|
}
|
|
|
|
|
|
<-sigChan
|
|
|
+ logger.Info("收到停止信号,正在退出")
|
|
|
fmt.Println("\n已停止")
|
|
|
wg.Wait()
|
|
|
+ logger.Info("所有监控协程已退出")
|
|
|
}
|
|
|
|
|
|
func runDynamicMode(ctx context.Context, host string, scanRange *[2]int, interval int, callback monitor.EventCallback, sigChan chan os.Signal) {
|
|
|
@@ -413,6 +487,7 @@ func runDynamicMode(ctx context.Context, host string, scanRange *[2]int, interva
|
|
|
mu.Unlock()
|
|
|
|
|
|
go func() {
|
|
|
+ logger.Info("开始监控端口: %d", port)
|
|
|
fmt.Printf("开始监控端口: %d\n", port)
|
|
|
m := monitor.New(host, port, callback)
|
|
|
m.Run(monitorCtx)
|
|
|
@@ -421,12 +496,14 @@ func runDynamicMode(ctx context.Context, host string, scanRange *[2]int, interva
|
|
|
delete(runningMonitors, port)
|
|
|
delete(monitoredPorts, port)
|
|
|
mu.Unlock()
|
|
|
+ logger.Info("端口 %d 监控已停止,等待重新连接", port)
|
|
|
fmt.Printf("端口 %d 监控已停止,等待重新连接\n", port)
|
|
|
}()
|
|
|
}
|
|
|
|
|
|
initial := scanner.Discover()
|
|
|
if len(initial) == 0 {
|
|
|
+ logger.Info("未找到运行中的 OpenCode 实例,等待自动检测")
|
|
|
fmt.Println("未找到运行中的 OpenCode 实例")
|
|
|
fmt.Println("请先执行: opencode serve --port 4096")
|
|
|
fmt.Println("启动后会自动检测,等待中...")
|
|
|
@@ -443,9 +520,11 @@ func runDynamicMode(ctx context.Context, host string, scanRange *[2]int, interva
|
|
|
ports = append(ports, p)
|
|
|
}
|
|
|
sort.Ints(ports)
|
|
|
+ logger.Info("找到 %d 个实例: %v", len(monitoredPorts), ports)
|
|
|
fmt.Printf("找到 %d 个实例: %v\n", len(monitoredPorts), ports)
|
|
|
}
|
|
|
|
|
|
+ logger.Info("动态模式启动,每 %d 秒扫描新实例", interval)
|
|
|
fmt.Printf("每 %d 秒扫描新实例,Ctrl+C 停止\n", interval)
|
|
|
fmt.Println(strings.Repeat("-", 40))
|
|
|
|
|
|
@@ -459,10 +538,14 @@ func runDynamicMode(ctx context.Context, host string, scanRange *[2]int, interva
|
|
|
return
|
|
|
case <-scanTicker.C:
|
|
|
newPorts := scanner.Discover()
|
|
|
+ if len(newPorts) > 0 {
|
|
|
+ logger.Debug("扫描到 %d 个端口: %v", len(newPorts), newPorts)
|
|
|
+ }
|
|
|
for _, port := range newPorts {
|
|
|
mu.Lock()
|
|
|
if !monitoredPorts[port] {
|
|
|
monitoredPorts[port] = true
|
|
|
+ logger.Info("发现新实例端口: %d,开始监控", port)
|
|
|
startMonitor(port)
|
|
|
}
|
|
|
mu.Unlock()
|
|
|
@@ -472,5 +555,6 @@ func runDynamicMode(ctx context.Context, host string, scanRange *[2]int, interva
|
|
|
}()
|
|
|
|
|
|
<-sigChan
|
|
|
+ logger.Info("收到停止信号,正在退出")
|
|
|
fmt.Println("\n已停止")
|
|
|
}
|