Используя пакет журналов python, как вставить дополнительное форматирование в несколько обработчиков логгеров, которые имеют свои собственные форматеры? - PullRequest
4 голосов
/ 20 января 2011

У меня есть один регистратор с несколькими обработчиками, которые имеют свои собственные форматеры.Теперь я хочу добавить функцию отступа с уровнем отступа, контролируемым во время выполнения.Я хочу сообщения от всех обработчиков, чтобы получить этот отступ.Я пытался создать его как фильтр, но обнаружил, что, похоже, не могу изменить содержимое сообщения.Тогда я попробовал это как средство форматирования, но у меня может быть только один на обработчик.Как я могу добавить такие отступы без явного изменения форматеров каждого обработчика?
Я должен упомянуть, что один из моих форматеров - это класс, который добавляет цвет к выводу.Это не простая строка формата.


Кроме того, я использую файл конфигурации.В идеале, я хотел бы быть в состоянии управлять этим в основном оттуда.Однако мне нужно изменить состояние форматера отступа (например, установить уровень отступа), но я не знаю, как добраться до этого конкретного форматера, так как нет метода logger.getFormatter("by_name").
Чтобы уточнить, мне нужен доступ к конкретному экземпляру форматера, по существу, для настройки формата на лету.Экземпляр был создан logging.config из файла.Я не нахожу никаких методов доступа, которые позволили бы мне получить конкретный форматер по его имени.

Ответы [ 3 ]

4 голосов
/ 20 января 2011
#!/usr/bin/env python

import logging
from random import randint

log = logging.getLogger("mylog")
log.setLevel(logging.DEBUG)

class MyFormatter(logging.Formatter):
    def __init__(self, fmt):
        logging.Formatter.__init__(self, fmt)

    def format(self, record):
        indent = " " * randint(0, 10) # To show that it works
        msg = logging.Formatter.format(self, record)
        return "\n".join([indent + x for x in msg.split("\n")])

# Log to file
filehandler = logging.FileHandler("indent.txt", "w")
filehandler.setLevel(logging.DEBUG)
filehandler.setFormatter(MyFormatter("%(levelname)-10s %(message)s"))
log.addHandler(filehandler)

# Log to stdout too
streamhandler = logging.StreamHandler()
streamhandler.setLevel(logging.INFO)
streamhandler.setFormatter(MyFormatter("%(message)s"))
log.addHandler(streamhandler)

# Test it
log.debug("Can you show me the dog-kennels, please")
log.info("They could grip it by the husk")
log.warning("That's no ordinary rabbit!")
log.error("Nobody expects the spanish inquisition")
try:
    crunchy_frog()
except:
    log.exception("It's a real frog")

результат:

    They could grip it by the husk
    That's no ordinary rabbit!
          Nobody expects the spanish inquisition
         It's a real frog
         Traceback (most recent call last):
           File "./logtest2.py", line 36, in 
             crunchy_frog()
         NameError: name 'crunchy_frog' is not defined

Я не уверен, что понимаю ваш второй вопрос.

0 голосов
/ 21 января 2011

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

# (make a LEVELS dict out of all the logging levels first)    
def indent(self, step = 1):
        "Change the current indent level by the step (use negative to decrease)"
        self._indent_level += step
        if self._indent_level < 0:
            self._indent_level = 0
        self._indent_str = self._indent_str_base * self._indent_level
        for lvl in LEVELS:
            level_name = self._indent_str + LEVELS[lvl]
            logging.addLevelName(lvl, level_name)

(см. Мой другой ответ о том, что окружает функцию отступа)
Теперь индентор может быть независимым классом, не вмешиваясь в детали процесса регистрации. Пока сообщение содержит строку уровня, отступ будет там, даже если перед ним будут какие-то вещи. В общем, не идеально, но может работать на меня.
У кого-нибудь есть идеи, которые подходят для любого формата сообщений?

0 голосов
/ 21 января 2011

Хорошо, вот один способ, который дает мне ПОЧТИ то, что мне нужно.Подкласс LogRecord для перезаписи getMessage для вставки отступа и регистратора подкласса для makeRecord с ним:

import logging
import logging.config  

################################################################################
class IndentingLogger(logging.Logger):
    """A Logger subclass to add indent on top of any logger output
    """
    ############################################################################
    def __init__(self, name = 'root', logging_level = logging.NOTSET):
        "Constructor to keep indent persistent"
        logging.Logger.__init__(self, name, logging_level)
        self.indenter = IndentedRecord("", logging.NOTSET, "", 0, None, None, None, None, None)

    ############################################################################
    def makeRecord(self, name, level, fn, lno, msg, args, exc_info, func=None, extra=None):
        return self.indenter.set_record(name, level, fn, lno, msg, args, exc_info, func, extra)


################################################################################
class IndentedRecord(logging.LogRecord):
    """A LogRecord subclass to add indent on top of any logger output
    """
    ######## Class data #########
    DEFAULT_INDENT_STR = '    '

    ############################################################################
    def __init__(self, name, level, fn, lno, msg, args, exc_info, func=None, extra=None):
        "Constructor"
        logging.LogRecord.__init__(self, name, level, fn, lno, msg, args, exc_info, func)
        self._indent_level = 0
        self._indent_str_base = IndentedRecord.DEFAULT_INDENT_STR
        self._indent_str = ""    # cache it

    ############################################################################
    def set_record(self, name, level, fn, lno, msg, args, exc_info, func=None, extra=None):
        "Constructs the base record"
        logging.LogRecord.__init__(self, name, level, fn, lno, msg, args, exc_info, func)
        return self

    ################################################################################
    def getMessage(self):
        "Adds indent on top of the normal getMessage result"

        # Call base class to get the formatted message
        message = logging.LogRecord.getMessage(self)

       # Now insert the indent
        return self._indent_str + message

    ################################################################################
    def indent(self, step = 1):
        "Change the current indent level by the step (use negative to decrease)"
        self._indent_level += step
        if self._indent_level < 0:
            self._indent_level = 0
        self._indent_str = self._indent_str_base * self._indent_level

    ################################################################################
    def set_indent_str(self, chars):
        "Change the current indent string"
        if not isinstance(chars, str):
            raise ValueError("Argument must be a string. Got %s" % chars)
        self._indent_str_base = chars

logging.config.fileConfig("reporter.conf")
logging.setLoggerClass(IndentingLogger)
logger = logging.getLogger('root') # will be wrong logger, if without argument

logger.debug("debug message")
logger.info("info message")
logger.indenter.indent(+1)
logger.warn("Indented? warn message")
logger.indenter.set_indent_str("***")
logger.error("Indented? error message: %s", "Oops, I did it again!")
logger.indenter.indent(+1)
logger.error("Indented? error message: %s", "Oops, I did it again!")
logger.indenter.indent(-1)
logger.critical("No indent; critical message")

Результат (окрашен в реальности):

Debug: debug message
Info: info message
Warning:     Indented? warn message
Error:     Indented? error message: Oops, I did it again!
Error: ******Indented? error message: Oops, I did it again!
Internal Error: ***No indent; critical message

Каким-то образом строка уровня журнала все ещеподкрадывается к фронту, так что это не совсем то, что я хочу.Кроме того, это неудобно - слишком много для такой простой функции :(
Лучшие идеи?

...