Combiné string.Formatter
à ajouter pprint.pformat
et de la conversion de type logging
: setLogRecordFactory
, setLoggerClass
. Il y a une astuce intéressante : je crée un tuple imbriqué supplémentaire pour l'argument args
para Logger._log
puis de le décompresser dans LogRecord
init pour omettre les surcharges dans Logger.makeRecord
. Utilisation de log.f wraps
chaque attribut (méthodes de journalisation à dessein) avec use_format
donc vous n'avez pas besoin de l'écrire explicitement. Cette solution est rétrocompatible.
from collections import namedtuple
from collections.abc import Mapping
from functools import partial
from pprint import pformat
from string import Formatter
import logging
Logger = logging.getLoggerClass()
LogRecord = logging.getLogRecordFactory()
class CustomFormatter(Formatter):
def format_field(self, value, format_spec):
if format_spec.endswith('p'):
value = pformat(value)
format_spec = format_spec[:-1]
return super().format_field(value, format_spec)
custom_formatter = CustomFormatter()
class LogWithFormat:
def __init__(self, obj):
self.obj = obj
def __getattr__(self, name):
return partial(getattr(self.obj, name), use_format=True)
ArgsSmuggler = namedtuple('ArgsSmuggler', ('args', 'smuggled'))
class CustomLogger(Logger):
def __init__(self, *ar, **kw):
super().__init__(*ar, **kw)
self.f = LogWithFormat(self)
def _log(self, level, msg, args, *ar, use_format=False, **kw):
super()._log(level, msg, ArgsSmuggler(args, use_format), *ar, **kw)
class CustomLogRecord(LogRecord):
def __init__(self, *ar, **kw):
args = ar[5]
# RootLogger use CustomLogRecord but not CustomLogger
# then just unpack only ArgsSmuggler instance
args, use_format = args if isinstance(args, ArgsSmuggler) else (args, False)
super().__init__(*ar[:5], args, *ar[6:], **kw)
self.use_format = use_format
def getMessage(self):
return self.getMessageWithFormat() if self.use_format else super().getMessage()
def getMessageWithFormat(self):
msg = str(self.msg)
args = self.args
if args:
fmt = custom_formatter.format
msg = fmt(msg, **args) if isinstance(args, Mapping) else fmt(msg, *args)
return msg
logging.setLogRecordFactory(CustomLogRecord)
logging.setLoggerClass(CustomLogger)
log = logging.getLogger(__name__)
log.info('%s %s', dict(a=1, b=2), 5)
log.f.info('{:p} {:d}', dict(a=1, b=2), 5)