fix(cli): make the stage log reachable, nothing configured logging (M3)

The stage costs added for PHP diagnosis never appeared: nothing in this project
configures logging, so the default root level of WARNING dropped every
informational line and a PMPro case ran for an hour producing none.

That is the same failure shape the project keeps relearning. A run that reports
nothing is indistinguishable from a run with nothing to report, and it is how four
PHP investigations were spent reading a later stage's timeout as the cause.

Logging is now configured from OUSAST_LOG_LEVEL, quiet by default and sent to
stderr when asked, so a hook stays silent while a diagnostic run says where its
budget went. Verified end to end: six stage lines with durations on a real replay.

1,410 tests passed, nine skipped.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01EmM1j46qgKMh3zqD1QMwEo
This commit is contained in:
Marius Ciepluch
2026-09-18 15:50:08 +02:00
co-authored by Claude Opus 5
parent 45f9d6562e
commit dfc29238b5
+19
View File
@@ -2,6 +2,7 @@ from __future__ import annotations
import argparse
import json
import logging
import os
import subprocess
import sys
@@ -109,6 +110,19 @@ if TYPE_CHECKING:
pass
def _configure_logging() -> None:
"""Send logs to stderr at the level the environment asks for; quiet by default."""
level = os.environ.get("OUSAST_LOG_LEVEL", "").strip().upper()
if level not in {"DEBUG", "INFO", "WARNING", "ERROR"}:
return
handler = logging.StreamHandler()
handler.setFormatter(logging.Formatter("%(levelname)s %(name)s %(message)s"))
root = logging.getLogger("openultrasast")
root.handlers = [handler]
root.setLevel(getattr(logging, level))
root.propagate = False
def main(argv: list[str] | None = None) -> int:
if (argv if argv is not None else sys.argv[1:])[:1] != ["pre-push"]:
load_dotenv()
@@ -224,6 +238,11 @@ def main(argv: list[str] | None = None) -> int:
candidates.add_argument("--json", action="store_true")
args = parser.parse_args(argv)
# Diagnosis needs the stage costs, and nothing configures logging, so the default root level of
# WARNING silently dropped every informational line. A run that reports nothing is
# indistinguishable from a run with nothing to report, which is how four PHP investigations were
# spent reading a later stage's timeout as the cause.
_configure_logging()
if args.command == "pre-push":
from .config import PushConfig
from .push.runner import push, replay