From 80f60e5468bf62db8c16416fd65fa84715659f14 Mon Sep 17 00:00:00 2001 From: Hongsheng Liu Date: Sun, 4 Oct 2026 11:14:08 +0800 Subject: [PATCH] Timestamp the runner log lines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Diagnosing the dogfood runner's 26-minute 'task listing failed' storm (348 lines from a transient DB lock) was guesswork because runner.log carried no timestamps at all. The runner process now configures WARNING+ logging with asctime — runner.log is the only forensic trail after a crash, and its lines must be placeable in time. --- src/nanodot/cli.py | 16 ++++++++++++++++ tests/test_cli.py | 17 +++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/src/nanodot/cli.py b/src/nanodot/cli.py index 716d955..d14e4a2 100644 --- a/src/nanodot/cli.py +++ b/src/nanodot/cli.py @@ -12,6 +12,7 @@ import argparse import getpass +import logging import os import signal import subprocess @@ -912,6 +913,7 @@ def unwind() -> None: return 0 signal.signal(signal.SIGINT, _sigint) signal.signal(signal.SIGTERM, _sigint) + _configure_runner_logging() print("nanodot runner started — Ctrl-C to stop", flush=True) daemon.serve(stop) print("nanodot runner stopped") @@ -981,6 +983,20 @@ def _run_start(_: argparse.Namespace) -> int: return 1 +_RUNNER_LOG_FORMAT = "%(asctime)s %(levelname)s %(name)s %(message)s" + + +def _configure_runner_logging() -> None: + """Timestamped WARNING+ logging for the runner process. runner.log + lines are the only forensic trail after a crash; without timestamps a + failure storm (e.g. a transient DB lock) cannot be placed in time. + Process-local: this is our own process, not a global library policy.""" + logging.basicConfig( + level=logging.WARNING, + format=_RUNNER_LOG_FORMAT, + ) + + def _runner_alive() -> bool: from nanodot.native.runner_control import running_pid diff --git a/tests/test_cli.py b/tests/test_cli.py index bbb7c76..792e0a3 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -391,3 +391,20 @@ def fake_input(prompt: str = "") -> str: assert main(["watch", "show", task.id]) == 0 assert "flaky alerts:" in capsys.readouterr().out + + +def test_runner_logging_is_timestamped() -> None: + """runner.log is the post-crash forensic trail; every line must carry + a timestamp (found missing while diagnosing the dogfood failure storm).""" + import logging + import re + + from nanodot.cli import _RUNNER_LOG_FORMAT + + rendered = logging.Formatter(_RUNNER_LOG_FORMAT).format( + logging.LogRecord( + "nanodot.native.daemon", logging.WARNING, __file__, 1, + "task listing failed; will retry next pass", None, None, + ) + ) + assert re.match(r"^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2},\d{3} ", rendered), rendered