新增web界面管理策略

This commit is contained in:
2025-11-21 16:08:03 +08:00
parent 4b7ec4e564
commit 218ca5f533
11 changed files with 747 additions and 124 deletions

View File

@@ -29,10 +29,10 @@ class RestartDaemon:
# 确保目录存在
self.pid_dir.mkdir(exist_ok=True)
self.log_dir.mkdir(exist_ok=True) # 确保日志目录存在
def _setup_logger(self):
"""配置日志"""
self.log_dir.mkdir(exist_ok=True)
log_file = self.log_dir / "restart_daemon.log"
logging.basicConfig(
@@ -41,9 +41,14 @@ class RestartDaemon:
handlers=[
logging.FileHandler(log_file, encoding='utf-8'),
logging.StreamHandler(sys.stdout)
]
],
force=True # 防止日志配置冲突
)
return logging.getLogger("RestartDaemon")
logger = logging.getLogger("RestartDaemon")
logger.info("=" * 80)
logger.info("📝 日志系统初始化完成")
logger.info("📂 日志文件: %s", log_file.absolute())
return logger
def start(self):
"""启动守护进程"""
@@ -57,8 +62,8 @@ class RestartDaemon:
self.logger.info("=" * 80)
self.logger.info("✅ 重启守护进程已启动")
self.logger.info("⏰ 监控时间点: {}".format(", ".join(self.RESTART_TIMES)))
self.logger.info("📂 PID目录: {}".format(self.pid_dir.absolute()))
self.logger.info("⏰ 监控时间点: %s", ", ".join(self.RESTART_TIMES))
self.logger.info("📂 PID目录: %s", self.pid_dir.absolute())
self.logger.info("=" * 80)
# 主线程阻塞(保持进程运行)
@@ -97,14 +102,14 @@ class RestartDaemon:
time.sleep(60) # 每分钟检查一次
except Exception as e:
self.logger.error("❌ 检查循环出错: {}".format(e))
self.logger.error("❌ 检查循环出错: %s", e, exc_info=True)
self.logger.error("=" * 80)
time.sleep(60) # 出错后等待1分钟继续
def _perform_restart(self, time_point: str):
"""执行重启"""
self.logger.info("\n" + "=" * 80)
self.logger.info("⏰ 到达重启时间: {}".format(time_point))
self.logger.info("⏰ 到达重启时间: %s", time_point)
self.logger.info("=" * 80)
# 1. 扫描所有PID文件
@@ -113,7 +118,7 @@ class RestartDaemon:
self.logger.info("⚠️ 未发现运行中的策略")
return
self.logger.info("📋 发现 {} 个策略需要重启".format(len(pid_files)))
self.logger.info("📋 发现 %d 个策略需要重启", len(pid_files))
# 2. 停止所有策略
stopped_count = 0
@@ -124,21 +129,21 @@ class RestartDaemon:
if psutil.pid_exists(pid):
proc = psutil.Process(pid)
self.logger.info("⏹️ 停止策略 PID {}: {}".format(pid, proc.name()))
self.logger.info("⏹️ 停止策略 PID %d: %s", pid, proc.name())
proc.terminate()
try:
proc.wait(timeout=30)
self.logger.info("✅ 已优雅停止 PID {}".format(pid))
self.logger.info("✅ 已优雅停止 PID %d", pid)
stopped_count += 1
except psutil.TimeoutExpired:
proc.kill()
self.logger.info("🔥 强制终止 PID {}".format(pid))
self.logger.info("🔥 强制终止 PID %d", pid)
stopped_count += 1
else:
self.logger.warning("⚠️ PID文件存在但进程已死: {}".format(pid))
self.logger.warning("⚠️ PID文件存在但进程已死: %d", pid)
except Exception as e:
self.logger.error("❌ 停止失败 {}: {}".format(pid_file, e))
self.logger.error("❌ 停止失败 %s: %s", pid_file, e, exc_info=True)
if stopped_count == 0:
self.logger.warning("⚠️ 未成功停止任何策略")
@@ -154,38 +159,38 @@ class RestartDaemon:
for pid_file in pid_files:
try:
# 从PID文件名推导配置路径
# DualModeTrendlineHawkesStrategy2_FG.pid -> strategies/DualModeTrendlineHawkesStrategy2/FG.config
# DualModeTrendlineHawkesStrategy2_FG.pid -> strategies/DualModeTrendlineHawkesStrategy2/FG.py
name = pid_file.stem
if '_' not in name:
self.logger.error("❌ PID文件名格式错误: {}".format(name))
self.logger.error("❌ PID文件名格式错误: %s", name)
continue
strategy_name, symbol = name.split('_', 1)
config_file = Path("strategies") / strategy_name / "{}.config".format(symbol)
config_file = Path("strategies") / strategy_name / "{}.py".format(symbol)
if not config_file.exists():
self.logger.error("❌ 配置文件不存在: {}".format(config_file))
self.logger.error("❌ 配置文件不存在: %s", config_file)
continue
# 启动新进程(不阻塞,立即返回)
process = subprocess.Popen(
[sys.executable, "launcher.py", "--config", str(config_file)],
stdout=subprocess.DEVNULL,
stdout=subprocess.DEVNULL, # launcher内会自行处理日志
stderr=subprocess.DEVNULL,
cwd=Path.cwd()
)
self.logger.info("✅ 启动新进程 PID {}: {}".format(process.pid, config_file.name))
self.logger.info("✅ 启动新进程 PID %d: %s", process.pid, config_file.name)
restarted_count += 1
except Exception as e:
self.logger.error("❌ 启动失败: {}".format(e))
self.logger.error("❌ 启动失败: %s", e, exc_info=True)
# 5. 统计结果
self.logger.info("\n" + "=" * 80)
self.logger.info("📊 重启统计:")
self.logger.info(" 停止成功: {}".format(stopped_count))
self.logger.info(" 启动成功: {}".format(restarted_count))
self.logger.info(" 停止成功: %d", stopped_count)
self.logger.info(" 启动成功: %d", restarted_count)
if stopped_count == restarted_count and stopped_count > 0:
self.logger.info("✅ 所有策略重启成功")