13 mai 2021

[Dynamo+=Python] Débogage - le module logging (partie 1/2 théorie)






Plutôt que des print(), une alternative .... les logs



Natif de la bibliothèque Python, le module logging permet de faire de la journalisation. Appliqué à un simple débogage ou à une journalisation centralisée à partir de différents serveurs, ce module logging est un outil très utile.

La création d'un logger s'effectue en plusieurs étapes

  • la création d'une instance
La création d'un logger peut s'effectuer de 3 façons : via un dictionnaire, via un fichier de configuration ou en définissant directement les propriétés

import logging
logger = logging.getLogger()

à savoir que le logger peut être nommé

import logging
logger = logging.getLogger("myProg")
Si cette méthode n'a jamais été appelée auparavant dans le script le logger est créé dans le cas contraire on récupère l'instance. 
Cela vous permet de récupérer celui-ci à un autre endroit, sauf dans le cas entre plusieurs nœuds Python (Dynamo) ou il faudra éventuellement se trimbaler la référence.


  • définition du niveau du logger

logger.setLevel(logging.DEBUG)
Par défaut, il existe 5 niveaux standard indiquant la gravité des événements. Chacun a une méthode correspondante qui peut être utilisée pour consigner les événements à ce niveau de gravité. Les niveaux définis, par ordre de gravité croissante, sont les suivants :


 

Niveau des messages pris en compte
Niveau
de journalisation
défini
Méthode pour définir le niveau DEBUG INFO WARNING ERROR CRITICAL Méthode pour logger dans ce niveau
DEBUG (10) loggerOrHandler.setLevel(logging.DEBUG) þ þ þ þ þ logger.debug('data in the list:' )
logger.debug(myList )
INFO (20) loggerOrHandler.setLevel(logging.INFO) ý þ þ þ þ logger.info('INFO ERROR')
WARNING (30) loggerOrHandler.setLevel(logging.WARNING) ý ý þ þ þ logger.warning('This is a warning')
ERROR (40) loggerOrHandler.setLevel(logging.ERROR) ý ý ý þ þ logger.error('This is a error :')
logger.error(traceback.format_exc())
#except Exception as ex:
logger.exception(ex) 
CRITICAL (50) loggerOrHandler.setLevel(logging.CRITICAL) ý ý ý ý þ logger.error('This is a critical error')
#except Exception as ex:
logger.exception(ex) 



  • configuration du formateur
   
 formatter = logging.Formatter('%(asctime)s :: %(levelname)s :: %(message)s')   
 
Plusieurs attributs sont disponibles 

Attribut formaté Description
%(asctime)s Date au format « AAAA-MM-JJ HH:MM:SS,xxx ». Remarquons que nous disposons d'une précision à la milliseconde
%(created)f Idem précédent, mais avec une date en timestamp (utilisation de time.time())
%(filename)s Nom du fichier ayant écrit dans le log
%(funcName)s Nom de la fonction contenant l'appel à l'écriture dans le log
%(levelname)s Niveau du message
%(levelno)s Numérotation logique du niveau du message (C:50, E:40, W:30, I:20, D:10)
%(lineno)d Ligne où trouver l'appel à écriture dans le log. Relatif au fichier d'appel
%(module)s Nom du module ayant appelé l'écriture dans le log
%(msecs)d Temps d'exécution depuis le lancement du programme en millisecondes
%(message)s Le message à logger
%(name)s Le nom de l'utilisateur courant
%(pathname)s Chemin absolu du fichier ayant appelé l'écriture
%(process)d Le numéro du process courant
%(processName)s Le nom du process courant
%(thread)d L'ID du thread courant
%(threadName)s Le nom du thread courant



  • création d'un handler

    Voici les principaux constructeurs de handlers
Logging handlers Description
StreamHandler envoi des messages aux flux (objets de type fichier)
Espace de Nom : logging
FileHandler envoi des messages à des fichiers sur le disque
Espace de Nom : logging
RotatingFileHandler envoi des messages à des fichiers sur le disque, si le fichier dépasse une certaine taille, renomme le fichier avec un compteur puis ecrit un nouveau fichier
Espace de Nom : logging.handlers
TimedRotatingFileHandler envoi des messages aux fichiers de disque, en permutant le fichier journal à intervalles réguliers.
Espace de Nom : logging.handlers
WatchedFileHandler surveillent le fichier sur lequel elles se connectent. Si le fichier change,
il est fermé et rouvert à l’aide du nom de fichier
Espace de Nom: logging.handlers
HTTPHandler envoi des messages à un serveur HTTP à l’aide de GET ou de POST
Espace de Nom : logging.handlers
MemoryHandler envoi des messages à un tampon en mémoire, qui est vidé chaque fois que des critères spécifiques sont remplis (transfert a un autre handler ex: SMTPHandler)
Espace de Nom : logging.handlers
NTEventLogHandler envoi des messages à un journal des événements Windows
Espace de Nommage : logging.handlers
SysLogHandler envoi des messages à un daemon (serveur) syslog
Espace de Nom : logging.handlers
SocketHandler envoi des messages aux connecteurs TCP/IP
Espace de Nom : logging.handlers
DatagramHandler envoi des messages aux connecteurs UDP
Espace de Nom : logging.handlers


exemple :
        
    import logging
    from logging.handlers import RotatingFileHandler
    from logging.handlers import SMTPHandler
    
    logger = logging.getLogger('MyTracker')
    # set root's level
    logger.setLevel(logging.DEBUG)
    formatter = logging.Formatter('%(asctime)s :: %(levelname)s :: %(message)s')
    #
    # create handler
    file_handler = RotatingFileHandler('activity.log', mode='a', maxBytes=1000000, backupCount=1)
    # set level on DEBUG for this file_handler
    file_handler.setLevel(logging.DEBUG)
    # set formatter 
    file_handler.setFormatter(formatter)
    # add this handler to logger
    logger.addHandler(file_handler)
    # add a second handler to logger with a different level
    mailer = SMTPHandler(mailhost='myhost',
                        fromaddr='myadress_mail',
                        toaddrs=['send_mail1', 'send_mail2', 'send_mail3'],
                        subject='MyTracker',
                        credentials=('username','password'),
                        secure=None)
    mailer.setLevel(logging.ERROR)
    logger.addHandler(mailer)
    

Note 1:
Les niveaux des handlers peuvent être différent que le niveau principal.  
  • si le handler n'a pas de niveau défini, c'est le niveau du logger qui est pris en compte
  • si le niveau d'un handler est inférieur au niveau du logger, c'est le niveau du logger qui devient prioritaire pour les messages (le setLevel() du handler ne sert a rien)
    ► on ne conserve que les messages associes au niveau du logger
  •  si le niveau d'un handler est supérieur au niveau du logger, on filtre un peu plus, c'est le niveau du handler qui devient prioritaire pour les messages  
    ► on ne conserve que les messages associes au niveau du 
    handler

Note 2:
Il existe 2 modes pour le FileHandler:
mode = 'a' → pour 'append' ajoute la suite du log existant
mode = 'w' → pour 'write' écrase l'ancien log et écrit de nouveau


  • on active ou on désactive le logger (optionnel)  
  
logger.disabled = False #or True

  • et enfin on print() on écrit nos logs

quelques exemples avec 2 handlers

tous les niveaux sont les mêmes (les setLevel() des handlers peuvent retirés)


ici le niveau du file_handler est supérieur au niveau du logger, on ne conserve que les messages associés a ce niveau


ici le niveau du stdout_handler est inférieur au niveau du logger, on ne conserve que les messages associés au niveau du logger


allez plutôt que de l'aspirine voici un petit logigramme en résumé 😀



voici la documentation complète sur le module logging


Maintenant que vous avez tout compris on passe prochainement à la pratique...

0 commentaires:

Enregistrer un commentaire