Source code for src.utils.logging

"""Structured logging configuration for Share of Search analysis."""

import logging
import sys
from pathlib import Path
from datetime import datetime
from typing import Optional


[docs] class ColoredFormatter(logging.Formatter): """Custom formatter with colored output for console.""" COLORS = { 'DEBUG': '\033[36m', # Cyan 'INFO': '\033[32m', # Green 'WARNING': '\033[33m', # Yellow 'ERROR': '\033[31m', # Red 'CRITICAL': '\033[35m', # Magenta } RESET = '\033[0m' ICONS = { 'DEBUG': '🔍', 'INFO': 'â„šī¸ ', 'WARNING': 'âš ī¸ ', 'ERROR': '❌', 'CRITICAL': 'đŸ”Ĩ', }
[docs] def format(self, record): # Add color and icon levelname = record.levelname if levelname in self.COLORS: record.levelname = f"{self.ICONS.get(levelname, '')} {levelname}" record.msg = f"{self.COLORS[levelname]}{record.msg}{self.RESET}" return super().format(record)
[docs] def setup_logging( log_level: str = "INFO", log_file: Optional[Path] = None, console: bool = True ) -> logging.Logger: """ Configure structured logging. Args: log_level: Logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL) log_file: Optional path to log file console: Whether to output to console Returns: Configured logger instance """ logger = logging.getLogger("share_of_search") logger.setLevel(getattr(logging, log_level.upper())) logger.handlers.clear() # Remove any existing handlers # Console handler with colors if console: console_handler = logging.StreamHandler(sys.stdout) console_handler.setLevel(getattr(logging, log_level.upper())) console_formatter = ColoredFormatter( fmt='[%(levelname)s] %(message)s', datefmt='%Y-%m-%d %H:%M:%S' ) console_handler.setFormatter(console_formatter) logger.addHandler(console_handler) # File handler without colors if log_file: log_file.parent.mkdir(parents=True, exist_ok=True) file_handler = logging.FileHandler(log_file, encoding='utf-8') file_handler.setLevel(logging.DEBUG) # Always log everything to file file_formatter = logging.Formatter( fmt='[%(asctime)s] [%(levelname)s] [%(name)s] %(message)s', datefmt='%Y-%m-%d %H:%M:%S' ) file_handler.setFormatter(file_formatter) logger.addHandler(file_handler) return logger
[docs] def get_logger(name: str = "") -> logging.Logger: """ Get logger instance in the share_of_search hierarchy. Args: name: Logger name (usually __name__ from calling module) Returns: Logger instance that's a child of the package logger """ base = logging.getLogger("share_of_search") return base.getChild(name) if name else base