Meine aktuelle Formatzeichenfolge lautet:

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

Und ich möchte ein neues Feld namens app_name hinzufügen, das in jedem Skript, das diesen Formatierer enthält, einen anderen Wert hat.

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

Ich bin mir jedoch nicht sicher, wie ich diesen app_name -Wert an den Logger übergeben soll, um ihn in die Formatzeichenfolge zu interpolieren. Ich kann es natürlich dazu bringen, dass es in der Protokollnachricht erscheint, aber es jedes Mal weitergibt, aber das ist chaotisch.

Ich habe es versucht:

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

Aber keine funktioniert.

100
nickponline 10 Juli 2013 im 01:26

5 Antworten

Beste Antwort

Sie können einen LoggerAdapter verwenden, damit Sie nicht bestehen müssen die zusätzlichen Informationen bei jedem Protokollierungsaufruf:

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')

Protokolle (so etwas wie)

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

Filter können auch verwendet werden, um Kontextinformationen hinzuzufügen.

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')

Erzeugt einen ähnlichen Protokolldatensatz.

109
rouble 30 Mai 2018 im 21:24

Python3

Ab Python3.2 können Sie jetzt LogRecordFactory verwenden

>>> import logging
>>> logging.basicConfig(format="%(custom_attribute)s - %(message)s")
>>> old_factory = logging.getLogRecordFactory()
>>> def record_factory(*args, **kwargs):
        record = old_factory(*args, **kwargs)
        record.custom_attribute = "my-attr"
        return record

>>> logging.setLogRecordFactory(record_factory)
>>> logging.info("hello")
my-attr - hello

Natürlich kann record_factory so angepasst werden, dass es aufrufbar ist, und der Wert von custom_attribute kann aktualisiert werden, wenn Sie einen Verweis auf die ab Werk aufrufbare Funktion beibehalten.

Warum ist das besser als die Verwendung von Adaptern / Filtern?

  • Sie müssen Ihren Logger nicht an die Anwendung weitergeben
  • Es funktioniert tatsächlich mit Bibliotheken von Drittanbietern, die ihren eigenen Logger verwenden (indem sie einfach logger = logging.getLogger(..) aufrufen) und jetzt dasselbe Protokollformat haben. (Dies ist nicht der Fall bei Filtern / Adaptern, bei denen Sie dasselbe Logger-Objekt verwenden müssen.)
  • Sie können mehrere Fabriken stapeln / verketten
10
Ahmad 6 Sept. 2019 im 10:39

Eine andere Möglichkeit besteht darin, einen benutzerdefinierten LoggerAdapter zu erstellen. Dies ist besonders nützlich, wenn Sie das Format nicht ändern können ODER wenn Ihr Format für Code freigegeben ist, der den eindeutigen Schlüssel nicht sendet (in Ihrem Fall Anwendungsname ):

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

Und in Ihrem Code würden Sie Ihren Logger wie gewohnt erstellen und initialisieren:

    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)

Schließlich würden Sie den Wrapper-Adapter erstellen, um nach Bedarf ein Präfix hinzuzufügen:

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

Die Ausgabe sieht ungefähr so aus:

2013-07-09 17:39:33,596 [myapp] The world bores you when you are cool.
8
rouble 31 Mai 2018 im 16:01

Mit der Antwort von mr2ert habe ich diese komfortable Lösung gefunden (obwohl ich denke, dass sie nicht empfohlen wird) - Überschreiben Sie die integrierten Protokollierungsmethoden, um das benutzerdefinierte Argument zu akzeptieren und das extra -Wörterbuch innerhalb der Methoden zu erstellen:

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')

Ausgabe:

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

Dies ist die eingebaute Referenzfunktion:

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. 2 März 2019 im 18:26

Sie müssen das Diktat als Parameter an extra übergeben, um dies auf diese Weise zu tun.

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

Beweis:

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

Wenn Sie versuchen, eine Nachricht zu protokollieren, ohne das Diktat zu übergeben, schlägt dies fehl.

>>> 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
46
mr2ert 24 März 2015 im 22:44