"""Structured logging configuration with rotation support.""" import logging import sys from logging.handlers import RotatingFileHandler from pathlib import Path from typing import Optional from pythonjsonlogger import jsonlogger from writing_studio.core.config import settings class CustomJsonFormatter(jsonlogger.JsonFormatter): """Custom JSON formatter with additional fields.""" def add_fields(self, log_record: dict, record: logging.LogRecord, message_dict: dict) -> None: """Add custom fields to log records.""" super().add_fields(log_record, record, message_dict) log_record["level"] = record.levelname log_record["logger"] = record.name log_record["app"] = settings.app_name log_record["environment"] = settings.environment def setup_logging( name: Optional[str] = None, level: Optional[str] = None, log_file: Optional[str] = None, ) -> logging.Logger: """ Configure structured logging with file rotation. Args: name: Logger name (default: root logger) level: Log level (default: from settings) log_file: Log file path (default: from settings) Returns: Configured logger instance """ logger = logging.getLogger(name) logger.setLevel(level or settings.log_level) logger.handlers.clear() # Console handler console_handler = logging.StreamHandler(sys.stdout) console_handler.setLevel(level or settings.log_level) # Format based on settings if settings.log_format == "json": formatter = CustomJsonFormatter( "%(timestamp)s %(level)s %(name)s %(message)s", rename_fields={"timestamp": "asctime"}, ) else: formatter = logging.Formatter( "%(asctime)s - %(name)s - %(levelname)s - %(message)s", datefmt="%Y-%m-%d %H:%M:%S", ) console_handler.setFormatter(formatter) logger.addHandler(console_handler) # File handler with rotation log_file_path = log_file or settings.log_file_path if log_file_path: # Ensure log directory exists Path(log_file_path).parent.mkdir(parents=True, exist_ok=True) file_handler = RotatingFileHandler( log_file_path, maxBytes=settings.log_max_bytes, backupCount=settings.log_backup_count, ) file_handler.setLevel(level or settings.log_level) file_handler.setFormatter(formatter) logger.addHandler(file_handler) # Prevent propagation to avoid duplicate logs logger.propagate = False return logger # Global logger instance logger = setup_logging("writing_studio")