Introduction
Cette section présente brièvement le module de journalisation.
This tutorial is from open-source community. Access the source code
Cette section présente brièvement le module de journalisation.
Le module logging est un module de la bibliothèque standard pour enregistrer des informations de diagnostic. C'est également un module très vaste avec beaucoup de fonctionnalités sophistiquées. Nous allons montrer un exemple simple pour illustrer son utilité.
Dans les exercices, nous avons écrit une fonction parse() qui ressemblait à ceci :
## fileparse.py
def parse(f, types=None, names=None, delimiter=None):
records = []
for line in f:
line = line.strip()
if not line: continue
try:
records.append(split(line,types,names,delimiter))
except ValueError as e:
print("Couldn't parse :", line)
print("Reason :", e)
return records
Prenons en compte l'énoncé try-except. Que devriez-vous faire dans le bloc except?
Devriez-vous afficher un message d'avertissement?
try:
records.append(split(line,types,names,delimiter))
except ValueError as e:
print("Couldn't parse :", line)
print("Reason :", e)
Ou l'ignorez-vous silencieusement?
try:
records.append(split(line,types,names,delimiter))
except ValueError as e:
pass
Aucune des solutions n'est satisfaisante car vous voulez souvent les deux comportements (sélectionnables par l'utilisateur).
Le module logging peut résoudre ce problème.
## fileparse.py
import logging
log = logging.getLogger(__name__)
def parse(f,types=None,names=None,delimiter=None):
...
try:
records.append(split(line,types,names,delimiter))
except ValueError as e:
log.warning("Couldn't parse : %s", line)
log.debug("Reason : %s", e)
Le code est modifié pour émettre des messages d'avertissement ou d'un objet Logger spécial. Celui créé avec logging.getLogger(__name__).
Créer un objet journal.
log = logging.getLogger(name) ## name est une chaîne de caractères
Émettre des messages de journal.
log.critical(message [, args])
log.error(message [, args])
log.warning(message [, args])
log.info(message [, args])
log.debug(message [, args])
Chacune des méthodes représente un niveau différent de gravité.
Toutes créent un message de journal formaté. args est utilisé avec l'opérateur % pour créer le message.
logmsg = message % args ## Écrit dans le journal
Le comportement de la journalisation est configuré séparément.
## main.py
...
if __name__ == '__main__':
import logging
logging.basicConfig(
filename = 'app.log', ## Fichier de sortie du journal
level = logging.INFO, ## Niveau de sortie
)
En général, il s'agit d'une configuration unique au démarrage du programme. La configuration est séparée du code qui effectue les appels de journalisation.
La journalisation est hautement configurable. Vous pouvez ajuster tous les aspects de celle-ci : fichiers de sortie, niveaux, formats de message, etc. Cependant, le code qui utilise la journalisation n'a pas à s'en préoccuper.
Dans fileparse.py, il y a une gestion d'erreurs liée aux exceptions causées par des entrées invalides. Elle ressemble à ceci :
## fileparse.py
import csv
def parse_csv(lines, select=None, types=None, has_headers=True, delimiter=',', silence_errors=False):
'''
Analyser un fichier CSV en une liste d'enregistrements avec conversion de type.
'''
if select and not has_headers:
raise RuntimeError('select nécessite des en-têtes de colonne')
rows = csv.reader(lines, delimiter=delimiter)
## Lire les en-têtes du fichier (s'il y en a)
headers = next(rows) if has_headers else []
## Si des colonnes spécifiques ont été sélectionnées, créer des indices pour le filtrage et définir les colonnes de sortie
if select:
indices = [ headers.index(colname) for colname in select ]
headers = select
records = []
for rowno, row in enumerate(rows, 1):
if not row: ## Ignorer les lignes sans données
continue
## Si des indices de colonne spécifiques sont sélectionnés, les extraire
if select:
row = [ row[index] for index in indices]
## Appliquer la conversion de type à la ligne
if types:
try:
row = [func(val) for func, val in zip(types, row)]
except ValueError as e:
if not silence_errors:
print(f"Ligne {rowno}: Impossible de convertir {row}")
print(f"Ligne {rowno}: Raison {e}")
continue
## Créer un dictionnaire ou un tuple
if headers:
record = dict(zip(headers, row))
else:
record = tuple(row)
records.append(record)
return records
Remarquez les instructions print qui émettent des messages de diagnostic. Remplacer ces print par des opérations de journalisation est relativement simple. Modifiez le code comme ceci :
## fileparse.py
import csv
import logging
log = logging.getLogger(__name__)
def parse_csv(lines, select=None, types=None, has_headers=True, delimiter=',', silence_errors=False):
'''
Analyser un fichier CSV en une liste d'enregistrements avec conversion de type.
'''
if select and not has_headers:
raise RuntimeError('select nécessite des en-têtes de colonne')
rows = csv.reader(lines, delimiter=delimiter)
## Lire les en-têtes du fichier (s'il y en a)
headers = next(rows) if has_headers else []
## Si des colonnes spécifiques ont été sélectionnées, créer des indices pour le filtrage et définir les colonnes de sortie
if select:
indices = [ headers.index(colname) for colname in select ]
headers = select
records = []
for rowno, row in enumerate(rows, 1):
if not row: ## Ignorer les lignes sans données
continue
## Si des indices de colonne spécifiques sont sélectionnés, les extraire
if select:
row = [ row[index] for index in indices]
## Appliquer la conversion de type à la ligne
if types:
try:
row = [func(val) for func, val in zip(types, row)]
except ValueError as e:
if not silence_errors:
log.warning("Ligne %d: Impossible de convertir %s", rowno, row)
log.debug("Ligne %d: Raison %s", rowno, e)
continue
## Créer un dictionnaire ou un tuple
if headers:
record = dict(zip(headers, row))
else:
record = tuple(row)
records.append(record)
return records
Maintenant que vous avez effectué ces modifications, essayez d'utiliser un peu de votre code avec des données invalides.
>>> import report
>>> a = report.read_portfolio('missing.csv')
Ligne 4: Mauvaise ligne : ['MSFT', '', '51.23']
Ligne 7: Mauvaise ligne : ['IBM', '', '70.44']
>>>
Si vous ne faites rien, vous ne recevrez que les messages de journalisation pour le niveau WARNING et supérieur. La sortie ressemblera à des instructions print simples. Cependant, si vous configurez le module de journalisation, vous recevrez des informations supplémentaires sur les niveaux de journalisation, le module, etc. Tapez ces étapes pour voir cela :
>>> import logging
>>> logging.basicConfig()
>>> a = report.read_portfolio('missing.csv')
AVERTISSEMENT:fileparse:Ligne 4: Mauvaise ligne : ['MSFT', '', '51.23']
AVERTISSEMENT:fileparse:Ligne 7: Mauvaise ligne : ['IBM', '', '70.44']
>>>
Vous remarquerez que vous ne voyez pas la sortie de l'opération log.debug(). Tapez ceci pour changer le niveau.
>>> logging.getLogger('fileparse').setLevel(logging.DEBUG)
>>> a = report.read_portfolio('missing.csv')
AVERTISSEMENT:fileparse:Ligne 4: Mauvaise ligne : ['MSFT', '', '51.23']
DEBUG:fileparse:Ligne 4: Raison : valeur littérale invalide pour int() avec base 10 : ''
AVERTISSEMENT:fileparse:Ligne 7: Mauvaise ligne : ['IBM', '', '70.44']
DEBUG:fileparse:Ligne 7: Raison : valeur littérale invalide pour int() avec base 10 : ''
>>>
Désactivez tous les messages de journalisation, sauf les plus critiques :
>>> logging.getLogger('fileparse').setLevel(logging.CRITIQUE)
>>> a = report.read_portfolio('missing.csv')
>>>
Pour ajouter la journalisation à une application, vous avez besoin d'un mécanisme pour initialiser le module de journalisation dans le module principal. Une manière de faire cela consiste à inclure du code de configuration qui ressemble à ceci :
## Ce fichier configure de base le module de journalisation.
## Changez les paramètres ici pour ajuster la sortie de journalisation selon vos besoins.
import logging
logging.basicConfig(
filename = 'app.log', ## Nom du fichier de journal (omettez pour utiliser stderr)
filemode = 'w', ## Mode d'ouverture du fichier (utilisez 'a' pour ajouter)
level = logging.WARNING, ## Niveau de journalisation (DEBUG, INFO, WARNING, ERROR ou CRITICAL)
)
Encore une fois, vous devriez placer ce code quelque part dans les étapes de démarrage de votre programme. Par exemple, où placeriez-vous ce code dans votre programme report.py?
Félicitations ! Vous avez terminé le laboratoire sur la journalisation. Vous pouvez pratiquer d'autres laboratoires sur LabEx pour améliorer vos compétences.