90 votes

Le format de journalisation de Python peut-il être modifié en fonction du niveau de journalisation des messages?

Je suis à l'aide de Python logging mécanisme de sortie pour imprimer à l'écran. Je pourrais le faire avec des instructions d'impression, mais je veux permettre à un plus à l'écoute de granularité pour l'utilisateur de désactiver certains types de sortie. J'aime le format imprimé pour les erreurs, mais préférez un format plus simple lorsque le niveau de sortie est "info".

Par exemple:

  logger.error("Running cmd failed")
  logger.info("Running cmd passed")

Dans cet exemple, je voudrais que le format de l'erreur afin d'être imprimé de manière différente:

# error
Aug 27, 2009 - ERROR: Running cmd failed
# info
Running cmd passed

Est-il possible d'avoir des formats différents pour les différents niveaux de journal, sans avoir de multiples objets de journalisation? Je préfère le faire sans modification de l'enregistreur une fois qu'il est créé puisqu'il y a un nombre élevé de if/else pour déterminer comment le produit doit être connecté.

91voto

JS. Points 1964

Je viens de rencontrer ce problème et j'ai eu du mal à combler les "trous" laissés dans l'exemple ci-dessus. Voici une version de travail plus complète que j'ai utilisée. Espérons que cela aide quelqu'un:

 # Custom formatter
class MyFormatter(logging.Formatter):

    err_fmt  = "ERROR: %(msg)s"
    dbg_fmt  = "DBG: %(module)s: %(lineno)d: %(msg)s"
    info_fmt = "%(msg)s"


    def __init__(self, fmt="%(levelno)s: %(msg)s"):
        logging.Formatter.__init__(self, fmt)


    def format(self, record):

        # Save the original format configured by the user
        # when the logger formatter was instantiated
        format_orig = self._fmt

        # Replace the original format with one customized by logging level
        if record.levelno == logging.DEBUG:
            self._fmt = MyFormatter.dbg_fmt

        elif record.levelno == logging.INFO:
            self._fmt = MyFormatter.info_fmt

        elif record.levelno == logging.ERROR:
            self._fmt = MyFormatter.err_fmt

        # Call the original formatter class to do the grunt work
        result = logging.Formatter.format(self, record)

        # Restore the original format configured by the user
        self._fmt = format_orig

        return result
 

Modifier:

Compliments de Halloleo, voici un exemple d'utilisation de ce qui précède dans votre script:

 fmt = MyFormatter()
hdlr = logging.StreamHandler(sys.stdout)

hdlr.setFormatter(fmt)
logging.root.addHandler(hdlr)
logging.root.setLevel(DEBUG)
 

32voto

Vinay Sajip Points 41286

Oui, vous pouvez le faire en ayant une classe personnalisée Formatter :

 class MyFormatter(logging.Formatter):
    def format(self, record):
        #compute s according to record.levelno
        #for example, by setting self._fmt
        #according to the levelno, then calling
        #the superclass to do the actual formatting
        return s
 

Attachez ensuite une instance MyFormatter à vos gestionnaires.

17voto

estani Points 1167

Et encore une fois, comme JS answer mais plus compact.

 class SpecialFormatter(logging.Formatter):
    FORMATS = {logging.DEBUG :"DBG: %(module)s: %(lineno)d: %(message)s",
               logging.ERROR : "ERROR: %(message)s",
               logging.INFO : "%(message)s",
               'DEFAULT' : "%(levelname)s: %(message)s"}

    def format(self, record):
        self._fmt = self.FORMATS.get(record.levelno, self.FORMATS['DEFAULT'])
        return logging.Formatter.format(self, record)

hdlr = logging.StreamHandler(sys.stderr)
hdlr.setFormatter(SpecialFormatter())
logging.root.addHandler(hdlr)
logging.root.setLevel(logging.INFO)
 

10voto

Evpok Points 1214

Ceci est une adaptation de la réponse de estani à la nouvelle implémentation de logging.Formatter qui s'appuie désormais sur des styles de formatage. Mine s'appuie sur le style '{' , mais il peut être adapté. Pourrait être affiné pour être plus général et permettre la sélection du style de formatage et des messages personnalisés comme arguments à __init__ également.

 class SpecialFormatter(logging.Formatter):
    FORMATS = {logging.DEBUG : logging._STYLES['{']("{module} DEBUG: {lineno}: {message}"),
           logging.ERROR : logging._STYLES['{']("{module} ERROR: {message}"),
           logging.INFO : logging._STYLES['{']("{module}: {message}"),
           'DEFAULT' : logging._STYLES['{']("{module}: {message}")}

    def format(self, record):
        # Ugly. Should be better
        self._style = self.FORMATS.get(record.levelno, self.FORMATS['DEFAULT'])
        return logging.Formatter.format(self, record)

hdlr = logging.StreamHandler(sys.stderr)
hdlr.setFormatter(SpecialFormatter())
logging.root.addHandler(hdlr)
logging.root.setLevel(logging.INFO)
 

6voto

user1837990 Points 6

La solution ci-dessus fonctionne avec 3.3.3 libération. Cependant, avec 3.3.4 vous obtenez l'erreur suivante.

FORMATS = { logging.DEBUG : logging._STYLES['{']("{module} DEBUG: {lineno}: {message}"),

TypeError: 'tuple' objet n'est pas appelable

Après quelques recherches dans la classe de log Lib\logging__init__.py J'ai trouvé que la structure de données a changé à partir 3.3.3 3.3.4 qui provoque le problème

3.3.3

_STYLES = {
    '%': PercentStyle,
    '{': StrFormatStyle,
    '$': StringTemplateStyle
}

3.3.4

_STYLES = {
   '%': (PercentStyle, BASIC_FORMAT),
   '{': (StrFormatStyle, '{levelname}:{name}:{message} AA'),
    '$': (StringTemplateStyle, '${levelname}:${name}:${message} BB'),
}

La solution mise à jour est donc

class SpecialFormatter(logging.Formatter):
     FORMATS = {logging.DEBUG : logging._STYLES['{'][0]("{module} DEBUG: {lineno}: {message}"),
       logging.ERROR : logging._STYLES['{'][0]("{module} ERROR: {message}"),
       logging.INFO : logging._STYLES['{'][0]("{module}: {message}"),
       'DEFAULT' : logging._STYLES['{'][0]("{module}: {message}")}

 def format(self, record):
    # Ugly. Should be better
    self._style = self.FORMATS.get(record.levelno, self.FORMATS['DEFAULT'])
    return logging.Formatter.format(self, record)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X