web-dev-qa-db-fra.com

Comment ajouter un champ personnalisé à Python chaîne de format de journal?

Ma chaîne de format actuelle est:

formatter = logging.Formatter('%(asctime)s : %(message)s')

et je veux ajouter un nouveau champ appelé app_name et qui aura une valeur différente dans chaque script qui contient ce formateur.

import logging
formatter = logging.Formatter('%(asctime)s %(app_name)s : %(message)s')
syslog.setFormatter(formatter)
logger.addHandler(syslog)

Mais je ne sais pas comment passer ça app_name valeur à l'enregistreur à interpoler dans la chaîne de format. Je peux évidemment le faire apparaître dans le message de journal mais en le passant à chaque fois mais c'est désordonné.

J'ai essayé:

logging.info('Log message', app_name='myapp')
logging.info('Log message', {'app_name', 'myapp'})
logging.info('Log message', 'myapp')

mais aucun ne fonctionne.

63
nickponline

Vous pouvez utiliser un LoggerAdapter pour ne pas avoir à transmettre les informations supplémentaires à chaque appel de journalisation:

import logging
extra = {'app_name':'Super App'}

logger = logging.getLogger(__name__)
syslog = logging.StreamHandler()
formatter = logging.Formatter('%(asctime)s %(app_name)s : %(message)s')
syslog.setFormatter(formatter)
logger.setLevel(logging.INFO)
logger.addHandler(syslog)

logger = logging.LoggerAdapter(logger, extra)
logger.info('The sky is so blue')

journaux (quelque chose comme)

2013-07-09 17:39:33,596 Super App : The sky is so blue

Filtres peut également être utilisé pour ajouter des informations contextuelles.

import logging

class AppFilter(logging.Filter):
    def filter(self, record):
        record.app_name = 'Super App'
        return True

logger = logging.getLogger(__name__)
logger.addFilter(AppFilter())
syslog = logging.StreamHandler()
formatter = logging.Formatter('%(asctime)s %(app_name)s : %(message)s')
syslog.setFormatter(formatter)
logger.setLevel(logging.INFO)
logger.addHandler(syslog)

logger.info('The sky is so blue')

produit un enregistrement de journal similaire.

94
unutbu

Vous devez passer le dict comme paramètre à extra pour le faire de cette façon.

logging.info('Log message', extra={'app_name': 'myapp'})

Preuve:

>>> import logging
>>> logging.basicConfig(format="%(foo)s - %(message)s")
>>> logging.warning('test', extra={'foo': 'bar'})
bar - test 

De plus, si vous essayez d'enregistrer un message sans passer le dict, il échouera.

>>> logging.warning('test')
Traceback (most recent call last):
  File "/usr/lib/python2.7/logging/__init__.py", line 846, in emit
    msg = self.format(record)
  File "/usr/lib/python2.7/logging/__init__.py", line 723, in format
    return fmt.format(record)
  File "/usr/lib/python2.7/logging/__init__.py", line 467, in format
    s = self._fmt % record.__dict__
KeyError: 'foo'
Logged from file <stdin>, line 1
39
mr2ert

Une autre façon consiste à créer un LoggerAdapter personnalisé. Ceci est particulièrement utile lorsque vous ne pouvez pas changer le format OR si votre format est partagé avec du code qui n'envoie pas la clé unique (dans votre cas nom_app):

class LoggerAdapter(logging.LoggerAdapter):
    def __init__(self, logger, prefix):
        super(LoggerAdapter, self).__init__(logger, {})
        self.prefix = prefix

    def process(self, msg, kwargs):
        return '[%s] %s' % (self.prefix, msg), kwargs

Et dans votre code, vous devez créer et initialiser votre enregistreur comme d'habitude:

    logger = logging.getLogger(__name__)
    # Add any custom handlers, formatters for this logger
    myHandler = logging.StreamHandler()
    myFormatter = logging.Formatter('%(asctime)s %(message)s')
    myHandler.setFormatter(myFormatter)
    logger.addHandler(myHandler)
    logger.setLevel(logging.INFO)

Enfin, vous devez créer l'adaptateur wrapper pour ajouter un préfixe selon les besoins:

    logger = LoggerAdapter(logger, 'myapp')
    logger.info('The world bores you when you are cool.')

La sortie ressemblera à ceci:

2013-07-09 17:39:33,596 [myapp] The world bores you when you are cool.
7
rouble

En utilisant la réponse de mr2ert, j'ai trouvé cette solution confortable (bien que je suppose que ce n'est pas recommandé) - Remplacez les méthodes de journalisation intégrées pour accepter l'argument personnalisé et créer le dictionnaire extra à l'intérieur des méthodes:

import logging

class CustomLogger(logging.Logger):

   def debug(self, msg, foo, *args, **kwargs):
       extra = {'foo': foo}

       if self.isEnabledFor(logging.DEBUG):
            self._log(logging.DEBUG, msg, args, extra=extra, **kwargs)

   *repeat for info, warning, etc*

logger = CustomLogger('CustomLogger', logging.DEBUG)
formatter = logging.Formatter('%(asctime)s [%(foo)s] %(message)s') 
handler = logging.StreamHandler()
handler.setFormatter(formatter) 
logger.addHandler(handler)

logger.debug('test', 'bar')

Sortie:

2019-03-02 20:06:51,998 [bar] test

Ceci est la fonction intégrée pour référence:

def debug(self, msg, *args, **kwargs):
    """
    Log 'msg % args' with severity 'DEBUG'.

    To pass exception information, use the keyword argument exc_info with
    a true value, e.g.

    logger.debug("Houston, we have a %s", "thorny problem", exc_info=1)
    """
    if self.isEnabledFor(DEBUG):
        self._log(DEBUG, msg, args, **kwargs)
0
Yaniv K.