refactor: adopt Python logging module with named loggers
Replace all print() calls with structured logging using the Python logging module. This provides: - Named loggers per subsystem: commander_ai_lab.api, .batch, .sim, .coach, .deckgen, .collection, .scanner, .ml, .cache, .pplx - Dual output: console (StreamHandler) + rotating file (RotatingFileHandler) at logs/commander-ai-lab.log (25 MB x 5 backups = 125 MB max) - Configurable via env vars: CAL_LOG_DIR, CAL_LOG_MAX_MB, CAL_LOG_BACKUP_COUNT - --verbose / -v flag for DEBUG-level output - overnight-run.py: separate rotating log at logs/overnight-run.log (10 MB x 5) - Sub-module loggers in ml/, scanner/, deck_sources/, src/ all propagate to the root commander_ai_lab logger - Stripped redundant [TAG] prefixes from messages (logger name carries context) - Format: '2026-03-13 14:00:00 INFO [commander_ai_lab.batch] message' Files changed: lab_api.py, overnight-run.py, and 11 sub-module .py files 176 total print() calls converted (101 in lab_api.py, 76 in sub-modules)
This commit is contained in:
1 parent
8d2e6199db
commit
a1fd290d5f
13 files changed
+390
-257
No files matched your search
+6
-2
@@ -13,6 +13,10 @@ No local OCR or OpenCV needed — all recognition is done by the
|
||||
Ximilar cloud API using visual AI.
|
||||
"""
|
||||
from typing import Callable, Optional
|
||||
import logging
|
||||
|
||||
log = logging.getLogger("commander_ai_lab.scanner")
|
||||
|
||||
|
||||
from .ximilar_client import identify_cards, XimilarResult
|
||||
|
||||
@@ -160,7 +164,7 @@ def _ximilar_to_scan_result(xr: XimilarResult,
|
||||
try:
|
||||
scryfall_data = scryfall_lookup(xr.name)
|
||||
except Exception as e:
|
||||
print(f" [SCAN] Scryfall lookup failed for '{xr.name}': {e}")
|
||||
log.error(f" Scryfall lookup failed for '{xr.name}': {e}")
|
||||
|
||||
if scryfall_data and scryfall_data.get("object") != "error":
|
||||
result.scryfall_id = scryfall_data.get("id", "")
|
||||
@@ -181,6 +185,6 @@ def _ximilar_to_scan_result(xr: XimilarResult,
|
||||
# Extract image from scryfall URL pattern
|
||||
# e.g., https://scryfall.com/card/m11/149/lightning-bolt
|
||||
result.image_uri = "" # Will be fetched by frontend via scryfall_id
|
||||
print(f" [SCAN] No Scryfall match for '{xr.name}' — using Ximilar data only")
|
||||
log.info(f" No Scryfall match for '{xr.name}' — using Ximilar data only")
|
||||
|
||||
return result
|
||||
@@ -13,6 +13,10 @@ import json
|
||||
from typing import Optional, List
|
||||
from urllib.request import Request, urlopen
|
||||
from urllib.error import URLError, HTTPError
|
||||
import logging
|
||||
|
||||
log = logging.getLogger("commander_ai_lab.scanner.ximilar")
|
||||
|
||||
|
||||
XIMILAR_ENDPOINT = "https://api.ximilar.com/collectibles/v2/tcg_id"
|
||||
|
||||
@@ -82,7 +86,7 @@ def identify_cards(image_bytes: bytes, api_key: str,
|
||||
)
|
||||
|
||||
try:
|
||||
print(f" [XIMILAR] Sending {len(image_bytes):,} bytes to API (analyze_all={analyze_all})")
|
||||
log.info(f" Sending {len(image_bytes):,} bytes to API (analyze_all={analyze_all})")
|
||||
with urlopen(req, timeout=60) as resp:
|
||||
resp_data = json.loads(resp.read().decode("utf-8"))
|
||||
except HTTPError as e:
|
||||
@@ -168,12 +172,12 @@ def _parse_response(resp_data: dict) -> List[XimilarResult]:
|
||||
if not results:
|
||||
results.append(XimilarResult(error="Ximilar could not identify any cards"))
|
||||
|
||||
print(f" [XIMILAR] Identified {len(results)} card(s)")
|
||||
log.info(f" Identified {len(results)} card(s)")
|
||||
for r in results:
|
||||
if r.name:
|
||||
print(f" [XIMILAR] - {r.name} ({r.set_code}) conf={r.confidence:.2f}")
|
||||
log.info(f" - {r.name} ({r.set_code}) conf={r.confidence:.2f}")
|
||||
elif r.error:
|
||||
print(f" [XIMILAR] - ERROR: {r.error}")
|
||||
log.error(f" - ERROR: {r.error}")
|
||||
|
||||
return results
|
||||
|
||||
|
||||
Reference in new issue
Block a user