feat(logging): per-scan `{run_dir}/strix.log` with scan/agent context tagging

Every scan now writes a complete log file at ``{run_dir}/strix.log``
captured from the moment ``run_dir`` is resolved through teardown.
Stdlib ``logging`` only — no parallel framework.

New ``strix/telemetry/logging.py``:
  * ``setup_scan_logging(run_dir, debug=)`` attaches a ``FileHandler``
    (DEBUG, all ``strix.*``) plus a ``StreamHandler`` (ERROR by
    default; DEBUG via ``STRIX_DEBUG=1``).
  * ``ContextVar``-backed ``scan_id`` and ``agent_id`` injected by a
    ``Filter`` so every line is auto-tagged across asyncio tasks
    without callers passing them explicitly.
  * Third-party noise (``httpx``, ``litellm``, ``openai``,
    ``anthropic``, ``urllib3``, ``httpcore``) capped at WARNING.
  * Returns a teardown handle for ``finally`` cleanup.

Wiring:
  * ``orchestration/scan.py`` calls ``setup_scan_logging`` once per
    scan after ``run_dir`` resolves; sets scan_id; tears down in
    ``finally``. Adds INFO logs for sandbox bring-up + scan
    start/end.
  * ``orchestration/hooks.py`` sets/clears ``agent_id`` ContextVar in
    ``on_agent_start`` / ``on_agent_end`` and emits INFO for agent
    lifecycle, DEBUG for every tool start/end and LLM call.
  * ``interface/main.py`` drops the ``setLevel(ERROR)`` silencer.

Coverage expanded across ~20 files (orchestration, agents, runtime,
llm, tools, interface, config, skills) with INFO for lifecycle and
DEBUG for verbose detail. Per the system instructions in
``logger.warning(f"…{e}")`` were converted to module logger calls.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
0xallam
2026-04-25 23:35:01 -07:00
parent 9d7f754b59
commit 46ff025209
22 changed files with 415 additions and 34 deletions
+6
View File
@@ -16,6 +16,7 @@ back, so typos fail loudly.
from __future__ import annotations
import logging
from collections.abc import Awaitable, Callable
from typing import TYPE_CHECKING, Any
@@ -24,6 +25,9 @@ if TYPE_CHECKING:
from agents.sandbox.manifest import Manifest
logger = logging.getLogger(__name__)
# A backend brings up a fresh session and returns the (client, session)
# pair. The client is whatever object exposes ``await client.delete(session)``
# for cleanup — typically an ``agents.sandbox.client.BaseSandboxClient``
@@ -75,6 +79,7 @@ def get_backend(name: str) -> SandboxBackend:
raise ValueError(
f"Unknown STRIX_RUNTIME_BACKEND: {name!r} (supported: {supported})",
)
logger.debug("Selected sandbox backend: %s", name)
return backend
@@ -86,6 +91,7 @@ def register_backend(name: str, backend: SandboxBackend) -> None:
an existing name overwrites the prior entry.
"""
_BACKENDS[name] = backend
logger.info("Registered sandbox backend: %s", name)
def supported_backends() -> list[str]:
+2
View File
@@ -103,6 +103,7 @@ async def create_or_reuse(
caido_endpoint = await session.resolve_exposed_port(_CONTAINER_CAIDO_PORT)
host_caido_url = f"http://{caido_endpoint.host}:{caido_endpoint.port}"
logger.debug("Caido host endpoint resolved: %s", host_caido_url)
caido_client = await bootstrap_caido(
session,
@@ -116,6 +117,7 @@ async def create_or_reuse(
"caido_client": caido_client,
}
_SESSION_CACHE[scan_id] = bundle
logger.info("Sandbox session for scan %s ready and cached", scan_id)
return bundle