Можно ли изменить формат ведения журнала Python в зависимости от уровня журнала сообщений?
Я использую в Python logging
механизм для печати вывода на экран. Я мог бы сделать это с помощью инструкций print, но я хочу разрешить более тонкую настройку детализации для пользователя, чтобы отключить определенные типы вывода. Мне нравится формат, напечатанный для ошибок, но предпочел бы более простой формат, когда уровень вывода - "информация."
например:
logger.error("Running cmd failed")
logger.info("Running cmd passed")
в этом примере я хотел бы, чтобы формат ошибки был напечатан иначе:
# error Aug 27, 2009 - ERROR: Running cmd failed # info Running cmd passed
возможно ли иметь разные форматы для разных уровней журнала без наличия нескольких объектов ведения журнала? Я бы предпочел сделать это без изменения регистратора после его создания, поскольку существует большое количество операторов if/else для определения того, как вывод должен быть зарегистрирован.
7 ответов:
Да, вы можете сделать это, имея собственный
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
экземпляр руки.
Я просто столкнулся с этой проблемой и проблема с заполнением "дыр" оставленных в приведенном выше примере. Вот более полная, рабочая версия, которую я использовал. Надеюсь, это поможет кому-то:
# 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, вот пример того, как использовать выше в вашем скрипте:
fmt = MyFormatter() hdlr = logging.StreamHandler(sys.stdout) hdlr.setFormatter(fmt) logging.root.addHandler(hdlr) logging.root.setLevel(DEBUG)
Edit 2:
Регистрация Python3 немного изменилась. Смотрите здесь для Python3 подход.
и снова, как 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)
это адаптация estani это к новой реализации
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)
вместо того, чтобы полагаться на стили или внутренние поля, вы могли бы также создать Праматерию, что делегаты других средств форматирования в зависимости от записи.левельно (или по другим критериям). Это немного более чистое решение, на мой скромный взгляд. Приведенный ниже код должен работать для любой версии 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)
я использовал дикт в качестве ввода здесь, но, очевидно, вы могли бы также используйте кортежи, * * кварги, все, что плавает на вашей лодке. Это будет использоваться как:
formatter = VarFormatter({logging.INFO: '[%(message)s]', logging.WARNING: 'warning: %(message)s'}) <... attach formatter to logger ...>
вышеуказанное решение работает с выпуском 3.3.3. Однако с 3.3.4 вы получаете следующую ошибку.
FORMATS = { logging.DEBUG : logging._STYLES['{']("{module} DEBUG: {lineno}: {message}"),
TypeError: объект' tuple ' не вызывается
после некоторого поиска в классе ведения журнала Lib\logging__init__.py Я обнаружил, что структура данных изменилась с 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)
Если вы просто хотите пропустить форматирование определенных уровней, вы можете сделать что-то проще, чем другие ответы, такие как:
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, ни собственной._стиль.