Post Image

Guida Completa ai Formati del Log in Python: Configurazioni, Custom Formatter ed Esempi Pratici

Il logging è una componente cruciale nello sviluppo software, in particolare quando si tratta di monitorare e diagnosticare problemi nelle applicazioni. In Python, il modulo logging offre una struttura flessibile e potente per gestire i log. Nell'articolo Guida Completa al Logging in Python dalla Configurazione di Base alla Configurazione Avanzata ti abbiamo mostrato come configurare i logger in modo automatico o manuale, sottolineando i pregi e i difetti di ciascuna scelta. Nell'articolo Livelli di Log in Python: Tutto Quello che Devi Sapere per un Logging Efficace abbiamo mostrato come gestire i livelli di log per attribuire ad ogni messaggio un peso ed un ruolo.

Questo articolo affronta in dettaglio il tema dei formati di log disponibili in Python. L'articolo mostra come configurare i messaggi di log, come personalizzarli, e offre moltissimi esempi di utilizzo.

Opzioni di formattazione

La formattazione dei messaggi gioca un ruolo fondamentale nella personalizzazione dei file di log. A seconda del livello di granularità che vogliamo raggiungere, possiamo inserire più o meno informazioni all'interno del file di log. Ovviamente, non esiste una regola precisa da seguire, ma, a seconda della volontà del team di sviluppo, si può decidere che tipo di informazioni inserire.

Le informazioni all'interno dei file di log sono veicolate per mezzo dei placeholder. Un placeholder è una stringa così formata: %(NOME_PLACEHOLDER)s, dove NOME_PLACEHOLDER rappresenta il nome dell'informazione da stampare. Al posto della s finale, si può avere la d se il placeholder rappresenta un intero.

Il modulo logging di Python fornisce una moltitudine di opzioni che consentono la formattazione dei vari messaggi di log. Di seguito sono riportati gli esempi più utilizzati di placeholder che è possibile inserire nei formati dei log:

  • %(asctime)s: ora del log
  • %(name)s: nome del logger
  • %(levelname)s: livello di log
  • %(message)s: messaggio di log
  • %(filename)s: nome del file da cui è stato emesso il log
  • %(module)s: nome del modulo da cui è stato emesso il log
  • %(lineno)d: numero di riga nel file da cui è stato emesso il log
  • %(funcName)s: nome della funzione da cui è stato emesso il log
  • %(pathname)s: percorso completo del file
  • %(process)s: ID del processo
  • %(processName)s: nome del processo
  • %(thread)s: ID del thread
  • %(threadName)s: nome del thread

Esempi di log

I precedenti placeholder possono essere usati per comporre i messaggi di log che saranno visibili nei file, sulla console, etc. Sono innumerevoli le opzioni possibili per formattare i log, di seguito vedremo alcuni esempi.

Formato semplice

Il formato semplice prevede la stampa del solo messaggio di log.

formatter = logging.Formatter('%(message)s')
logger.info("messaggio")

L'output del formato è:

messaggio

Formato con Data e Ora

Aggiungiamo al formato semplice la stampa della Data e dell'ora.

formatter = logging.Formatter('%(asctime)s - %(message)s')
logger.info("messaggio con data e ora")

L'output del formato è:

2024-01-01 12:00:00,000 - messaggio con data e ora

Formato con livello di log

Possiamo aggiungere al formato con data e ora, il livello di log.

formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
logger.info("messaggio con data e ora e livello di log")

L'output del formato è:

2024-01-01 12:00:00,000 - INFO - messaggio con data e ora e livello di log

Formato completo

Come visto le combinazioni dei messaggi di log posso essere tantissime. Nella prossima stampa inseriamo tutte le opzioni elencate così da vedere che output producono.

formatter = logging.Formatter('''
    ASCTIME:%(asctime)s
    NAME:%(name)s
    LEVELNAME:%(levelname)s
    MESSAGE:%(message)s
    FILENAME:%(filename)s
    MODULE:%(module)s
    LINENO:%(lineno)d
    FUNCNAME%(funcName)s
    PATHNAME:%(pathname)s
    PROCESS:%(process)s
    PROCESSNAME:%(processName)s
    THREAD:%(thread)s
    THREADNAME:%(threadName)s
'''
)
logger.info("Messaggio completo")

L'output del formato è:

ASCTIME:2024-07-01 20:00:47,803
    NAME:root
    LEVELNAME:INFO
    MESSAGE:Messaggio completo
    FILENAME:opzioni_formattazione.py
    MODULE:opzioni_formattazione
    LINENO:43
    FUNCNAME<module>
    PATHNAME:/home/xm3ron/src/test_log_python/opzioni_formattazione.py
    PROCESS:104881
    PROCESSNAME:MainProcess
    THREAD:128862875727680
    THREADNAME:MainThread

Formatter personalizzati

Oltre ai formati predefiniti, è possibile creare formatter personalizzati estendendo la classe logging.Formatter. Questo permette di aggiungere logica personalizzata nella formattazione dei messaggi.

import logging

class CustomFormatter(logging.Formatter):
    def format(self, record):
        record.custom_attribute = 'Valore custom formatter'
        return super().format(record)

logger = logging.getLogger()
logger.setLevel(logging.DEBUG)

console_handler = logging.StreamHandler()
logger.addHandler(console_handler)

formatter = CustomFormatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s - %(custom_attribute)s')
console_handler.setFormatter(formatter)

logger.info('Messaggio con formatter personalizzato.')

Aggiunta di Attributi Personalizzabili

Puoi aggiungere attributi personalizzati al tuo log, come l'id dell'utente e la versione dell'app. Nell'esempio seguente sono inseriti proprio questi attributi.

import logging

class CustomFormatter(logging.Formatter):
    def format(self, record):
        record.user_id = '89'
        record.app_version = '1.2.0'
        return super().format(record)

logger = logging.getLogger('custom_logger')
logger.setLevel(logging.DEBUG)

# Crea un handler per la console
console_handler = logging.StreamHandler()
logger.addHandler(console_handler)

formatter = CustomFormatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s - User ID: %(user_id)s - App Version: %(app_version)s')
console_handler.setFormatter(formatter)

logger.info('Messaggio con attributi personalizzati.')

Formattazione condizionale

Puoi applicare diverse formattazioni al tuo log, a seconda del livello di log del messaggio, come segue.

import logging


class ConditionalFormatter(logging.Formatter):
    def format(self, record):
        if record.levelno == logging.ERROR:
            self._style._fmt = '%(asctime)s - %(name)s - %(levelname)s - %(message)s - ERROR OCCURRED'
        else:
            self._style._fmt = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
        return super().format(record)


logger = logging.getLogger('conditional_logger')
logger.setLevel(logging.DEBUG)

console_handler = logging.StreamHandler()
logger.addHandler(console_handler)

formatter = ConditionalFormatter()
console_handler.setFormatter(formatter)

logger.info('Messaggio informativo.')
logger.error('Messaggio di errore.')

Conclusioni

Il modulo logging di Python offre una vasta gamma di opzioni per configurare e personalizzare i messaggi di log. Dalla semplice configurazione di base a configurazioni avanzate con formatter personalizzati e file di configurazione, è possibile adattare il sistema di logging alle esigenze specifiche del progetto. Sperimenta con i vari placeholder e approcci per trovare la configurazione ottimale per il tuo contesto.

Spero che questo articolo ti abbia fornito una comprensione approfondita dei formati del log in Python e delle possibilità offerte dalla libreria logging. Buon lavoro con il logging!