91 votes

Enregistrement des données variables avec une nouvelle chaîne de format

J'utilise la fonction de journalisation pour python 2.7.3. La documentation pour cette version de Python dit :

le paquet de journalisation est antérieur aux nouvelles options de formatage telles que str.format() et string.Template. Ces nouvelles options de formatage sont prises en charge...

J'aime le format "nouveau" avec des accolades. J'essaie donc de faire quelque chose comme.. :

 log = logging.getLogger("some.logger")
 log.debug("format this message {0}", 1)

Et j'obtiens une erreur :

TypeError : tous les arguments n'ont pas été convertis lors du formatage des chaînes de caractères.

Qu'est-ce que je rate ici ?

P.S. Je ne veux pas utiliser

log.debug("format this message {0}".format(1))

car dans ce cas, le message est toujours formaté, quel que soit le niveau de journalisation.

31voto

Felipe Points 626

Voici une autre option qui ne présente pas les problèmes de mots-clés mentionnés dans la réponse de Dunes. Elle ne peut traiter que les mots-clés positionnels ( {0} ) et non des mots-clés ( {foo} ). Elle ne nécessite pas non plus deux appels au format (en utilisant le trait de soulignement). Il possède le facteur de sécurité de la sous-classification de str :

class BraceString(str):
    def __mod__(self, other):
        return self.format(*other)
    def __str__(self):
        return self

class StyleAdapter(logging.LoggerAdapter):

    def __init__(self, logger, extra=None):
        super(StyleAdapter, self).__init__(logger, extra)

    def process(self, msg, kwargs):
        if kwargs.pop('style', "%") == "{":  # optional
            msg = BraceString(msg)
        return msg, kwargs

Vous l'utilisez comme ça :

logger = StyleAdapter(logging.getLogger(__name__))
logger.info("knights:{0}", "ni", style="{")
logger.info("knights:{}", "shrubbery", style="{")

Bien sûr, vous pouvez supprimer le contrôle noté avec # optional pour forcer tous les messages passant par l'adaptateur à utiliser le nouveau style de formatage.


Note pour ceux qui lisent cette réponse des années plus tard : En commençant par Python 3.2 vous pouvez utiliser le paramètre de style con Formatter objets :

La journalisation (à partir de la version 3.2) offre un support amélioré pour ces deux styles de formatage supplémentaires. La classe Formatter a été améliorée pour prendre un paramètre supplémentaire, facultatif, nommé "mot-clé". style . Ce site par défaut '%' mais d'autres valeurs possibles sont '{' y '$' qui correspondent à th styles de mise en forme. La rétrocompatibilité est maintenue par défaut (comme on pourrait s'y attendre), mais par spécifiant explicitement un paramètre de style, vous avez la possibilité de spécifier des chaînes de formatage qui fonctionnent avec str.format() o string.Template .

La documentation fournit l'exemple suivant logging.Formatter('{asctime} {name} {levelname:8s} {message}', style='{')

Notez que dans ce cas, vous ne pouvez toujours pas appeler la fonction logger avec le nouveau format. Par exemple, ce qui suit ne fonctionnera toujours pas :

logger.info("knights:{say}", say="ni")  # Doesn't work!
logger.info("knights:{0}", "ni")  # Doesn't work either

24voto

Thomas Orozco Points 15280

La solution la plus simple serait d'utiliser l'option excellent logbook module

import logbook
import sys

logbook.StreamHandler(sys.stdout).push_application()
logbook.debug('Format this message {k}', k=1)

Ou le plus complet :

>>> import logbook
>>> import sys
>>> logbook.StreamHandler(sys.stdout).push_application()
>>> log = logbook.Logger('MyLog')
>>> log.debug('Format this message {k}', k=1)
[2017-05-06 21:46:52.578329] DEBUG: MyLog: Format this message 1

12voto

pR0Ps Points 502

MISE À JOUR : il existe désormais un paquet sur PyPI appelé bracelogger qui met en œuvre la solution détaillée ci-dessous.


Afin d'activer le formatage en accolade des messages du journal, nous pouvons modifier un peu le code du journal.

Le texte suivant corrige les logging pour créer un get_logger qui renvoie un enregistreur qui utilise le nouveau style de formatage pour chaque enregistrement qu'il traite.

import functools
import logging
import types

def _get_message(record):
    """Replacement for logging.LogRecord.getMessage
    that uses the new-style string formatting for
    its messages"""
    msg = str(record.msg)
    args = record.args
    if args:
        if not isinstance(args, tuple):
            args = (args,)
        msg = msg.format(*args)
    return msg

def _handle_wrap(fcn):
    """Wrap the handle function to replace the passed in
    record's getMessage function before calling handle"""
    @functools.wraps(fcn)
    def handle(record):
        record.getMessage = types.MethodType(_get_message, record)
        return fcn(record)
    return handle

def get_logger(name=None):
    """Get a logger instance that uses new-style string formatting"""
    log = logging.getLogger(name)
    if not hasattr(log, "_newstyle"):
        log.handle = _handle_wrap(log.handle)
    log._newstyle = True
    return log

Utilisation :

>>> log = get_logger()
>>> log.warning("{!r}", log)
<logging.RootLogger object at 0x4985a4d3987b>

Notes :

  • Entièrement compatible avec les méthodes d'enregistrement normales (remplacez simplement logging.getLogger con get_logger )
  • N'affectera que les enregistreurs spécifiques créés par l'option get_logger (ne casse pas les paquets des tiers).
  • Le formatage du message est retardé jusqu'à sa sortie (ou pas du tout si le message du journal est filtré).
  • Les arguments sont stockés sur logging.LogRecord comme d'habitude (utile dans certains cas avec des gestionnaires de journaux personnalisés).
  • Fonctionne dans toutes les versions de Python de 2.7 à 3.10.

2voto

nexcvon Points 67

Essayez logging.setLogRecordFactory dans Python 3.2+ :

import collections
import logging

class _LogRecord(logging.LogRecord):

    def getMessage(self):
        msg = str(self.msg)
        if self.args:
            if isinstance(self.args, collections.Mapping):
                msg = msg.format(**self.args)
            else:
                msg = msg.format(*self.args)
        return msg

logging.setLogRecordFactory(_LogRecord)

1voto

Gringo Suave Points 5985

J'ai créé un formateur personnalisé, appelé ColorFormatter qui gère le problème comme ceci :

class ColorFormatter(logging.Formatter):

    def format(self, record):
        # previous stuff, copy from logging.py…

        try:  # Allow {} style
            message = record.getMessage()  # printf
        except TypeError:
            message = record.msg.format(*record.args)

        # later stuff…

Cela lui permet d'être compatible avec diverses bibliothèques. L'inconvénient est qu'elle n'est probablement pas performante, car elle peut tenter de formater la chaîne deux fois.

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