"""Logging setup module for DaemonControl. This module provides logging configuration with file rotation support for both daemon logs and job execution logs. """ import logging from logging.handlers import TimedRotatingFileHandler from pathlib import Path from typing import Optional def setup_logger( name: str, log_file: Optional[str] = None, level: str = "INFO" ) -> logging.Logger: """Setup logger with rotation. Creates a logger with optional file output and console output. File logs use daily rotation for manageability. Args: name: Logger name (typically module or component name) log_file: Path to log file (if None, console only) level: Log level (DEBUG, INFO, WARNING, ERROR, CRITICAL) Returns: Configured logger instance Example: >>> logger = setup_logger('daemon', '/var/log/daemon.log', 'INFO') >>> logger.info('Daemon started') """ # Create logger logger = logging.getLogger(name) # Convert string level to logging constant numeric_level = getattr(logging, level.upper(), logging.INFO) logger.setLevel(numeric_level) # Avoid adding duplicate handlers if logger already configured if logger.handlers: return logger # Create formatter formatter = logging.Formatter( '%(asctime)s | %(levelname)-8s | %(name)s:%(funcName)s:%(lineno)d | %(message)s', datefmt='%Y-%m-%d %H:%M:%S' ) # Console handler (always present) console_handler = logging.StreamHandler() console_handler.setLevel(numeric_level) console_handler.setFormatter(formatter) logger.addHandler(console_handler) # File handler (optional) if log_file: # Create log directory if it doesn't exist log_path = Path(log_file) log_path.parent.mkdir(parents=True, exist_ok=True) # Use TimedRotatingFileHandler for daily rotation file_handler = TimedRotatingFileHandler( filename=log_file, when='midnight', interval=1, backupCount=30, # Keep 30 days of logs encoding='utf-8' ) file_handler.setLevel(numeric_level) file_handler.setFormatter(formatter) logger.addHandler(file_handler) # Prevent propagation to root logger logger.propagate = False return logger