Можно ли изменить формат ведения журнала Python в зависимости от уровня журнала сообщений?
Я использую механизм Python logging
для вывода вывода на экран. Я мог бы сделать это с помощью операторов печати, но я хочу разрешить более тонкую детализацию для пользователя, чтобы отключить определенные типы вывода. Мне нравится формат, напечатанный для ошибок, но предпочел бы более простой формат, когда выходной уровень "info".
Например:
logger.error("Running cmd failed")
logger.info("Running cmd passed")
В этом примере я хотел бы, чтобы формат ошибки печатался по-разному:
# error
Aug 27, 2009 - ERROR: Running cmd failed
# info
Running cmd passed
Возможно ли иметь разные форматы для разных уровней журналов без наличия нескольких объектов ведения журнала? Я бы предпочел сделать это без изменения регистратора после его создания, так как существует большое количество операторов if/else, чтобы определить, как должен регистрироваться вывод.
Ответы
Ответ 1
Да, вы можете сделать это, имея собственный класс Formatter
:
class MyFormatter(logging.Formatter):
def format(self, record):
#compute s according to record.levelno
#for example, by setting self._fmt
#according to the levelno, then calling
#the superclass to do the actual formatting
return s
Затем присоедините экземпляр MyFormatter
к вашим обработчикам.
Ответ 2
Я просто столкнулся с этой проблемой и не смог заполнить "дыры", оставшиеся в приведенном выше примере. Вот более полная, рабочая версия, которую я использовал. Надеюсь, это поможет кому-то:
# Custom formatter
class MyFormatter(logging.Formatter):
err_fmt = "ERROR: %(msg)s"
dbg_fmt = "DBG: %(module)s: %(lineno)d: %(msg)s"
info_fmt = "%(msg)s"
def __init__(self, fmt="%(levelno)s: %(msg)s"):
logging.Formatter.__init__(self, fmt)
def format(self, record):
# Save the original format configured by the user
# when the logger formatter was instantiated
format_orig = self._fmt
# Replace the original format with one customized by logging level
if record.levelno == logging.DEBUG:
self._fmt = MyFormatter.dbg_fmt
elif record.levelno == logging.INFO:
self._fmt = MyFormatter.info_fmt
elif record.levelno == logging.ERROR:
self._fmt = MyFormatter.err_fmt
# Call the original formatter class to do the grunt work
result = logging.Formatter.format(self, record)
# Restore the original format configured by the user
self._fmt = format_orig
return result
Edit:
Комплименты Halloleo, вот пример того, как использовать приведенное выше в script:
fmt = MyFormatter()
hdlr = logging.StreamHandler(sys.stdout)
hdlr.setFormatter(fmt)
logging.root.addHandler(hdlr)
logging.root.setLevel(DEBUG)
Изменить 2:
Запись в Python3 немного изменилась. См. здесь для подхода Python3.
Ответ 3
И снова, как JS ответ, но более компактный.
class SpecialFormatter(logging.Formatter):
FORMATS = {logging.DEBUG :"DBG: %(module)s: %(lineno)d: %(message)s",
logging.ERROR : "ERROR: %(message)s",
logging.INFO : "%(message)s",
'DEFAULT' : "%(levelname)s: %(message)s"}
def format(self, record):
self._fmt = self.FORMATS.get(record.levelno, self.FORMATS['DEFAULT'])
return logging.Formatter.format(self, record)
hdlr = logging.StreamHandler(sys.stderr)
hdlr.setFormatter(SpecialFormatter())
logging.root.addHandler(hdlr)
logging.root.setLevel(logging.INFO)
Ответ 4
Это адаптация estani answer к новой реализации logging.Formatter
, которая теперь опирается на стили форматирования. Шахта опирается на формат стиля '{'
, но его можно адаптировать. Может быть уточнен, чтобы быть более общим и разрешить выбор стиля форматирования и настраиваемых сообщений в качестве аргументов для __init__
тоже.
class SpecialFormatter(logging.Formatter):
FORMATS = {logging.DEBUG : logging._STYLES['{']("{module} DEBUG: {lineno}: {message}"),
logging.ERROR : logging._STYLES['{']("{module} ERROR: {message}"),
logging.INFO : logging._STYLES['{']("{module}: {message}"),
'DEFAULT' : logging._STYLES['{']("{module}: {message}")}
def format(self, record):
# Ugly. Should be better
self._style = self.FORMATS.get(record.levelno, self.FORMATS['DEFAULT'])
return logging.Formatter.format(self, record)
hdlr = logging.StreamHandler(sys.stderr)
hdlr.setFormatter(SpecialFormatter())
logging.root.addHandler(hdlr)
logging.root.setLevel(logging.INFO)
Ответ 5
Вместо того, чтобы полагаться на стили или внутренние поля, вы также можете создать Formatter, который делегирует другим форматам в зависимости от record.levelno(или других критериев). Это немного более чистое решение по моему скромному мнению. Код ниже должен работать для любой версии python >= 2.7:
Простой способ будет выглядеть примерно так:
class MyFormatter(logging.Formatter):
default_fmt = logging.Formatter('%(levelname)s in %(name)s: %(message)s')
info_fmt = logging.Formatter('%(message)s')
def format(self, record):
if record.levelno == logging.INFO:
return self.info_fmt.format(record)
else:
return self.default_fmt.format(record)
Но вы могли бы сделать его более общим:
class VarFormatter(logging.Formatter):
default_formatter = logging.Formatter('%(levelname)s in %(name)s: %(message)s')
def __init__(self, formats):
""" formats is a dict { loglevel : logformat } """
self.formatters = {}
for loglevel in formats:
self.formatters[loglevel] = logging.Formatter(formats[loglevel])
def format(self, record):
formatter = self.formatters.get(record.levelno, self.default_formatter)
return formatter.format(record)
В качестве входного сигнала я использовал dict, но, очевидно, вы могли бы также использовать кортежи, ** kwargs, независимо от того, что плавает ваша лодка. Затем это будет использоваться как:
formatter = VarFormatter({logging.INFO: '[%(message)s]',
logging.WARNING: 'warning: %(message)s'})
<... attach formatter to logger ...>
Ответ 6
Вышеупомянутое решение работает с выпуском 3.3.3.
Однако с 3.3.4 вы получаете следующую ошибку.
FORMATS = { logging.DEBUG : logging._STYLES['{']("{module} DEBUG: {lineno}: {message}"),
TypeError: объект 'tuple' не может быть вызван
После некоторого поиска в классе ведения журнала
Lib\logging__init __. Ру
Я обнаружил, что структура данных изменилась с 3.3.3 до 3.3.4, что вызывает проблему
3.3.3
_STYLES = {
'%': PercentStyle,
'{': StrFormatStyle,
'$': StringTemplateStyle
}
3.3.4
_STYLES = {
'%': (PercentStyle, BASIC_FORMAT),
'{': (StrFormatStyle, '{levelname}:{name}:{message} AA'),
'$': (StringTemplateStyle, '${levelname}:${name}:${message} BB'),
}
Таким образом, обновленное решение
class SpecialFormatter(logging.Formatter):
FORMATS = {logging.DEBUG : logging._STYLES['{'][0]("{module} DEBUG: {lineno}: {message}"),
logging.ERROR : logging._STYLES['{'][0]("{module} ERROR: {message}"),
logging.INFO : logging._STYLES['{'][0]("{module}: {message}"),
'DEFAULT' : logging._STYLES['{'][0]("{module}: {message}")}
def format(self, record):
# Ugly. Should be better
self._style = self.FORMATS.get(record.levelno, self.FORMATS['DEFAULT'])
return logging.Formatter.format(self, record)
Ответ 7
Если вы просто хотите пропустить форматирование определенных уровней, вы можете сделать что-то более простое, чем другие ответы, например:
class FormatterNotFormattingInfo(logging.Formatter):
def __init__(self, fmt = '%(levelname)s:%(message)s'):
logging.Formatter.__init__(self, fmt)
def format(self, record):
if record.levelno == logging.INFO:
return record.getMessage()
return logging.Formatter.format(self, record)
Это также имеет преимущество работы до и после выпуска 3.2, не используя внутренние переменные, такие как self._fmt и self._style.
Ответ 8
ОДИН СПОСОБ ДЕЛАТЬ ЭТО
Определить класс
import logging
class CustomFormatter(logging.Formatter):
"""Logging Formatter to add colors and count warning / errors"""
grey = "\x1b[38;21m"
yellow = "\x1b[33;21m"
red = "\x1b[31;21m"
bold_red = "\x1b[31;1m"
reset = "\x1b[0m"
format = "%(asctime)s - %(name)s - %(levelname)s - %(message)s (%(filename)s:%(lineno)d)"
FORMATS = {
logging.DEBUG: grey + format + reset,
logging.INFO: grey + format + reset,
logging.WARNING: yellow + format + reset,
logging.ERROR: red + format + reset,
logging.CRITICAL: bold_red + format + reset
}
def format(self, record):
log_fmt = self.FORMATS.get(record.levelno)
formatter = logging.Formatter(log_fmt)
return formatter.format(record)
Создайте логгер
# create logger with 'spam_application'
logger = logging.getLogger("My_app")
logger.setLevel(logging.DEBUG)
# create console handler with a higher log level
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
ch.setFormatter(CustomFormatter())
logger.addHandler(ch)
И использовать!
logger.debug("debug message")
logger.info("info message")
logger.warning("warning message")
logger.error("error message")
logger.critical("critical message")
Результат ![enter image description here]()