diff --git a/monitor/runtime_config.py b/monitor/runtime_config.py index 4185672..1c3c7c9 100644 --- a/monitor/runtime_config.py +++ b/monitor/runtime_config.py @@ -306,6 +306,8 @@ GO_ENV_SPECS_BY_PRIMARY = {spec["primary"]: spec for spec in GO_ENV_SPECS} # 远端文件不存在时,用固定标记避免和 shell 错误混淆。 REMOTE_FILE_MISSING_MARKER = "__HY_APP_MONITOR_FILE_MISSING__" +# TAT 单次输出有长度上限,读取较大的 env / compose 文件时需要分块。 +REMOTE_TEXT_CHUNK_BYTES = 4096 def go_service_hosts() -> list[dict[str, Any]]: @@ -391,7 +393,7 @@ def remote_host_result(host: dict[str, Any], script: str, command_name: str, *, return result -def read_remote_text(host: dict[str, Any], path: str) -> str: +def remote_file_size(host: dict[str, Any], path: str) -> int: quoted_path = shlex.quote(path) script = f"""set -eu FILE={quoted_path} @@ -399,13 +401,54 @@ if [ ! -f "$FILE" ]; then printf '%s' {shlex.quote(REMOTE_FILE_MISSING_MARKER)} exit 0 fi -cat "$FILE" +python3 - "$FILE" <<'PY' +from pathlib import Path +import sys + +print(Path(sys.argv[1]).stat().st_size, end="") +PY """ - result = remote_host_result(host, script, f"read-{host['host']}-service-env") + result = remote_host_result(host, script, f"stat-{host['host']}-remote-file") output = str(result.get("output") or "") if output.strip() == REMOTE_FILE_MISSING_MARKER: raise RuntimeError(f"{host['host']} missing file: {path}") - return output + return int(output.strip() or "0") + + +def read_remote_text(host: dict[str, Any], path: str) -> str: + quoted_path = shlex.quote(path) + file_size = remote_file_size(host, path) + if file_size == 0: + return "" + + # 通过多次小输出读取完整文件,避免 TAT 把大文本截断。 + chunks: list[bytes] = [] + for offset in range(0, file_size, REMOTE_TEXT_CHUNK_BYTES): + script = f"""set -eu +FILE={quoted_path} +if [ ! -f "$FILE" ]; then + printf '%s' {shlex.quote(REMOTE_FILE_MISSING_MARKER)} + exit 0 +fi +python3 - "$FILE" {offset} {REMOTE_TEXT_CHUNK_BYTES} <<'PY' +from pathlib import Path +import base64 +import sys + +path = Path(sys.argv[1]) +offset = int(sys.argv[2]) +chunk_size = int(sys.argv[3]) +data = path.read_bytes()[offset : offset + chunk_size] +print(base64.b64encode(data).decode("ascii"), end="") +PY +""" + result = remote_host_result(host, script, f"read-{host['host']}-remote-file-chunk-{offset}") + output = str(result.get("output") or "") + if output.strip() == REMOTE_FILE_MISSING_MARKER: + raise RuntimeError(f"{host['host']} missing file: {path}") + chunks.append(base64.b64decode(output.strip() or "")) + + return b"".join(chunks).decode("utf-8") def write_remote_text(host: dict[str, Any], path: str, content: str) -> None: