Scopo | Riporta messaggi di stato, di errore ed informativi |
Versione Python | 2.3 |
A partire dal 1 gennaio 2021 le versioni 2.x di Python non sono piu' supportate. Ti invito a consultare la corrispondente versione 3.x dell'articolo per il modulo logging
Il modulo logging definisce una API standard per segnalare errori ed informazioni di stato da applicazioni e librerie. Il vantaggio chiave dell'avere l'API di logging fornita da un modulo di libreria standard è che tutti i moduli Python possono concorrere al logging, in modo che il log della propria applicazione possa includere messaggi da moduli di terze parti.
Ci sono due prospettive da cui esaminare i log. Gli sviluppatori dell'applicazione imposta il modulo logging , smistando i messaggi agli appropriati canali in uscita. E' possibile registrare messaggi con livelli di dettaglio diversi oppure verso destinazioni diverse. Gestori per la scrittura dei messaggi di log a file, ad indirizzi HTTP GET/POST, email tramite SMTP, socket generici oppure verso meccanismi di registrazione specifici di un sistema operativo sono tutti inclusi ed è possibile creare le proprie classi di destinazione del log se si hanno specifiche esigenze che non sono coperte dalle classi built-in.
La maggior parte delle applicazioni probabilmente vorranno eseguire il log ad un file. Si usa la funzione
basicConfig()
per impostare il gestore predefinito in modo che i messaggi di debug siano scritti ad un file.
import logging
LOG_FILENAME = 'logging_example.out'
logging.basicConfig(filename=LOG_FILENAME,
level=logging.DEBUG,
)
logging.debug('Questo messaggio dovrebbe andare nel file di log')
f = open(LOG_FILENAME, 'rt')
try:
body = f.read()
finally:
f.close()
print 'FILE:'
print body
Dopo l'esecuzione dello script il messaggio di log viene scritto in :
logging_file_example.py FILE: DEBUG:root:Questo messaggio dovrebbe andare nel file di log
Se si esgue lo script ripetutamente, i messaggi di log aggiuntivi sono accodati al file. Per creare ogni volta un nuovo file, si passa un parametro
filemode
a basicConfig() con il valore
'w'
. Piuttosto che gestire in prima persona la dimensione del file è più semplice usare
RotatingFileHandler
:
import glob
import logging
import logging.handlers
LOG_FILENAME = 'logging_rotatingfile_example.out'
# Impostazione di un logger specifico con il livello di output desiderato
my_logger = logging.getLogger('MyLogger')
my_logger.setLevel(logging.DEBUG)
# Aggiunta dell'handler dei messaggi di log al logger
handler = logging.handlers.RotatingFileHandler(LOG_FILENAME,
maxBytes=20,
backupCount=5,
)
my_logger.addHandler(handler)
# Registrazione di qualche messaggio
for i in range(20):
my_logger.debug('i = %d' % i)
# Visualizzazione dei file che sono stati creati
logfiles = glob.glob('%s*' % LOG_FILENAME)
for filename in logfiles:
print filename
Dovrebbero risultare 6 file separati, ognuno con una parte della storia delle registrazioni per l'applicazione:
$ python logging_rotatingfile_example.py logging_rotatingfile_example.out logging_rotatingfile_example.out.1 logging_rotatingfile_example.out.2 logging_rotatingfile_example.out.3 logging_rotatingfile_example.out.4 logging_rotatingfile_example.out.5
Il file con le registrazioni più recenti è sempre logging_rotatingfile_example.out , ed ogni volta che esso raggiunge il limite di dimensione viene rinominato con il suffisso .1. . Ognuno dei file di backup esistenti viene rinominato incrementando il suffisso ( .1. ) diventa .2. ecc.) mentre il file .5. viene eliminato.
Un'altra utile caratteristica dell'API di logging è la capacità di produrre diversi messaggi per diversi livelli di log. Questo consente di fornire al proprio codice messaggi di debug, ad esempio; ma abbassando il livello di dettaglio del log questi messaggi di debug non sono scritti nel proprio sistema in produzione.
Livello,Valore |
---|
CRITICAL,50 |
ERROR,40 |
WARNING,30 |
INFO,20 |
DEBUG,10 |
UNSET,0 |
Le chiamate di logger, handler e messaggi di log possono ognuna specificare un livello. Il messaggio di log viene emesso solamente se l'handler ed il logger sono configurati per l'emissione di messaggi di quel livello o di quelli superiori. Ad esempio, se un messaggio è CRITICAL, ed il logger è impostato ad ERROR, il messaggio viene emesso (50 > 40). Se un messaggio è un WARNING, ed il logger viene impostato per produrre solo ERRORi, il messaggio non viene emesso (30 < 40).
import logging
import sys
LEVELS = { 'debug':logging.DEBUG,
'info':logging.INFO,
'warning':logging.WARNING,
'error':logging.ERROR,
'critical':logging.CRITICAL,
}
if len(sys.argv) > 1:
level_name = sys.argv[1]
level = LEVELS.get(level_name, logging.NOTSET)
logging.basicConfig(level=level)
logging.debug('Questo è un messaggio di debug')
logging.info('Questo + un messaggio di informazione')
logging.warning('Questo è un messaggio di avvertimento')
logging.error('Questo è un messaggio di errore')
logging.critical('Questo è un messaggio di errore critico')
Si esegua lo script con un parametro tipo 'debug' oppure 'warning' per vedere quali messaggi vengono mostrati ai livelli diversi
$ python logging_level_example.py debug DEBUG:root:Questo è un messaggio di debug INFO:root:Questo è un messaggio di informazione WARNING:root:Questo è un messaggio di avvertimento ERROR:root:Questo è un messaggio di errore CRITICAL:root:Questo è un messaggio di errore critico
logging dovrebbe essere usato dagli sviluppatori di librerie piuttosto che di applicazioni. Per loro c'è perfino meno lavoro da fare. Occorre creare semplicemente una istanza di logger per ogni contesto, usando un nome appropriato, quindi registrare i messaggi usando i livelli standard. Fintanto che una libreria usa l'API di logging con consistente nomenclatura e selezione di livelli, l'applicazione può essere configurata per mostrare o nascondere i messaggi dalla libreria, a seconda delle necessità.
Si noterà che tutti i precedenti messaggi di log hanno 'root' incorporato in essi. Il modulo logging supporta una gerarchia di logger con nomi diversi. Un facile modo per determinare da dove provenga uno specifico messaggio di log è usare un oggetto logger separato per ciascuno modulo. Ogni nuovo logger eredita la configurazione del genitore, ed i messaggi di log inviati ad un logger includono il nome di quel logger. Opzionalmente, ogni logger può essere configurato diversamente, in modo che i messaggi dai diversi moduli vengono gestiti in diversi modi. Si osservi un semplice esempio di come eseguire il log da diversi moduli in modo da tracciare facilmente la sorgente dei messaggi:
import logging
logging.basicConfig(level=logging.WARNING)
logger1 = logging.getLogger('package1.module1')
logger2 = logging.getLogger('package2.module2')
logger1.warning('Questo messaggio proviene da un modulo')
logger2.warning('E questo messaggio proviene da un altro modulo')
E l'output:
$ python logging_modules_example.py WARNING:package1.module1:Questo messaggio proviene da un modulo WARNING:package2.module2:E questo messaggio proviene da un altro modulo
Ci sono moltissime altre opzioni per configurazre logging, incluse diverse opzioni di formattazione dei messaggi di log, la possibilità di invaire messaggi a destinazioni multiple, e la modifica della configurazione di un applicazione da tempo in esecuzione al volo usando un'interfaccia socket. Tutte queste opzioni sono trattate dettagliatamente nella documentazione della libreria del modulo.
Vedere anche: