SXXXXXXX_GitUtility/gitutility/logging_setup/logger_config.py
2025-05-05 10:28:19 +02:00

151 lines
6.7 KiB
Python

# --- 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 ---