Używam narzędzia do logowania w Pythonie 2.7.3. Dokumentacja dla tej wersji Pythona mówi :
pakiet rejestrowania poprzedza nowsze opcje formatowania, takie jak str.format () i string.Template. Te nowsze opcje formatowania są obsługiwane ...
Podoba mi się „nowy” format z nawiasami klamrowymi. Więc próbuję zrobić coś takiego:
log = logging.getLogger("some.logger")
log.debug("format this message {0}", 1)
I otrzymaj błąd:
TypeError: nie wszystkie argumenty zostały przekonwertowane podczas formatowania ciągu
Za czym tęsknię?
PS nie chcę używać
log.debug("format this message {0}".format(1))
ponieważ w tym przypadku wiadomość jest zawsze formatowana niezależnie od poziomu rejestratora.
python
python-2.7
string-formatting
MajesticRa
źródło
źródło
log.debug("format this message%d" % 1)
Formatter
używanie '{' jako styluOdpowiedzi:
EDYCJA: spójrz na
StyleAdapter
podejście w odpowiedzi @Dunes w przeciwieństwie do tej odpowiedzi; pozwala na użycie alternatywnych stylów formatowania bez standardowego podczas wywoływania metod rejestratora (debug (), info (), error (), itp.).Z dokumentów - Stosowanie alternatywnych stylów formatowania :
I:
Skopiuj i wklej to do
wherever
modułu: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)
Następnie:
from wherever import BraceMessage as __ log.debug(__('Message with {0} {name}', 2, name='placeholders'))
Uwaga: faktyczne formatowanie jest opóźnione do momentu, gdy jest to konieczne, np. Jeśli komunikaty DEBUG nie są rejestrowane, formatowanie nie jest w ogóle wykonywane.
źródło
num = 2; name = 'placeholders'; log.debug(f'Message with {num} {name}')
Oto kolejna opcja, która nie ma problemów ze słowami kluczowymi wymienionymi w odpowiedzi Dunes. Obsługuje tylko
{0}
argumenty pozycyjne ( ), a nie argumenty słów kluczowych ({foo}
). Nie wymaga również dwóch wywołań formatowania (przy użyciu podkreślenia). Ma ten współczynnik podklasystr
: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
Używasz tego w ten sposób:
logger = StyleAdapter(logging.getLogger(__name__)) logger.info("knights:{0}", "ni", style="{") logger.info("knights:{}", "shrubbery", style="{")
Oczywiście można usunąć zaznaczenie oznaczone ikoną,
# optional
aby wymusić na wszystkich komunikatach przechodzących przez adapter użycie nowego stylu formatowania.Uwaga dla każdego, kto po latach przeczyta tę odpowiedź : Począwszy od Pythona 3.2 , można używać parametru style z
Formatter
obiektami:Dokumentacja dostarcza przykładu
logging.Formatter('{asctime} {name} {levelname:8s} {message}', style='{')
Zauważ, że w tym przypadku nadal nie możesz zadzwonić
logger
do nowego formatu. To znaczy, że nadal nie będzie działać:logger.info("knights:{say}", say="ni") # Doesn't work! logger.info("knights:{0}", "ni") # Doesn't work either
źródło
Formatter
, co jest teraz poprawne (myślę). TeStyleAdapter
prace nadal,BraceString
jest to podklasa typu string, można bezpiecznie wrócić z__str__
To było moje rozwiązanie problemu, gdy zauważyłem, że rejestrowanie wykorzystuje tylko formatowanie w stylu printf. Pozwala to rejestrować wywołania, które pozostają takie same - bez specjalnej składni, takiej jak
log.info(__("val is {}", "x"))
. Zmiana wymagana do kodu polega na zawinięciu rejestratora w plikStyleAdapter
.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}
Wykorzystanie to:
log = StyleAdapter(logging.getLogger(__name__)) log.info("a log message using {type} substitution", type="brace")
Warto zauważyć, że ta implementacja ma problemy, jeśli słowa kluczowe używane do zastąpienia nawiasów to
level
,msg
,args
,exc_info
,extra
lubstack_info
. Są to nazwy argumentów używane przezlog
metodęLogger
. Jeśli potrzebujesz jednej z tych nazw, zmień je,process
aby wykluczyć te nazwy lub po prostu usuń jelog_kwargs
z_log
połączenia. Co więcej, ta implementacja również po cichu ignoruje błędnie napisane słowa kluczowe przeznaczone dla Loggera (np.ectra
).źródło
Łatwiejszym rozwiązaniem byłoby użycie znakomitego
logbook
modułuimport logbook import sys logbook.StreamHandler(sys.stdout).push_application() logbook.debug('Format this message {k}', k=1)
Lub bardziej kompletne:
>>> 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
źródło
Jak wspominają inne odpowiedzi, formatowanie w nawiasach klamrowych wprowadzone w Pythonie 3.2 jest używane tylko w ciągu formatu, a nie w samych komunikatach dziennika.
Aby włączyć formatowanie w nawiasach klamrowych w aktualnym komunikacie dziennika, możemy małpować kawałek kodu rejestratora.
Poniższe poprawki dotyczą
logging
modułu w celu utworzeniaget_logger
funkcji, która zwróci program rejestrujący, który używa nowego formatu formatowania dla każdego obsługiwanego rekordu dziennika.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
Stosowanie:
>>> log = get_logger() >>> log.warning("{!r}", log) <logging.RootLogger object at 0x4985a4d3987b>
Uwagi:
logging.getLogger
zget_logger
)get_logger
funkcję (nie psuje pakietów innych firm).logging.getLogger()
połączenia, nadal będzie obowiązywać formatowanie w nowym stylu.exc_info
,stack_info
,stacklevel
iextra
).logging.LogRecord
obiektach jak zwykle (przydatne w niektórych przypadkach w przypadku niestandardowych programów obsługi dziennika).logging
kod źródłowy modułu , wydaje się, że powinien on działać aż do Pythona 2.6, kiedystr.format
został wprowadzony (ale testowałem go tylko w 3.5 i nowszych)źródło
Wypróbuj
logging.setLogRecordFactory
w Pythonie 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)
źródło
%
formatowania, ponieważ fabryka rekordów jest globalna dla modułu rejestrowania.Stworzyłem niestandardowy program formatujący o nazwie ColorFormatter, który rozwiązuje ten problem:
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…
Dzięki temu jest kompatybilny z różnymi bibliotekami. Wadą jest to, że prawdopodobnie nie jest wydajny z powodu potencjalnej próby dwukrotnego sformatowania ciągu.
źródło
Oto coś naprawdę prostego, które działa:
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))
Następnie:
mydebuglog("hello {} {val}", "Python", val="World")
źródło
Podobne rozwiązanie do pR0Ps', owijania
getMessage
wLogRecord
owijającmakeRecord
(zamiasthandle
na ich odpowiedź) w przypadkachLogger
, które powinny być nowe formatowanie z obsługą: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
Przetestowałem to w Pythonie 3.5.3.
źródło