Source code for udaan.utils.logging
"""Structured logging for Udaan library.
This module provides a consistent logging interface for the library.
Library users can configure logging through standard Python logging mechanisms.
Usage:
# In library code
from udaan.utils.logging import get_logger
logger = get_logger(__name__)
logger.info("Controller initialized")
# For users to enable logging output
from udaan.utils.logging import setup_logging
setup_logging() # Enable INFO level output
setup_logging(level=logging.DEBUG) # Enable debug output
"""
from __future__ import annotations
import logging
import sys
from typing import Any
# Create library root logger
_root_logger = logging.getLogger("udaan")
_root_logger.addHandler(logging.NullHandler()) # Don't force handler on library users
[docs]
def get_logger(name: str) -> logging.Logger:
"""Get a logger for the given module name.
Args:
name: Module name, typically __name__.
Returns:
Logger instance under the udaan namespace.
Example:
>>> logger = get_logger(__name__)
>>> logger.info("Message")
"""
# Ensure logger is under udaan namespace
if name.startswith("udaan."):
return logging.getLogger(name)
return logging.getLogger(f"udaan.{name}")
class _ColoredFormatter(logging.Formatter):
"""Formatter that adds ANSI colors to log level names."""
COLORS = {
logging.DEBUG: "\033[90m", # gray
logging.INFO: "\033[92m", # green
logging.WARNING: "\033[93m", # yellow
logging.ERROR: "\033[91m", # red
logging.CRITICAL: "\033[1;91m", # bold red
}
RESET = "\033[0m"
def format(self, record: logging.LogRecord) -> str:
color = self.COLORS.get(record.levelno, "")
record.levelname = f"{color}{record.levelname}{self.RESET}"
return super().format(record)
def _supports_color(stream: Any) -> bool:
"""Check if the output stream supports ANSI colors."""
return hasattr(stream, "isatty") and stream.isatty()
[docs]
def setup_logging(
level: int = logging.INFO,
format_string: str | None = None,
stream: Any = None,
color: bool | None = None,
) -> None:
"""Configure logging for interactive use.
Call this function to enable logging output from Udaan.
By default, the library produces no output (NullHandler).
Args:
level: Logging level (default: INFO).
format_string: Custom format string. If None, uses default format.
stream: Output stream (default: sys.stdout).
color: Enable colored output. None = auto-detect from terminal.
Example:
>>> import logging
>>> from udaan.utils.logging import setup_logging
>>> setup_logging() # Enable INFO level with colors
>>> setup_logging(level=logging.DEBUG) # Enable debug
"""
if format_string is None:
format_string = "[%(levelname)s] %(name)s: %(message)s"
if stream is None:
stream = sys.stdout
if color is None:
color = _supports_color(stream)
handler = logging.StreamHandler(stream)
if color:
handler.setFormatter(_ColoredFormatter(format_string))
else:
handler.setFormatter(logging.Formatter(format_string))
_root_logger.addHandler(handler)
_root_logger.setLevel(level)
[docs]
class LoggerMixin:
"""Mixin class providing logging capabilities to classes.
Inherit from this mixin to get a _logger property that returns
a logger named after the class.
Example:
>>> class MyController(LoggerMixin):
... def compute(self):
... self._logger.debug("Computing...")
"""
@property
def _logger(self) -> logging.Logger:
"""Get logger for this class instance."""
return logging.getLogger(f"udaan.{self.__class__.__module__}.{self.__class__.__name__}")
def _log_debug(self, msg: str, *args: Any, **kwargs: Any) -> None:
"""Log a debug message."""
self._logger.debug(msg, *args, **kwargs)
def _log_info(self, msg: str, *args: Any, **kwargs: Any) -> None:
"""Log an info message."""
self._logger.info(msg, *args, **kwargs)
def _log_warning(self, msg: str, *args: Any, **kwargs: Any) -> None:
"""Log a warning message."""
self._logger.warning(msg, *args, **kwargs)
def _log_error(self, msg: str, *args: Any, **kwargs: Any) -> None:
"""Log an error message."""
self._logger.error(msg, *args, **kwargs)