Как использовать разные форматы с одним и тем же обработчиком ведения журнала в 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))