Как использовать разные форматы с одним и тем же обработчиком ведения журнала в python

Возможно ли зарегистрировать один пункт назначения (т.е. использовать один FileHandler) с несколькими регистраторами (т.е. logging.getLogger("base.foo") и logging.getLogger("base.bar")) и использовать разные форматы для каждого из регистраторов.

По моему мнению, можно только назначить один форматтер для каждого дескриптора. Может быть, возможно связать форматтер с регистратором, а не с обработчиком?

Ответы

Ответ 1

Легко отправлять в разные форматы на основе record.name. Ниже приведен пример кода примера:

import logging


class DispatchingFormatter:

    def __init__(self, formatters, default_formatter):
        self._formatters = formatters
        self._default_formatter = default_formatter

    def format(self, record):
        formatter = self._formatters.get(record.name, self._default_formatter)
        return formatter.format(record)


handler = logging.StreamHandler()
handler.setFormatter(DispatchingFormatter({
        'base.foo': logging.Formatter('FOO: %(message)s'),
        'base.bar': logging.Formatter('BAR: %(message)s'),
    },
    logging.Formatter('%(message)s'),
))
logging.getLogger().addHandler(handler)

logging.getLogger('base.foo').error('Log from foo')
logging.getLogger('base.bar').error('Log from bar')
logging.getLogger('base.baz').error('Log from baz')

Другой способ - открыть файл вручную и создать из него два обработчика потока с помощью разных форматов.

Ответ 2

Небольшое исправление для отличного решения Дениса.

Система имен журналов на основе иерархической структуры:

name - это потенциально иерархическое значение, разделенное периодом, например foo.bar.baz (хотя он также может быть просто простым foo, например). Регистраторы, которые находятся ниже в иерархическом списке, являются дочерними регистраторы выше в списке. Например, с учетом регистратора с именем из foo, регистраторы с именами foo.bar, foo.bar.baz и foo.bam являются все потомки foo.

Например, когда вы setLevel() для некоторого регистратора, этот уровень будет также применяться к дочерним регистраторам. Вот почему вы можете захотеть, чтобы ваш форматировщик использовался для регистратора, а также для дочерних регистраторов. Например, форматер 'one.two' должен также применяться к 'one.two.three' logger (если нет форматирования для 'one.two.three'). Здесь версия DispatchingFormatter, выполняющая работу (код Python 3):

class DispatchingFormatter:
    """Dispatch formatter for logger and it sub logger."""
    def __init__(self, formatters, default_formatter):
        self._formatters = formatters
        self._default_formatter = default_formatter

    def format(self, record):
        # Search from record logger up to it parents:
        logger = logging.getLogger(record.name)
        while logger:
            # Check if suitable formatter for current logger exists:
            if logger.name in self._formatters:
                formatter = self._formatters[logger.name]
                break
            else:
                logger = logger.parent
        else:
            # If no formatter found, just use default:
            formatter = self._default_formatter
        return formatter.format(record)

Пример:

handler = logging.StreamHandler()
handler.setFormatter(DispatchingFormatter({
        'one': logging.Formatter('%(message)s -> one'),
        'one.two': logging.Formatter('%(message)s -> one.two'),
    },
    logging.Formatter('%(message)s -> <default>'),
))
logging.getLogger().addHandler(handler)

print('Logger used -> formatter used:')
logging.getLogger('one').error('one')
logging.getLogger('one.two').error('one.two')
logging.getLogger('one.two.three').error('one.two.three')  # parent formatter 'one.two' will be used here
logging.getLogger('other').error('other')

# OUTPUT:
# Logger used -> formatter used:
# one -> one
# one.two -> one.two
# one.two.three -> one.two
# other -> <default>

Ответ 3

Эта работа для вас? Различные форматы вывода на уровне журнала и разные места назначения журнала, файл и стандартный вывод (и разные уровни для каждого места назначения):

    logger = logging.getLogger(__name__)
    logger.setLevel(logging.DEBUG)

    format_for_stdout = logging.Formatter('%(message)s')
    format_for_logfile = logging.Formatter('%(asctime)s: %(name)s: %(levelname)s: %(message)s')

    handler_logfile = logging.FileHandler('my_awesome_logs.log')
    handler_logfile.setLevel(logging.DEBUG)
    handler_logfile.setFormatter(format_for_logfile)

    handler_stdout = logging.StreamHandler()
    handler_stdout.setLevel(logging.INFO)
    handler_stdout.setFormatter(format_for_stdout)

    logger.addHandler(handler_logfile)
    logger.addHandler(handler_stdout)

    logging.addLevelName(logging.INFO, logging.getLevelName(logging.INFO))
    logging.addLevelName(logging.ERROR, logging.getLevelName(logging.ERROR))