Python的日志格式可以根据消息日志级别进行修改吗?

我正在使用Python的logging机制将输出打印到屏幕上。 我可以用print语句来做到这一点,但是我希望允许用户更精细的粒度来禁用某些types的输出。 我喜欢打印错误的格式,但是当输出级别是“信息”时,会更喜欢简单的格式。

例如:

  logger.error("Running cmd failed") logger.info("Running cmd passed") 

在这个例子中,我想要以不同的方式打印错误的格式:

 # error Aug 27, 2009 - ERROR: Running cmd failed # info Running cmd passed 

有没有多个日志logging对象可以有不同的日志级别的不同格式? 我宁愿这样做,因为有大量的if / else语句来确定如何logging输出,所以一旦创buildlogging器就不要修改logging器。

是的,你可以通过一个自定义的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 

编辑:

Halloleo的赞美,这里是一个如何在你的脚本中使用上面的例子:

 fmt = MyFormatter() hdlr = logging.StreamHandler(sys.stdout) hdlr.setFormatter(fmt) logging.root.addHandler(hdlr) logging.root.setLevel(DEBUG) 

编辑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 。 我依靠'{'风格的格式,但它可以适应。 可以细化为更一般化,并允许select格式化风格和自定义消息作为__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) 

除了依赖于样式或内部字段外,还可以创build一个格式化器,根据record.levelno(或其他条件)委托给其他格式化器。 我认为这是一个稍微干净的解决scheme。 下面的代码应该适用于任何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) 

我在这里用了一个字典作为input,但显然你也可以使用元组,** kwargs,任何漂浮在你的船上的东西。 这将被用来像这样:

 formatter = VarFormatter({logging.INFO: '[%(message)s]', logging.WARNING: 'warning: %(message)s'}) <... attach formatter to logger ...> 

以上解决scheme适用于3.3.3版本。 但是,3.3.4会出现以下错误。

 FORMATS = { logging.DEBUG : logging._STYLES['{']("{module} DEBUG: {lineno}: {message}"), 

TypeError:“元组”对象不可调用

在日志类Lib \ logging__init __。py中search了一些后,发现数据结构已经从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'), } 

因此更新的解决scheme

 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和self._style等内部variables。