新增web界面管理策略
This commit is contained in:
@@ -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("✅ 所有策略重启成功")
|
||||
|
||||
Reference in New Issue
Block a user