feat(warmup): log canaries to stderr by default (with main-thread violation warning)
Per module: prints a one-line summary to stderr when the import completes or fails: [warmup 1] google.genai on controller-io_0 (id=18636): 1218.6ms [warmup 2] anthropic on controller-io_1 (id=5500): 1148.3ms [warmup 3] openai on controller-io_2 (id=34376): 1144.2ms ... When the entire warmup completes, prints an aggregate: [warmup done] 9 modules: 9 completed (sum of per-module elapsed: 3591.7ms) If ANY canary ran on the main thread (main-thread-purity violation), the per-module line is tagged with [MAIN-THREAD] AND a final WARNING is printed: [warmup WARNING] N module(s) loaded on the MAIN THREAD: google.genai Default is log_to_stderr=True so production runs get the observability for free. Tests opt out via WarmupManager(pool, log_to_stderr=False) in the _build_warmup helper. 5 new tests (4 stderr logging + 1 quiet). All 13 canary tests pass. Use case: 'did my heavy import run on the GUI thread when it shouldnt have?' is now answered by grepping stderr for [warmup ...] [MAIN-THREAD] lines. No hook server required.
This commit is contained in:
@@ -27,9 +27,9 @@ from src.io_pool import make_io_pool
|
||||
|
||||
|
||||
def _build_warmup() -> tuple[WarmupManager, object]:
|
||||
"""Build a fresh WarmupManager + pool for testing."""
|
||||
"""Build a fresh WarmupManager + pool for testing (silent by default)."""
|
||||
pool = make_io_pool()
|
||||
mgr = WarmupManager(pool)
|
||||
mgr = WarmupManager(pool, log_to_stderr=False)
|
||||
return mgr, pool
|
||||
|
||||
|
||||
@@ -164,3 +164,78 @@ def test_canary_canary_id_increments_across_resets() -> None:
|
||||
f"canary_ids should be [first=1, second=2]; got {second_ids}"
|
||||
)
|
||||
pool.shutdown(wait=True)
|
||||
|
||||
def test_warmup_logs_to_stderr_on_completion(capsys: pytest.CaptureFixture) -> None:
|
||||
"""Successful canaries print a one-line summary to stderr."""
|
||||
pool = make_io_pool()
|
||||
mgr = WarmupManager(pool, log_to_stderr=True)
|
||||
mgr.submit(["os", "json"])
|
||||
assert mgr.wait(timeout=10.0)
|
||||
captured = capsys.readouterr()
|
||||
# Each completed module should have a log line
|
||||
assert "[warmup" in captured.err
|
||||
assert " os " in captured.err
|
||||
assert " json " in captured.err
|
||||
# Format: "[warmup N] module on thread (id=IDENT): ELAPSEDms"
|
||||
assert "controller-io" in captured.err
|
||||
assert "ms" in captured.err
|
||||
pool.shutdown(wait=True)
|
||||
|
||||
|
||||
def test_warmup_can_be_quiet(capsys: pytest.CaptureFixture) -> None:
|
||||
"""log_to_stderr=False suppresses the per-module log lines."""
|
||||
pool = make_io_pool()
|
||||
mgr = WarmupManager(pool, log_to_stderr=False)
|
||||
mgr.submit(["os", "json"])
|
||||
assert mgr.wait(timeout=10.0)
|
||||
captured = capsys.readouterr()
|
||||
# No per-module log lines
|
||||
assert "[warmup]" not in captured.err
|
||||
# But the structured canary records still exist
|
||||
canaries = mgr.canaries()
|
||||
assert len(canaries) == 2
|
||||
assert all(c["status"] == "completed" for c in canaries)
|
||||
pool.shutdown(wait=True)
|
||||
|
||||
|
||||
def test_warmup_logs_total_time_at_completion(capsys: pytest.CaptureFixture) -> None:
|
||||
"""A summary line is printed when the entire warmup completes."""
|
||||
pool = make_io_pool()
|
||||
mgr = WarmupManager(pool, log_to_stderr=True)
|
||||
mgr.submit(["os", "json"])
|
||||
assert mgr.wait(timeout=10.0)
|
||||
captured = capsys.readouterr()
|
||||
# Summary line contains "done" or "ready" or "complete"
|
||||
err_lines = [l for l in captured.err.splitlines() if l.strip()]
|
||||
assert len(err_lines) >= 3 # 2 per-module + 1 summary
|
||||
# The summary should mention total/total_ms or something aggregate
|
||||
summary_line = err_lines[-1]
|
||||
assert "warmup" in summary_line.lower()
|
||||
pool.shutdown(wait=True)
|
||||
|
||||
|
||||
def test_warmup_logs_failure_to_stderr(capsys: pytest.CaptureFixture) -> None:
|
||||
"""A failed import prints a FAILED log line to stderr."""
|
||||
pool = make_io_pool()
|
||||
mgr = WarmupManager(pool, log_to_stderr=True)
|
||||
mgr.submit(["definitely_does_not_exist_xyz_12345"])
|
||||
assert mgr.wait(timeout=10.0)
|
||||
captured = capsys.readouterr()
|
||||
# Should contain a FAILED marker
|
||||
assert "FAILED" in captured.err
|
||||
assert "definitely_does_not_exist_xyz_12345" in captured.err
|
||||
pool.shutdown(wait=True)
|
||||
|
||||
|
||||
def test_warmup_log_line_includes_thread_id(capsys: pytest.CaptureFixture) -> None:
|
||||
"""The log line includes the thread_id (matching the canary record)."""
|
||||
pool = make_io_pool()
|
||||
mgr = WarmupManager(pool, log_to_stderr=True)
|
||||
mgr.submit(["os"])
|
||||
assert mgr.wait(timeout=10.0)
|
||||
canaries = mgr.canaries()
|
||||
captured = capsys.readouterr()
|
||||
# The thread_id from the canary should appear in the log
|
||||
thread_id = str(canaries[0]["thread_id"])
|
||||
assert thread_id in captured.err, f"expected thread_id {thread_id} in stderr: {captured.err!r}"
|
||||
pool.shutdown(wait=True)
|
||||
|
||||
Reference in New Issue
Block a user