fix(HEL-412): 刷新降级不再整次失败,并补齐准备中提示
手动刷新与自动补跑共用可用数据判定:日线推算或上一交易日快照记为部分/准备中成功,避免前端误报刷新失败。HTTP JSON 解析错误不再把请求正文写入日志。 Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
co-authored by
Cursor
multica-agent
parent
0d13066386
commit
5085cacf0d
@@ -145,7 +145,9 @@ class TushareAdapter(MarketAdapter):
|
||||
try:
|
||||
with urllib.request.urlopen(request, timeout=self.timeout) as response:
|
||||
result = json.loads(response.read().decode("utf-8"))
|
||||
except (urllib.error.URLError, TimeoutError, json.JSONDecodeError) as exc:
|
||||
except json.JSONDecodeError:
|
||||
raise AdapterError("Tushare returned invalid json") from None
|
||||
except (urllib.error.URLError, TimeoutError) as exc:
|
||||
raise AdapterError(f"Tushare request failed: {exc}") from exc
|
||||
if result.get("code") != 0:
|
||||
raise AdapterError(result.get("msg") or "Tushare returned an unknown error")
|
||||
|
||||
@@ -190,7 +190,15 @@ class HubRequestHandler(BaseHTTPRequestHandler):
|
||||
return {}
|
||||
if length <= 0 or length > 65536:
|
||||
raise ValueError("请求内容为空或过大")
|
||||
return json.loads(self.rfile.read(length).decode("utf-8"))
|
||||
raw = self.rfile.read(length)
|
||||
try:
|
||||
payload = json.loads(raw.decode("utf-8"))
|
||||
except (UnicodeDecodeError, json.JSONDecodeError):
|
||||
LOGGER.warning("invalid json request body")
|
||||
raise ValueError("请求不是合法 JSON") from None
|
||||
if not isinstance(payload, dict):
|
||||
raise ValueError("请求不是合法 JSON")
|
||||
return payload
|
||||
|
||||
def _cookie_value(self, name: str) -> str:
|
||||
cookie = SimpleCookie()
|
||||
|
||||
@@ -2,7 +2,9 @@ from __future__ import annotations
|
||||
|
||||
import json
|
||||
import logging
|
||||
import re
|
||||
import sys
|
||||
import traceback
|
||||
from typing import Any
|
||||
|
||||
from datahub.timeutil import isoformat
|
||||
@@ -11,6 +13,13 @@ _SECRET_KEYS = (
|
||||
"token", "password", "secret", "key", "authorization", "credential",
|
||||
"tushare_token", "datahub_token", "encryption_key", "cookie",
|
||||
)
|
||||
_SECRET_JSON = re.compile(
|
||||
r'(?i)("(?:' + "|".join(re.escape(key) for key in _SECRET_KEYS) + r')"\s*:\s*")([^"\\]*(?:\\.[^"\\]*)*)(")'
|
||||
)
|
||||
|
||||
|
||||
def redact_log_text(text: str) -> str:
|
||||
return _SECRET_JSON.sub(r"\1***\3", str(text))
|
||||
|
||||
|
||||
def _redact(value: Any, key: str = "") -> Any:
|
||||
@@ -21,22 +30,39 @@ def _redact(value: Any, key: str = "") -> Any:
|
||||
return {str(item_key): _redact(item_value, str(item_key)) for item_key, item_value in value.items()}
|
||||
if isinstance(value, list):
|
||||
return [_redact(item) for item in value]
|
||||
if isinstance(value, str):
|
||||
return redact_log_text(value)
|
||||
return value
|
||||
|
||||
|
||||
def _safe_exc_text(exc_info: tuple[Any, Any, Any]) -> str:
|
||||
exc = exc_info[1]
|
||||
if isinstance(exc, json.JSONDecodeError):
|
||||
return f"JSONDecodeError: invalid json at position {exc.pos}"
|
||||
cause = getattr(exc, "__cause__", None)
|
||||
if isinstance(cause, json.JSONDecodeError):
|
||||
return f"{type(exc).__name__}: invalid json in request"
|
||||
text = "".join(traceback.format_exception(*exc_info))
|
||||
if isinstance(cause, json.JSONDecodeError) and cause.doc:
|
||||
text = text.replace(cause.doc, "")
|
||||
if isinstance(exc, json.JSONDecodeError) and exc.doc:
|
||||
text = text.replace(exc.doc, "")
|
||||
return redact_log_text(text)
|
||||
|
||||
|
||||
class JsonFormatter(logging.Formatter):
|
||||
def format(self, record: logging.LogRecord) -> str:
|
||||
payload: dict[str, Any] = {
|
||||
"ts": isoformat(),
|
||||
"level": record.levelname,
|
||||
"logger": record.name,
|
||||
"message": record.getMessage(),
|
||||
"message": redact_log_text(record.getMessage()),
|
||||
}
|
||||
extra = getattr(record, "hub", None)
|
||||
if isinstance(extra, dict):
|
||||
payload.update(_redact(extra))
|
||||
if record.exc_info:
|
||||
payload["exc"] = self.formatException(record.exc_info)
|
||||
payload["exc"] = _safe_exc_text(record.exc_info)
|
||||
return json.dumps(payload, ensure_ascii=False, default=str)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user