Utilizo la función de registro para Python 2.7.3. La documentación para esta versión de Python dice :
el paquete de registro es anterior a las opciones de formato más nuevas, como str.format () y string.Template. Estas nuevas opciones de formato son compatibles ...
Me gusta el formato "nuevo" con llaves. Entonces estoy tratando de hacer algo como:
log = logging.getLogger("some.logger")
log.debug("format this message {0}", 1)
Y obtén el error:
TypeError: no todos los argumentos se convierten durante el formateo de cadenas
¿Qué extraño aquí?
PD: no quiero usar
log.debug("format this message {0}".format(1))
porque en este caso el mensaje siempre se formatea independientemente del nivel del registrador.
python
python-2.7
string-formatting
MajesticRa
fuente
fuente

log.debug("format this message%d" % 1)Formatterpara usar '{' como estiloRespuestas:
EDITAR: eche un vistazo al
StyleAdapterenfoque en la respuesta de @Dunes a diferencia de esta respuesta; permite usar estilos de formato alternativos sin el texto estándar mientras se llama a los métodos del registrador (debug (), info (), error (), etc.).De los documentos: uso de estilos de formato alternativos :
Y:
Copie y pegue esto en el
wherevermódulo: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)Entonces:
from wherever import BraceMessage as __ log.debug(__('Message with {0} {name}', 2, name='placeholders'))Nota: el formateo real se retrasa hasta que sea necesario, por ejemplo, si los mensajes DEBUG no se registran, el formateo no se realiza en absoluto.
fuente
num = 2; name = 'placeholders'; log.debug(f'Message with {num} {name}')Aquí hay otra opción que no tiene los problemas de palabras clave mencionados en la respuesta de Dunes. Solo puede manejar
{0}argumentos posicionales ( ) y no argumentos de palabra clave ({foo}). Tampoco requiere dos llamadas para formatear (usando el guión bajo). Tiene el factor ick de subclasificarstr: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, kwargsLo usas así:
logger = StyleAdapter(logging.getLogger(__name__)) logger.info("knights:{0}", "ni", style="{") logger.info("knights:{}", "shrubbery", style="{")Por supuesto, puede eliminar la marca de verificación indicada con
# optionalpara forzar que todos los mensajes a través del adaptador utilicen un formato de nuevo estilo.Nota para cualquiera que lea esta respuesta años después : a partir de Python 3.2 , puede usar el parámetro de estilo con
Formatterobjetos:Los documentos proporcionan el ejemplo
logging.Formatter('{asctime} {name} {levelname:8s} {message}', style='{')Tenga en cuenta que en este caso aún no puede llamar al
loggercon el nuevo formato. Es decir, lo siguiente todavía no funcionará:logger.info("knights:{say}", say="ni") # Doesn't work! logger.info("knights:{0}", "ni") # Doesn't work eitherfuente
Formatter, que es correcto ahora (creo). ElStyleAdaptertodavía funciona,BraceStringes una subclase de cadena, es seguro regresar desde__str__Esta fue mi solución al problema cuando descubrí que el registro solo usa formato de estilo printf. Permite que las llamadas de registro sigan siendo las mismas, sin sintaxis especial como
log.info(__("val is {}", "x")). El cambio requerido al código es envolver el registrador en unStyleAdapter.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}El uso es:
log = StyleAdapter(logging.getLogger(__name__)) log.info("a log message using {type} substitution", type="brace")Vale la pena señalar que esta aplicación tiene problemas si las palabras clave que se utilizan para la sustitución de refuerzo incluyen
level,msg,args,exc_info,extraostack_info. Estos son nombres de argumentos utilizados por ellogmétodo deLogger. Si necesita uno de estos nombres, modifíquelosprocesspara excluirlos o simplemente elimíneloslog_kwargsde la_logllamada. En una nota adicional, esta implementación también ignora silenciosamente las palabras clave mal escritas destinadas al Logger (por ejemploectra).fuente
La solución más sencilla sería utilizar el módulo excelente
logbookimport logbook import sys logbook.StreamHandler(sys.stdout).push_application() logbook.debug('Format this message {k}', k=1)O el más completo:
>>> 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 1fuente
Como mencionan otras respuestas, el formato de estilo de llave introducido en Python 3.2 solo se usa en la cadena de formato, no en los mensajes de registro reales.
Para habilitar el formato de estilo de llaves en el mensaje de registro real, podemos parchear un poco del código del registrador.
Lo siguiente parchea el
loggingmódulo para crear unaget_loggerfunción que devolverá un registrador que usa el formato de nuevo estilo para cada registro que maneja.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 logUso:
>>> log = get_logger() >>> log.warning("{!r}", log) <logging.RootLogger object at 0x4985a4d3987b>Notas:
logging.getLoggerconget_logger)get_loggerfunción (no rompe los paquetes de terceros).logging.getLogger()llamada normal , se seguirá aplicando el formato de nuevo estilo.exc_info,stack_info,stacklevelyextra).logging.LogRecordobjetos como de costumbre (útil en algunos casos con controladores de registro personalizados).loggingcódigo fuente del módulo , parece que debería funcionar hasta Python 2.6 cuandostr.formatse introdujo (pero solo lo probé en 3.5 y versiones posteriores)fuente
Prueba
logging.setLogRecordFactoryen 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)fuente
%formato, ya que la fábrica de registros es global para el módulo de registro.Creé un formateador personalizado, llamado ColorFormatter que maneja el problema de esta manera:
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…Esto lo mantiene compatible con varias bibliotecas. El inconveniente es que probablemente no funcione debido a que podría intentar formatear la cadena dos veces.
fuente
Aquí hay algo realmente simple que funciona:
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))Entonces:
mydebuglog("hello {} {val}", "Python", val="World")fuente
Solución similar a pR0Ps', envoltura
getMessagedeLogRecordpor envolturamakeRecord(en lugar dehandleen su respuesta) en los casos deLoggerque debe ser completamente nuevo formateo habilitados:def getLogger(name): log = logging.getLogger(name) def Logger_makeRecordWrapper(name, level, fn, lno, msg, args, exc_info, func=None, extra=None, sinfo=None): self = log record = logging.Logger.makeRecord(self, name, level, fn, lno, msg, args, exc_info, func, sinfo) def LogRecord_getMessageNewStyleFormatting(): self = record msg = str(self.msg) if self.args: msg = msg.format(*self.args) return msg record.getMessage = LogRecord_getMessageNewStyleFormatting return record log.makeRecord = Logger_makeRecordWrapper return logProbé esto con Python 3.5.3.
fuente