# --- FILE: gitsync_tool/logging_setup/logger_config.py --- import logging import os import sys # Per fallback print su stderr from typing import Dict, Optional # Aggiunti type hints # --- Costanti Condivise --- # Nome del file di log (potrebbe essere importato da un file di costanti globali se preferito) LOG_FILE: str = "git_sync_tool.log" # Formato standard per i messaggi di log scritti nel file LOG_FORMAT: str = ( "%(asctime)s - %(levelname)-8s - [%(name)s:%(funcName)s] - %(message)s" ) # Formato per data/ora nel file di log LOG_DATE_FORMAT: str = "%Y-%m-%d %H:%M:%S" # Mappa Livelli (Opzionale qui, ma utile se si usa get_log_level_name) LOG_LEVEL_MAP: Dict[int, str] = { logging.CRITICAL: "CRITICAL", logging.ERROR: "ERROR", logging.WARNING: "WARNING", logging.INFO: "INFO", logging.DEBUG: "DEBUG", logging.NOTSET: "NOTSET", } def get_log_level_name(level_number: int) -> str: """Restituisce il nome stringa leggibile per un livello di logging numerico.""" return LOG_LEVEL_MAP.get(level_number, f"LEVEL_{level_number}") def setup_file_logging(level: int = logging.INFO) -> None: """ Configures the root logger to write log messages to a file. Questa funzione aggiunge un FileHandler configurato al logger root di Python. Tutti i messaggi di log inviati tramite il modulo standard `logging` (con livello uguale o superiore a quello impostato qui e sul root logger) verranno scritti su questo file. Args: level (int): Il livello minimo di log da catturare e scrivere nel file (es. logging.INFO, logging.DEBUG). Default è logging.INFO. """ func_name: str = "setup_file_logging" # Nome per log interni (se necessario) try: # 1. Crea il Formatter per i messaggi nel file log_formatter = logging.Formatter(LOG_FORMAT, datefmt=LOG_DATE_FORMAT) # 2. Configura il File Handler # mode='a': Accoda i log al file esistente ad ogni avvio. # encoding='utf-8': Usa UTF-8 per supportare caratteri internazionali. log_file_path: str = os.path.abspath(LOG_FILE) # Ottieni percorso assoluto file_handler = logging.FileHandler(log_file_path, mode="a", encoding="utf-8") file_handler.setLevel(level) # Imposta il livello MINIMO per QUESTO handler file_handler.setFormatter(log_formatter) # Applica il formattatore # 3. Ottieni il Root Logger # Configurare il root logger è il modo standard per avere un logging # di base per l'intera applicazione. root_logger = logging.getLogger() # 4. (Opzionale ma Consigliato) Rimuovi handler file preesistenti per lo stesso file # Questo previene la duplicazione dei log se setup_file_logging # viene chiamato accidentalmente più volte. handler_removed: bool = False for handler in root_logger.handlers[:]: # Itera su una copia della lista if isinstance(handler, logging.FileHandler): # Controlla se l'handler punta allo stesso file # Nota: handler.baseFilename potrebbe essere None se non ancora usato try: # Confronta percorsi assoluti normalizzati current_handler_path = getattr(handler, "baseFilename", None) if current_handler_path and os.path.normcase( current_handler_path ) == os.path.normcase(log_file_path): root_logger.removeHandler(handler) handler.close() # Chiudi l'handler prima di rimuoverlo handler_removed = True print( f"INFO: Removed pre-existing file handler for: {log_file_path}" ) # Usa print qui except Exception as e_rm: # Logga avviso se il controllo/rimozione fallisce print( f"WARNING: Could not check/remove existing file handler: {e_rm}", file=sys.stderr, ) # 5. Aggiungi il nuovo File Handler al Root Logger root_logger.addHandler(file_handler) # 6. Imposta il Livello del Root Logger # Il root logger deve avere un livello uguale o *inferiore* a quello # dell'handler affinché i messaggi raggiungano l'handler. # Impostarlo al livello più basso tra quelli degli handler configurati # (o DEBUG se si vuole massima flessibilità) è una buona pratica. # Qui lo impostiamo almeno al livello richiesto per il file handler. if root_logger.level == logging.NOTSET or root_logger.level > level: root_logger.setLevel(level) print(f"INFO: Root logger level set to: {get_log_level_name(level)}") # 7. Log di Conferma (su console e nel file stesso) log_level_name: str = get_log_level_name(level) confirmation_message: str = ( f"File logging configured. Minimum level for file '{LOG_FILE}': {log_level_name}. " f"Path: {log_file_path}" ) print(f"INFO: {confirmation_message}") # Conferma su console # Scrive un messaggio nel file di log appena configurato logging.info(f"--- File logging initialized (Level: {log_level_name}) ---") if handler_removed: logging.warning( "Removed pre-existing file handler pointing to the same log file." ) except Exception as e: # Fallback critico in caso di errore nella configurazione del logging error_message: str = ( f"CRITICAL: Failed to set up file logging for '{LOG_FILE}': {e}" ) print(error_message, file=sys.stderr) # Tenta di usare basicConfig come ultima risorsa per loggare l'errore su console try: logging.basicConfig(level=logging.ERROR) logging.critical(error_message, exc_info=True) except Exception: pass # Ignora errori in basicConfig # Esempio di utilizzo (non eseguito quando importato) if __name__ == "__main__": print("Setting up file logging with DEBUG level...") setup_file_logging(level=logging.DEBUG) print(f"File logging configured. Check the file: '{LOG_FILE}'") # Esempi di log che dovrebbero apparire nel file logging.debug("This is a debug message for the file.") logging.info("This is an info message for the file.") logging.warning("This is a warning message for the file.") logging.error("This is an error message for the file.") # Puoi anche ottenere un logger specifico e usarlo test_logger = logging.getLogger("MyTestModule") test_logger.info("Message from test_logger.") # --- END OF FILE gitsync_tool/logging_setup/logger_config.py ---