From 8f047aced9f0ec8f5d8ca52438437dfa65716692 Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Wed, 2 Sep 2026 21:13:57 -0700 Subject: [PATCH] =?UTF-8?q?refactor(hermes=5Fcli):=20debug.py=20=E2=80=94?= =?UTF-8?q?=20lift=20tail-reader=20out=20of=20=5Fcapture=5Flog=5Fsnapshot;?= =?UTF-8?q?=20cron=20status=20lock-probe=20collapse?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hermes_cli/cron.py | 8 +++---- hermes_cli/debug.py | 52 +++++++++++++++++++++++---------------------- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/hermes_cli/cron.py b/hermes_cli/cron.py index 210fa0252e..7bcc5bdb31 100644 --- a/hermes_cli/cron.py +++ b/hermes_cli/cron.py @@ -465,11 +465,9 @@ def cron_status(): try: from gateway.status import get_running_pid, is_gateway_runtime_lock_active - if is_gateway_runtime_lock_active(): - gateway_alive_via_lock = True - lock_pid = get_running_pid() - if lock_pid: - pids = [lock_pid] + gateway_alive_via_lock = is_gateway_runtime_lock_active() + lock_pid = get_running_pid() if gateway_alive_via_lock else None + pids = [lock_pid] if lock_pid else pids except Exception: pass if pids or gateway_alive_via_lock: diff --git a/hermes_cli/debug.py b/hermes_cli/debug.py index a0d4e16161..3403f33c42 100644 --- a/hermes_cli/debug.py +++ b/hermes_cli/debug.py @@ -299,6 +299,32 @@ def _redact_log_text(text: str) -> str: return _EMAIL_ADDRESS_RE.sub("[REDACTED_EMAIL]", text) +def _read_tail_bytes(log_path: Path, size: int, max_bytes: int, tail_lines: int) -> tuple[bytes, bool]: + """Read the whole file, or enough of its tail for both views → (raw, truncated). + + For oversized files, read backwards until we have ``max_bytes`` for the standalone upload + AND enough newline context to render the summary tail from the same snapshot. + """ + with open(log_path, "rb") as f: + if size <= max_bytes: + return f.read(), False + chunk_size = 8192 + pos = size + chunks: list[bytes] = [] + total = 0 + newline_count = 0 + while pos > 0 and (total < max_bytes or newline_count <= tail_lines + 1) and total < max_bytes * 2: + read_size = min(chunk_size, pos) + pos -= read_size + f.seek(pos) + chunk = f.read(read_size) + chunks.insert(0, chunk) + total += len(chunk) + newline_count += chunk.count(b"\n") + chunk_size = min(chunk_size * 2, 65536) + return b"".join(chunks), pos > 0 + + def _capture_log_snapshot( log_name: str, *, tail_lines: int, max_bytes: int = _MAX_LOG_BYTES, redact: bool = True, ) -> LogSnapshot: @@ -320,31 +346,7 @@ def _capture_log_snapshot( # race: file was truncated between _resolve_log_path and stat return LogSnapshot(path=log_path, tail_text="(file empty)", full_text=None) - with open(log_path, "rb") as f: - if size <= max_bytes: - raw = f.read() - truncated = False - else: - # Read from the end until we have enough bytes for the standalone upload and - # enough newline context to render the summary tail from the same snapshot. - chunk_size = 8192 - pos = size - chunks: list[bytes] = [] - total = 0 - newline_count = 0 - - while pos > 0 and (total < max_bytes or newline_count <= tail_lines + 1) and total < max_bytes * 2: - read_size = min(chunk_size, pos) - pos -= read_size - f.seek(pos) - chunk = f.read(read_size) - chunks.insert(0, chunk) - total += len(chunk) - newline_count += chunk.count(b"\n") - chunk_size = min(chunk_size * 2, 65536) - - raw = b"".join(chunks) - truncated = pos > 0 + raw, truncated = _read_tail_bytes(log_path, size, max_bytes, tail_lines) full_raw = raw if truncated and len(full_raw) > max_bytes: