Source code for ncaa_eval.utils.logger
"""Structured logging with configurable verbosity levels.
Provides project-wide logging configuration using Python's standard
`logging` module. Four verbosity levels map to standard (and one custom)
Python log levels:
======== ============== =====
Project Python level Value
======== ============== =====
QUIET WARNING 30
NORMAL INFO 20
VERBOSE VERBOSE (custom) 15
DEBUG DEBUG 10
======== ============== =====
Usage:
Configure once at application startup, then obtain named loggers
anywhere in the codebase::
>>> from ncaa_eval.utils.logger import configure_logging, get_logger
>>> configure_logging("VERBOSE")
>>> log = get_logger("ingest")
>>> log.info("Loading data...")
The verbosity can also be controlled via the `NCAA_EVAL_LOG_LEVEL`
environment variable (case-insensitive). An explicit `level` argument
to `configure_logging` takes precedence over the environment variable,
which in turn takes precedence over the default (`NORMAL`).
"""
from __future__ import annotations
import logging
import os
import sys
# ---------------------------------------------------------------------------
# Custom VERBOSE level (between INFO=20 and DEBUG=10)
# ---------------------------------------------------------------------------
VERBOSE: int = 15
"""Custom log level between INFO and DEBUG for detailed operational output."""
logging.addLevelName(VERBOSE, "VERBOSE")
# ---------------------------------------------------------------------------
# Convenience aliases for all project verbosity levels
# ---------------------------------------------------------------------------
QUIET: int = logging.WARNING
"""Project verbosity that suppresses routine output (maps to WARNING=30)."""
NORMAL: int = logging.INFO
"""Default project verbosity (maps to INFO=20)."""
DEBUG: int = logging.DEBUG
"""Full diagnostic output (maps to DEBUG=10)."""
# ---------------------------------------------------------------------------
# Internal mapping from project level names to numeric values
# ---------------------------------------------------------------------------
_LEVEL_MAP: dict[str, int] = {
"QUIET": QUIET,
"NORMAL": NORMAL,
"VERBOSE": VERBOSE,
"DEBUG": DEBUG,
}
_ROOT_LOGGER_NAME: str = "ncaa_eval"
_LOG_FORMAT: str = "%(asctime)s | %(name)s | %(levelname)-8s | %(message)s"
[docs]
def get_logger(name: str) -> logging.Logger:
"""Return a logger under the ``ncaa_eval`` hierarchy.
Args:
name: Dot-separated path appended to the root ``ncaa_eval`` logger
(e.g. ``"transform.features"`` yields ``ncaa_eval.transform.features``).
Returns:
A `logging.Logger` instance.
Example:
>>> log = get_logger("transform.features")
>>> log.info("Computing features for season %d", 2025)
"""
return logging.getLogger(f"{_ROOT_LOGGER_NAME}.{name}")