# Logging Constraints ## Core Rule **Use `logging.getLogger(__name__)` - never use `print()`** ## Logger Initialization Get logger at module level: ```python # GOOD: Use logging.getLogger(__name__) import logging _logger = logging.getLogger(__name__) def some_function(): _logger.info("Processing started") _logger.debug("Detail: %s", detail) ``` ## Logger Naming Use `__name__` for automatic module path: ```python # In database.py - logger will be "database" _logger = logging.getLogger(__name__) # In pipeline/svd_pipeline.py - logger will be "pipeline.svd_pipeline" _logger = logging.getLogger(__name__) ``` ## Log Levels | Level | When to Use | |-------|-------------| | DEBUG | Detailed diagnostic info (dev only) | | INFO | Normal operation milestones | | WARNING | Unexpected but handled (fallbacks) | | ERROR | Operation failed, may need attention | | CRITICAL | Fatal error, program may crash | ## Examples ### Good Logging Practice ```python _logger.info("Pipeline run: %s → %s (%s windows)", start, end, count) _logger.debug("Batch embedding attempt %d failed: %s", attempt, exc) _logger.warning("Fallback used for motion %d: %s", motion_id, reason) _logger.error("Query failed: %s", exc) ``` ### Bad: Using print() ```python # BAD - don't use print print(f"Fetched {len(voting_records)} voting records from API") print(f"Error fetching motions from API: {e}") ``` ### Good: Using logger ```python # GOOD - use logger _logger.info("Fetched %d voting records from API", len(voting_records)) _logger.error("Error fetching motions from API: %s", e) ``` ## Exception Logging Use `_logger.exception()` for caught exceptions (includes traceback): ```python try: result = risky_operation() except Exception as exc: _logger.exception("Operation failed: %s", exc) return fallback_value ``` Use `_logger.error()` with explicit exception for controlled errors: ```python try: result = risky_operation() except Exception as exc: _logger.error("Operation failed: %s", exc) return fallback_value ``` ## Configuration Ensure logging is configured in entry points: ```python # pipeline/run_pipeline.py def run(args): logging.basicConfig( level=logging.INFO, format="%(asctime)s %(levelname)s %(name)s: %(message)s", ) # ... rest of pipeline ``` ## Anti-Patterns ### Debug Prints in Production Code ```python # BAD print(f"[TRAJ DEBUG] processing window {wid}") # GOOD _logger.debug("Processing window %s", wid) ``` ### Inconsistent Logger Names ```python # BAD - mixing _logger and logger _logger = logging.getLogger(__name__) logger = logging.getLogger("other") # Inconsistent # GOOD - use single consistent pattern _logger = logging.getLogger(__name__) ``` ### Missing Logger Initialization ```python # BAD - no logger defined def some_function(): logging.getLogger(__name__).info("...") # Redundant calls # GOOD - define once at module level _logger = logging.getLogger(__name__) def some_function(): _logger.info("...") ``` ## Sensitive Data Never log sensitive information: - API keys - User votes - Session IDs (if tied to user data) - Personal information ```python # BAD _logger.info("User %s voted %s", user_id, vote) # GOOD - log aggregates, not individual votes _logger.info("Vote recorded for session %s", session_id[:8]) ``` ## Structured Logging For complex data, use structured logging: ```python _logger.info( "Motion processed", extra={ "motion_id": motion_id, "policy_area": policy_area, "processing_time_ms": elapsed_ms, } ) ```