Quand Python prend ses responsabilités

Le GoF pattern comportemental « chaîne de responsabilité » (chain of responsibility) permet de mettre en place une chaîne d’objets et de faire transiter une requête par celle-ci. Les objets se passe la requête jusqu’à ce que l’un d’entre eux puisse y répondre.

Pour illustrer ce principe, nous nous pencherons sur la réalisation d’un système de journalisation (logging). Lors de l’écriture d’un programme on désire conserver un historique de certaines informations. L’idée est ici de définir le support de stockage d’une information selon son importance.

Nous définissons les niveaux d’importance des informations, et la classe Logger. Celle-ci propose une méthode log qui vérifie le niveau d’un message, le traite ou le passe au logger suivant. Le logger propose également une méthode perform qui se charge du traitement de la requête.

DEBUG, INFO, WARNING, ERROR, CRITICAL = 'Debug', 'Info', 'Warn', 'Error',
                                        'Critical'
 
class Logger:
 
    def __init__(self, masks, next):
        self.masks, self.next = masks, next
 
    def log(self, msg, mask):
        if mask in self.masks:
            self.perform(msg, mask)
        elif self.next:
            self.next.log(msg, mask)
        else:
            raise Exception('End of chain, "%s" was not performed' % msg)
 
    def perform(self, msg, mask):
        pass

Nous définissons ensuite trois logger spécifiques, un qui écrit le message sur la sortie standard, un qui l’écrit dans un fichier et un qui l’envoi par email au responsable de l’application (un serveur SMTP doit être disponible).

from smtplib import SMTP
 
class ShellLogger(Logger):
 
    def perform(self, msg, mask):
        print '%s: %s' % (mask, msg)
 
class FileLogger(Logger):
 
    def __init__(self, masks, next, path = 'log.txt'):
        Logger.__init__(self, masks, next)
        self.file = open(path, 'w')
 
    def perform(self, msg, mask):
        self.file.write('%s: %sn' % (mask, msg))
 
class MailLogger(Logger):
 
    def __init__(self, masks, next, smtp = 'localhost'):
        Logger.__init__(self, masks, next)
        self.server = SMTP(smtp)
 
    def perform(self, msg, mask):
        fro = 'appli@blabla.com'
        to = 'admin@blabla.com'
        msg = ("From: %srnTo: %srnrn%srn" % (fro, to, msg))
        self.server.sendmail(fro, to, msg)

Afin d’utiliser notre module de journalisation, il suffit de créer la chaîne des différents loggers en définissant les niveaux de message qu’ils prennent en comptes. On utilise alors systématiquement le premier pour journaliser les messages

mal = MailLogger(CRITICAL, None)
fil = FileLogger(ERROR, mal)
l = ShellLogger([DEBUG, INFO, WARNING], fil)
 
print 'nLogger testsn%s' % (12 * '-')
l.log('Cache problem', WARNING)
l.log('Test titi function', DEBUG)
l.log('Avoid divide by zero', ERROR)
l.log('Out of bound', ERROR)
l.log('Data successfully exported', INFO)
l.log('Crash system', CRITICAL)

Le résultat de ce test est la répartition des informations sur les supports concernés.

En console

En console

Dans le fichier log.txt

Dans le fichier log.txt

Dans un email adressé au responsable

Dans un email adressé au responsable

En conclusion, ce pattern permet un découplage intéressant des différents objets chargés du traitement d’une requête, ce principe est particulièrement utilisé dans les librairies graphiques pour traiter les événements déclenchés par l’utilisateur (ex: clic sur un bouton).

L’exemple ci-dessus reste une illustration pédagogique de la journalisation, d’ailleurs, comble pour un historique, il n’indique ni la date ni l’heure des événenements. Python propose un module logging bien plus complet.

Divers , Permalien.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

*

Vous pouvez utiliser ces balises et attributs HTML : <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="">