fix(system): 优化服务状态检查的异常处理逻辑
在 `system.py` 中更新了服务状态检查的异常处理逻辑,当 supervisor 未安装或未运行时,记录为 WARNING 并返回友好的错误信息。增强了日志记录的可读性,确保在出现问题时提供清晰的反馈。同时,在 `position_manager.py` 中改进了止损止盈检查的错误日志,确保记录详细的错误信息以便于调试。
This commit is contained in:
parent
c750478af9
commit
3a2536ae96
|
|
@ -917,14 +917,28 @@ async def list_trading_services(_admin: Dict[str, Any] = Depends(require_system_
|
||||||
"raw": status_all
|
"raw": status_all
|
||||||
}
|
}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# 如果 supervisorctl status 失败,可能返回非 0 exit code
|
# supervisor 未安装/未运行时(如 unix socket 不存在)避免刷 ERROR,改为 WARNING 并返回友好说明
|
||||||
# 但 _run_supervisorctl 已经处理了 status 命令的 exit 3 (stopped)
|
err_msg = str(e).strip()
|
||||||
# 如果是其他错误,记录日志并返回空列表
|
if not err_msg:
|
||||||
|
err_msg = repr(e)
|
||||||
|
is_supervisor_unavailable = (
|
||||||
|
"no such file" in err_msg.lower()
|
||||||
|
or "connection refused" in err_msg.lower()
|
||||||
|
or "sock" in err_msg.lower()
|
||||||
|
or "unix://" in err_msg.lower()
|
||||||
|
)
|
||||||
|
if is_supervisor_unavailable:
|
||||||
|
logger.warning(f"列出服务失败(supervisor 未运行或不可用): {err_msg}")
|
||||||
|
return {
|
||||||
|
"summary": {"total": 0, "running": 0, "stopped": 0, "unknown": 0},
|
||||||
|
"services": [],
|
||||||
|
"error": "supervisor 未安装或未运行,请检查 supervisord 或配置 SUPERVISOR_CONF"
|
||||||
|
}
|
||||||
logger.error(f"列出服务失败: {e}")
|
logger.error(f"列出服务失败: {e}")
|
||||||
return {
|
return {
|
||||||
"summary": {"total": 0, "running": 0, "stopped": 0, "unknown": 0},
|
"summary": {"total": 0, "running": 0, "stopped": 0, "unknown": 0},
|
||||||
"services": [],
|
"services": [],
|
||||||
"error": str(e)
|
"error": err_msg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2032,30 +2032,41 @@ class BinanceClient:
|
||||||
# 优先尝试 WebSocket 下单(减少 REST 超时)
|
# 优先尝试 WebSocket 下单(减少 REST 超时)
|
||||||
if self._ws_trade_client and self._ws_trade_client.is_connected():
|
if self._ws_trade_client and self._ws_trade_client.is_connected():
|
||||||
try:
|
try:
|
||||||
# 准备 WS 参数(需包含 apiKey, timestamp, signature)
|
# 准备 WS 参数(与币安 WS 文档一致:apiKey, timestamp, recvWindow, 其余同 REST)
|
||||||
ws_params = dict(params)
|
ws_params = dict(params)
|
||||||
ws_params["apiKey"] = self.api_key
|
ws_params["apiKey"] = self.api_key
|
||||||
|
ts_ms = int(time.time() * 1000)
|
||||||
if "timestamp" not in ws_params:
|
if "timestamp" not in ws_params:
|
||||||
ws_params["timestamp"] = int(time.time() * 1000)
|
ws_params["timestamp"] = ts_ms
|
||||||
# 计算签名:币安要求参与签名的值格式与 REST 一致(布尔小写、空值不参与或为空串)
|
else:
|
||||||
|
ws_params["timestamp"] = int(ws_params["timestamp"])
|
||||||
|
# 扩大接收窗口,避免服务器与本地时钟偏差导致签名被拒
|
||||||
|
ws_params["recvWindow"] = 60000
|
||||||
|
# 币安 WS 文档中 closePosition 为 STRING,发送与签名均用 "true"/"false"
|
||||||
|
if ws_params.get("closePosition") is True:
|
||||||
|
ws_params["closePosition"] = "true"
|
||||||
|
elif ws_params.get("closePosition") is False:
|
||||||
|
ws_params["closePosition"] = "false"
|
||||||
|
# 计算签名:参与签名的字符串必须与 REST 一致(键按字母序、值统一为字符串,布尔为 true/false)
|
||||||
if "signature" not in ws_params:
|
if "signature" not in ws_params:
|
||||||
import hmac
|
import hmac
|
||||||
import hashlib
|
import hashlib
|
||||||
from urllib.parse import urlencode
|
from urllib.parse import urlencode
|
||||||
def _param_val_for_signature(v):
|
def _val_for_sig(v):
|
||||||
if v is True:
|
if v is True:
|
||||||
return "true"
|
return "true"
|
||||||
if v is False:
|
if v is False:
|
||||||
return "false"
|
return "false"
|
||||||
if v is None:
|
if v is None:
|
||||||
return ""
|
return ""
|
||||||
s = str(v).strip()
|
if isinstance(v, (int, float)):
|
||||||
return s if s else ""
|
return str(int(v)) if isinstance(v, float) and v == int(v) else str(v)
|
||||||
sign_params = [(k, _param_val_for_signature(v)) for k, v in ws_params.items() if k != "signature"]
|
return str(v).strip()
|
||||||
query_string = urlencode(sorted(sign_params))
|
to_sign = [(k, _val_for_sig(v)) for k, v in ws_params.items() if k != "signature"]
|
||||||
|
query_string = urlencode(sorted(to_sign))
|
||||||
signature = hmac.new(
|
signature = hmac.new(
|
||||||
self.api_secret.encode('utf-8'),
|
self.api_secret.encode("utf-8"),
|
||||||
query_string.encode('utf-8'),
|
query_string.encode("utf-8"),
|
||||||
hashlib.sha256
|
hashlib.sha256
|
||||||
).hexdigest()
|
).hexdigest()
|
||||||
ws_params["signature"] = signature
|
ws_params["signature"] = signature
|
||||||
|
|
@ -2357,7 +2368,7 @@ class BinanceClient:
|
||||||
# 如果还是失败,记录详细参数用于调试
|
# 如果还是失败,记录详细参数用于调试
|
||||||
logger.error(f"{symbol} ❌ 所有重试都失败,保护单挂单失败")
|
logger.error(f"{symbol} ❌ 所有重试都失败,保护单挂单失败")
|
||||||
logger.error(f" 参数: {params}")
|
logger.error(f" 参数: {params}")
|
||||||
logger.error(f" 对冲模式: {dual}")
|
logger.error(f" 对冲模式: {dual}(None 表示无法读取持仓模式,请检查网络或 API 权限)")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
except BinanceAPIException as e:
|
except BinanceAPIException as e:
|
||||||
|
|
|
||||||
|
|
@ -2383,7 +2383,8 @@ class PositionManager:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"检查止损止盈失败: {e}")
|
err_msg = str(e).strip() or repr(e) or type(e).__name__
|
||||||
|
logger.error(f"检查止损止盈失败: {err_msg}")
|
||||||
|
|
||||||
return closed_positions
|
return closed_positions
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user