Подтвердить что ты не робот

Запись переменных данных с помощью новой строки формата

Я использую средство ведения журнала для python 2.7.3. Документация для этой версии Python говорит:

пакет протоколирования предварительно задает новые параметры форматирования, такие как str.format() и string.Template. Эти новые параметры форматирования поддерживаются...

Мне нравится "новый" формат с фигурными фигурными скобками. Поэтому я пытаюсь сделать что-то вроде:

 log = logging.getLogger("some.logger")
 log.debug("format this message {0}", 1)

И получите ошибку:

TypeError: не все аргументы, преобразованные во время форматирования строки

Что мне здесь не хватает?

P.S. Я не хочу использовать

log.debug("format this message {0}".format(1))

потому что в этом случае сообщение всегда отформатируется независимо от уровня регистратора.

4b9b3361

Ответ 1

EDIT: посмотрите на StyleAdapter подход в ответ @Dunes' в отличие от этого ответа; он позволяет использовать альтернативные стили форматирования без шаблона при вызове методов регистрации (debug(), info(), error() и т.д.).


Из документов - Использование альтернативных стилей форматирования:

Журнальные вызовы (logger.debug(), logger.info() и т.д.) Принимают только позиционные параметры для самого фактического сообщения регистрации, причем параметры ключевого слова используются только для определения параметров обработки фактического вызова ведения журнала (например, параметр ключевого слова exc_info чтобы указать, что информация трассировки должна быть зарегистрирована, или дополнительный параметр ключевого слова, чтобы указать дополнительную контекстуальную информацию, которая будет добавлена в журнал). Таким образом, вы не можете напрямую вести вызовы журналов с использованием синтаксиса str.format() или string.Template, потому что внутри пакета протоколирования используется% -formatting для объединения строки формата и аргументов переменной. Это не изменило бы это, сохранив обратную совместимость, поскольку все вызовы журналов, которые есть в существующем коде, будут использовать строки% -format.

А также:

Тем не менее, вы можете использовать {} - и $ - форматирование для создания отдельных сообщений журнала. Напомним, что для сообщения вы можете использовать произвольный объект в качестве строки формата сообщения и чтобы пакет регистрации вызывал str() для этого объекта, чтобы получить фактическую строку формата.

Скопируйте это в 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)

Затем:

from wherever import BraceMessage as __

log.debug(__('Message with {0} {name}', 2, name='placeholders'))

Примечание: фактическое форматирование задерживается до тех пор, пока это не понадобится, например, если сообщения DEBUG не регистрируются, форматирование вообще не выполняется.

Ответ 2

Вот еще один вариант, который не имеет проблем с ключевыми словами, упомянутых в ответе Дюны. Он может обрабатывать только аргументы positional ({0}), а не ключевые слова ({foo}). Он также не требует двух вызовов для форматирования (с использованием подчеркивания). Он имеет ick-фактор подклассификации 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

Вы используете его следующим образом:

logger = StyleAdapter(logging.getLogger(__name__))
logger.info("knights:{0}", "ni", style="{")
logger.info("knights:{}", "shrubbery", style="{")

Конечно, вы можете удалить отмеченную отметку с помощью # optional чтобы заставить все сообщения через адаптер использовать форматирование нового стиля.


Примечание для тех, кто читает этот ответ спустя годы: Начиная с Python 3.2, вы можете использовать параметр стиля с объектами Formatter:

Регистрация (с 3.2) обеспечивает улучшенную поддержку этих двух дополнительных стилей форматирования. Класс Formatter был расширен, чтобы получить дополнительный необязательный параметр ключевого слова с именем style. Это значение по умолчанию равно '%', но другие возможные значения: '{' и '$', которые соответствуют двум другим стилям форматирования. Обратная совместимость поддерживается по умолчанию (как и следовало ожидать), но явно указывая параметр стиля, вы получаете возможность указывать строки формата, которые работают с str.format() или string.Template.

Документы предоставляют пример logging.Formatter('{asctime} {name} {levelname:8s} {message}', style='{')

Обратите внимание, что в этом случае вы все равно не можете вызвать logger с новым форматом. Т.е., все еще не будет работать:

logger.info("knights:{say}", say="ni")  # Doesn't work!
logger.info("knights:{0}", "ni")  # Doesn't work either

Ответ 3

Более простым решением было бы использовать отличный модуль logbook

import logbook
import sys

logbook.StreamHandler(sys.stdout).push_application()
logbook.debug('Format this message {k}', k=1)

Или более полное:

>>> 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

Ответ 4

Это было мое решение проблемы, когда я обнаружил, что для ведения журнала используется только форматирование стиля printf. Это позволяет вести журнал вызовов, чтобы оставаться неизменным - никакого специального синтаксиса, такого как log.info(__("val is {}", "x")). Необходимое для кода изменение состоит в том, чтобы обернуть логгер в 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}

Использование:

log = StyleAdapter(logging.getLogger(__name__))
log.info("a log message using {type} substiution", type="brace")

Стоит отметить, что эта реализация имеет проблемы, если ключевые слова, используемые для подстановки фигурных скобок, включают в себя level, msg, args, exc_info, extra или stack_info. Это имена аргументов, используемые методом log Logger. Если вам нужно одно из этих имен, измените process, чтобы исключить эти имена или просто удалить log_kwargs из вызова _log. Следующее примечание: эта реализация также молча игнорирует слова с ошибками, используемые для Logger (например, ectra).

Ответ 5

Как упоминается в других ответах, форматирование стиля представленное в Python 3.2, используется только в строке формата, а не в фактических сообщениях журнала.

Как и у Python 3.5, нет хорошего способа использовать форматирование в стиле скобок для записи сообщений.

Однако, как и в большинстве случаев на Python, есть не очень хороший способ.

Следующая обезьяна исправляет модуль logging для создания функции get_logger, которая возвращает журнал, который использует форматирование нового стиля для каждой записи журнала, которую он обрабатывает.

import functools
import logging
import types

def _get_message(record):
    """Replacement for logging.LogRecord.getMessage
    that uses the new-style string formatting for
    it 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 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

Использование:

>>> log = get_logger()
>>> log.warning("{!r}", log)
<logging.RootLogger object at 0x4985a4d3987b>

Примечания:

  • Будет влиять только на определенные журналы, созданные функцией get_logger.
  • Если регистратор снова получает доступ из обычного вызова logging.getLogger(), форматирование нового стиля будет по-прежнему применяться
  • kwargs не поддерживается
  • Достижение производительности должно быть минимальным (переписывание одного указателя функции для каждого сообщения журнала)
  • Форматирование сообщения задерживается до его выхода
  • Не останавливает хранение аргументов на объектах logging.LogRecord (полезно в некоторых случаях)
  • От взгляда на исходный код logging кажется, что он должен полностью вернуться к Python 2.6, когда str.format был введен (но был протестирован только на Python 3.5).

Ответ 6

Здесь что-то реальное простое, что работает:

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))

Тогда:

mydebuglog("hello {} {val}", "Python", val="World")

Ответ 7

Попробуйте logging.setLogRecordFactory в 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)

Ответ 8

Я создал пользовательский Formatter, называемый ColorFormatter, который обрабатывает проблему следующим образом:

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…

Это совместимо с различными библиотеками. Недостатком является то, что он, вероятно, не работает из-за потенциальной попытки форматирования строки дважды.