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)
Formatter
para usar '{' como estiloRespuestas:
EDITAR: eche un vistazo al
StyleAdapter
enfoque 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
wherever
mó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, kwargs
Lo 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
# optional
para 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
Formatter
objetos: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
logger
con 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 either
fuente
Formatter
, que es correcto ahora (creo). ElStyleAdapter
todavía funciona,BraceString
es 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
,extra
ostack_info
. Estos son nombres de argumentos utilizados por ellog
método deLogger
. Si necesita uno de estos nombres, modifíquelosprocess
para excluirlos o simplemente elimíneloslog_kwargs
de la_log
llamada. 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
logbook
import 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 1
fuente
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
logging
módulo para crear unaget_logger
funció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 log
Uso:
>>> log = get_logger() >>> log.warning("{!r}", log) <logging.RootLogger object at 0x4985a4d3987b>
Notas:
logging.getLogger
conget_logger
)get_logger
función (no rompe los paquetes de terceros).logging.getLogger()
llamada normal , se seguirá aplicando el formato de nuevo estilo.exc_info
,stack_info
,stacklevel
yextra
).logging.LogRecord
objetos como de costumbre (útil en algunos casos con controladores de registro personalizados).logging
código fuente del módulo , parece que debería funcionar hasta Python 2.6 cuandostr.format
se introdujo (pero solo lo probé en 3.5 y versiones posteriores)fuente
Prueba
logging.setLogRecordFactory
en 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
getMessage
deLogRecord
por envolturamakeRecord
(en lugar dehandle
en su respuesta) en los casos deLogger
que 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 log
Probé esto con Python 3.5.3.
fuente