feat: RESTART 走 NSSM 服务重启 + 失败原因可观测化

- RESTART 命令优先通过 nssm restart 重启主程序服务(假死场景可用),未配置服务名回退环回 HTTP
- nssm 输出按 UTF-16 解码并记录日志,权限不足时提示需以服务方式运行
- 环回管理接口 code=3001 时明确报鉴权失败,不再误报不可达
- 新增 nssm.path / main.service.name / log.dir 配置项
- 增加文件日志(RotatingFileHandler 5MB×5),服务方式运行可查日志
- HTTP 调用失败与状态码异常统一 WARNING 记录(原 DEBUG 不可见)
- 断线日志区分「网络不通」与「被服务器拒绝(未注册/token 错误)」
- 连接 URL 自动拼接 device.id,消除双处配置不一致

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-08-27 17:24:17 +08:00
co-authored by Claude
parent 86bbe8ef5a
commit f07bbbc49d
7 changed files with 144 additions and 26 deletions
+15 -5
View File
@@ -93,11 +93,21 @@ class CommandDispatcher:
result = self._get_log_stream().control(action, file)
elif msg_type == "RESTART":
ok = self.main_app_client.restart(1000)
result = {
"ok": ok,
"msg": "已触发优雅重启,等待 ProcessGuard 拉起" if ok else "重启请求失败",
}
# 优先走 NSSM 服务重启(主程序假死时 HTTP 不可达也能重启成功);
# 未配置服务名时回退环回 HTTP 触发主程序优雅重启
if self.config.main_service_name:
from agent.process_guard import restart_main_service
ok, reason = restart_main_service(self.config.nssm_path, self.config.main_service_name)
result = {
"ok": ok,
"msg": "已通过 NSSM 重启主程序服务" if ok else f"NSSM 重启失败:{reason}",
}
else:
ok, reason = self.main_app_client.restart(1000)
result = {
"ok": ok,
"msg": "已触发优雅重启,等待 ProcessGuard 拉起" if ok else f"重启请求失败:{reason}",
}
elif msg_type == "GET_CONFIG":
hospital = (payload or {}).get("hospital") or ""