147 lines
5.2 KiB
Python
147 lines
5.2 KiB
Python
# --- START OF FILE log_handler.py ---
|
|
|
|
import queue
|
|
import logging
|
|
import datetime
|
|
import traceback # Per log_exception
|
|
import sys # Per log_exception su stderr
|
|
|
|
# Coda condivisa thread-safe per i messaggi di log
|
|
# Usiamo una Queue standard, che è illimitata per default.
|
|
# Se si teme un consumo eccessivo di memoria in scenari estremi,
|
|
# si potrebbe usare maxsize, ma di solito non è necessario per i log.
|
|
log_queue = queue.Queue()
|
|
|
|
# Mappa livelli di logging numerici a nomi stringa per chiarezza nell'output
|
|
# Ricalca quella definita (ora rimossa) da logger_config.py
|
|
LOG_LEVEL_MAP = {
|
|
logging.CRITICAL: "CRITICAL",
|
|
logging.ERROR: "ERROR",
|
|
logging.WARNING: "WARNING",
|
|
logging.INFO: "INFO",
|
|
logging.DEBUG: "DEBUG",
|
|
logging.NOTSET: "NOTSET",
|
|
}
|
|
|
|
|
|
def get_log_level_name(level_no):
|
|
"""Restituisce il nome stringa leggibile per un livello di logging numerico."""
|
|
# Usa la mappa, con un fallback per livelli non standard
|
|
return LOG_LEVEL_MAP.get(level_no, f"LEVEL_{level_no}")
|
|
|
|
|
|
def _format_log_message(level, message, func_name):
|
|
"""
|
|
Funzione interna per formattare il messaggio di log.
|
|
Separa la formattazione dalla logica di accodamento.
|
|
"""
|
|
timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
level_name = get_log_level_name(level)
|
|
# Includi il nome della funzione solo se fornito
|
|
func_part = f" [{func_name}]" if func_name else ""
|
|
# Formato standard: Timestamp - LEVEL - [FuncName] - Messaggio
|
|
formatted_message = f"{timestamp} - {level_name} -{func_part} - {message}"
|
|
return formatted_message
|
|
|
|
|
|
def log_message(level, message, func_name=""):
|
|
"""
|
|
Formatta un messaggio di log e lo mette nella coda condivisa 'log_queue'.
|
|
|
|
Args:
|
|
level (int): Il livello di logging numerico (es. logging.INFO).
|
|
message (str): Il messaggio da loggare.
|
|
func_name (str, optional): Nome della funzione chiamante (per contesto).
|
|
"""
|
|
# Formatta il messaggio
|
|
formatted_message = _format_log_message(level, message, func_name)
|
|
|
|
# Crea il dizionario da mettere in coda
|
|
# Include sia il messaggio formattato che il livello numerico originale
|
|
# (utile per il processore della coda per filtrare o colorare).
|
|
log_entry = {"level": level, "message": formatted_message}
|
|
|
|
try:
|
|
# Metti l'entry nella coda senza bloccare (put_nowait)
|
|
log_queue.put_nowait(log_entry)
|
|
except queue.Full:
|
|
# Gestione fallback se la coda fosse piena (improbabile)
|
|
# Stampa direttamente su stderr per non perdere il messaggio critico
|
|
print(
|
|
f"!!! LOG QUEUE FULL - Dropping message: {formatted_message}",
|
|
file=sys.stderr,
|
|
)
|
|
except Exception as e:
|
|
# Gestione di altri errori imprevisti durante l'accodamento
|
|
print(
|
|
f"!!! ERROR putting message in log queue: {e} - Message: {formatted_message}",
|
|
file=sys.stderr,
|
|
)
|
|
|
|
|
|
# --- Funzioni Helper per Livelli Specifici ---
|
|
# Queste rendono il codice chiamante più pulito (es. log_handler.log_info(...) )
|
|
|
|
|
|
def log_debug(message, func_name=""):
|
|
"""Invia un messaggio di livello DEBUG alla coda log."""
|
|
log_message(logging.DEBUG, message, func_name)
|
|
|
|
|
|
def log_info(message, func_name=""):
|
|
"""Invia un messaggio di livello INFO alla coda log."""
|
|
log_message(logging.INFO, message, func_name)
|
|
|
|
|
|
def log_warning(message, func_name=""):
|
|
"""Invia un messaggio di livello WARNING alla coda log."""
|
|
log_message(logging.WARNING, message, func_name)
|
|
|
|
|
|
def log_error(message, func_name=""):
|
|
"""Invia un messaggio di livello ERROR alla coda log."""
|
|
log_message(logging.ERROR, message, func_name)
|
|
|
|
|
|
def log_critical(message, func_name=""):
|
|
"""Invia un messaggio di livello CRITICAL alla coda log."""
|
|
log_message(logging.CRITICAL, message, func_name)
|
|
|
|
|
|
def log_exception(message, func_name=""):
|
|
"""
|
|
Invia un messaggio di livello ERROR per un'eccezione e stampa
|
|
il traceback sulla console/stderr.
|
|
"""
|
|
# Logga un messaggio generico nella coda, indicando di controllare altrove
|
|
error_msg = f"{message} (See console/stderr for traceback)"
|
|
log_message(logging.ERROR, error_msg, func_name)
|
|
|
|
# Stampa il traceback completo su stderr per diagnostica immediata
|
|
# Questo NON va nella coda log GUI/File, solo sulla console da cui è
|
|
# stata lanciata l'applicazione (o dove viene reindirizzato stderr).
|
|
print(f"--- EXCEPTION TRACEBACK ({func_name}) ---", file=sys.stderr)
|
|
traceback.print_exc(file=sys.stderr)
|
|
print(f"--- END TRACEBACK ---", file=sys.stderr)
|
|
|
|
|
|
# Esempio di utilizzo (non eseguito quando importato):
|
|
if __name__ == "__main__":
|
|
print("Esempio di utilizzo del log_handler (messaggi vanno in coda):")
|
|
log_info("Questo è un messaggio informativo.", func_name="esempio")
|
|
log_debug("Questo è un messaggio di debug.", func_name="esempio")
|
|
try:
|
|
x = 1 / 0
|
|
except ZeroDivisionError:
|
|
log_exception(
|
|
"Si è verificato un errore durante il calcolo.", func_name="esempio_errore"
|
|
)
|
|
|
|
print(f"Dimensione attuale della coda log: {log_queue.qsize()}")
|
|
print("Recupero messaggi dalla coda:")
|
|
while not log_queue.empty():
|
|
entry = log_queue.get()
|
|
print(f" Livello: {entry['level']}, Messaggio: {entry['message']}")
|
|
|
|
# --- END OF FILE log_handler.py ---
|