J'utilise la fonction de journalisation pour python 2.7.3. La documentation de cette version Python dit :
le package de journalisation est antérieur aux nouvelles options de formatage telles que str.format () et string.Template. Ces nouvelles options de formatage sont supportées ...
J'aime le nouveau format avec des accolades. J'essaie donc de faire quelque chose comme:
log = logging.getLogger("some.logger")
log.debug("format this message {0}", 1)
Et obtenir une erreur:
TypeError: pas tous les arguments convertis lors du formatage de chaîne
Qu'est-ce qui me manque 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 l’enregistreur.
EDIT: jetez un oeil à l'approche StyleAdapter
dans la réponse de @Dunes contrairement à cette réponse; cela permet d'utiliser d'autres styles de formatage sans le passe-partout tout en appelant les méthodes de l'enregistreur (debug (), info (), error (), etc.).
À partir de la documentation - Utilisation de styles de formatage différents :
Les appels de journalisation (logger.debug (), logger.info () etc.) ne prennent que paramètres de position pour le message de consignation lui-même, avec paramètres de mot clé utilisés uniquement pour déterminer les options de traitement de l'appel de journalisation réel (par exemple, le paramètre mot clé exc_info à indique que les informations de suivi doivent être consignées, ou le paramètre mot clé supplémentaire pour indiquer que des informations contextuelles supplémentaires doivent être ajoutées au journal). Donc, vous ne pouvez pas directement passer des appels de journalisation en utilisant Syntaxe str.format () ou string.Template, car en interne la journalisation package utilise% -formatting pour fusionner la chaîne de format et la variable arguments. Il n’y aurait pas moyen de changer cela tout en préservant l’arrière compatibilité, puisque tous les appels de journalisation existant dans le code utilisera des chaînes% -format.
Et:
Cependant, il existe un moyen d'utiliser {} - et $ - le formatage en construisez vos messages de journal individuels. Rappelez-le pour un message vous pouvez utiliser un objet arbitraire comme chaîne de format de message, et que le Le paquet de journalisation appellera str () sur cet objet pour obtenir le .__ réel. chaîne de format.
Copier-coller dans le module wherever
:
class BraceMessage(object):
def __init__(self, fmt, *args, **kwargs):
self.fmt = fmt
self.args = args
self.kwargs = kwargs
def __str__(self):
return self.fmt.format(*self.args, **self.kwargs)
Ensuite:
from wherever import BraceMessage as __
log.debug(__('Message with {0} {name}', 2, name='placeholders'))
Remarque: le formatage réel est retardé jusqu'à ce qu'il soit nécessaire, par exemple, si les messages DEBUG ne sont pas enregistrés, le formatage n'est pas effectué du tout.
Voici une autre option qui ne présente pas les problèmes de mots clés mentionnés dans la réponse de Dunes. Il ne peut gérer que des arguments de position ({0}
) et non des arguments de mot clé ({foo}
). Il n’exige pas non plus que deux appels soient formatés (avec le trait de soulignement). Il a le facteur clé de sous-classement 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 ceci:
logger = StyleAdapter(logging.getLogger(__name__))
logger.info("knights:{0}", "ni", style="{")
logger.info("knights:{}", "shrubbery", style="{")
Bien sûr, vous pouvez supprimer la vérification notée avec # optional
pour forcer tous les messages de l'adaptateur à utiliser une mise en forme de nouveau style.
Note pour tous ceux qui liront cette réponse des années plus tard}: À partir de Python 3.2, vous pouvez utiliser le paramètre de style avec des objets Formatter
:
La journalisation (à partir de la version 3.2) offre un support amélioré pour ces deux styles de formatage supplémentaires. Le La classe de formatage a été améliorée pour prendre un paramètre de mot clé supplémentaire facultatif nommé
style
. Ce La valeur par défaut est'%'
, mais les autres valeurs possibles sont'{'
et'$'
, qui correspondent aux deux autres styles de mise en forme. La compatibilité ascendante est conservée par défaut (comme on peut s'y attendre), mais par en spécifiant explicitement un paramètre de style, vous avez la possibilité de spécifier des chaînes de format qui fonctionnent avecstr.format()
oustring.Template
.
La documentation fournit l'exemple logging.Formatter('{asctime} {name} {levelname:8s} {message}', style='{')
Notez que dans ce cas, vous ne pouvez toujours pas appeler logger
avec le nouveau format. C'est-à-dire que les éléments suivants ne fonctionnent toujours pas:
logger.info("knights:{say}", say="ni") # Doesn't work!
logger.info("knights:{0}", "ni") # Doesn't work either
La solution la plus simple serait d’utiliser le module excellent logbook
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
Telle était ma solution au problème lorsque j'ai découvert que la journalisation utilise uniquement le formatage de style printf. Il permet aux appels de consignation de rester les mêmes - pas de syntaxe spéciale telle que log.info(__("val is {}", "x"))
. La modification requise pour coder consiste à envelopper l'enregistreur dans une StyleAdapter
.
from inspect import getargspec
class BraceMessage(object):
def __init__(self, fmt, args, kwargs):
self.fmt = fmt
self.args = args
self.kwargs = kwargs
def __str__(self):
return str(self.fmt).format(*self.args, **self.kwargs)
class StyleAdapter(logging.LoggerAdapter):
def __init__(self, logger):
self.logger = logger
def log(self, level, msg, *args, **kwargs):
if self.isEnabledFor(level):
msg, log_kwargs = self.process(msg, kwargs)
self.logger._log(level, BraceMessage(msg, args, kwargs), (),
**log_kwargs)
def process(self, msg, kwargs):
return msg, {key: kwargs[key]
for key in getargspec(self.logger._log).args[1:] if key in kwargs}
L'utilisation est:
log = StyleAdapter(logging.getLogger(__name__))
log.info("a log message using {type} substiution", type="brace")
Il convient de noter que cette implémentation pose des problèmes si les mots clés utilisés pour la substitution d'accolade incluent level
, msg
, args
, exc_info
, extra
ou stack_info
. Ce sont des noms d'argument utilisés par la méthode log
de Logger
. Si vous avez besoin de l’un de ces noms, modifiez process
pour exclure ces noms ou supprimez simplement log_kwargs
de l’appel _log
. Par ailleurs, cette implémentation ignore également les mots clés mal orthographiés destinés à l'enregistreur (par exemple, ectra
).
Comme d'autres réponses le mentionnent, la mise en forme de type accolade introduite dans Python 3.2 est uniquement utilisée dans la chaîne de formatage, pas dans les messages de journal réels.
Depuis Python 3.5, il n’existait aucun moyen pratique d’utiliser le formatage en accolade pour consigner les messages.
Cependant, comme avec la plupart des choses en Python, il n'y a pas moyen de faire ça.
Le module suivant corrige le module logging
pour créer une fonction get_logger
qui renverra un consignateur utilisant le formatage de style nouveau pour chaque enregistrement de journal traité.
import functools
import logging
import types
def _get_message(record):
"""Replacement for logging.LogRecord.getMessage
that uses the new-style string formatting for
it's 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
Usage:
>>> log = get_logger()
>>> log.warning("{!r}", log)
<logging.RootLogger object at 0x4985a4d3987b>
Remarques:
get_logger
.logging.getLogger()
normal, le formatage de nouveau style s’appliquera toujours.logging.LogRecord
(utile dans certains cas)logging
, il semble que cela devrait fonctionner jusqu’à Python 2.6 lorsque str.format
a été introduit (mais n’a été testé que sur Python 3.5).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)
Voici quelque chose de très simple qui fonctionne:
debug_logger: logging.Logger = logging.getLogger("app.debug")
def mydebuglog(msg: str, *args, **kwargs):
if debug_logger.isEnabledFor(logging.DEBUG):
debug_logger.debug(msg.format(*args, **kwargs))
Ensuite:
mydebuglog("hello {} {val}", "Python", val="World")
J'ai créé un formateur personnalisé, appelé ColorFormatter qui gère le problème de la manière suivante:
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 le maintient compatible avec diverses bibliothèques ... L'inconvénient est qu'il n'est probablement pas performant en raison du risque de tentative de formatage de la chaîne deux fois.