我为python 2 here提出了这个问题,但是当答案不再适用于Python 3.2.3时,又遇到了问题。
这是适用于Python 2.7.3的代码:
import logging
# Attempt to set up a Python3 logger than will print custom messages
# based on each message's logging level.
# The technique recommended for Python2 does not appear to work for
# Python3
class CustomConsoleFormatter(logging.Formatter):
"""
Modify the way DEBUG messages are displayed.
"""
def __init__(self, fmt="%(levelno)d: %(msg)s"):
logging.Formatter.__init__(self, fmt=fmt)
def format(self, record):
# Remember the original format
format_orig = self._fmt
if record.levelno == logging.DEBUG:
self._fmt = "DEBUG: %(msg)s"
# Call the original formatter to do the grunt work
result = logging.Formatter.format(self, record)
# Restore the original format
self._fmt = format_orig
return result
# Set up a logger
my_logger = logging.getLogger("my_custom_logger")
my_logger.setLevel(logging.DEBUG)
my_formatter = CustomConsoleFormatter()
console_handler = logging.StreamHandler()
console_handler.setFormatter(my_formatter)
my_logger.addHandler(console_handler)
my_logger.debug("This is a DEBUG-level message")
my_logger.info("This is an INFO-level message")
使用Python 2.7.3运行:
tcsh-16: python demo_python_2.7.3.py
DEBUG: This is a DEBUG-level message
20: This is an INFO-level message
据我所知,转换为Python3只需要一个微调的CustomConsoleFormatter。 init ():
def __init__(self):
super().__init__(fmt="%(levelno)d: %(msg)s", datefmt=None, style='%')
在Python 3.2.3上:
tcsh-26: python3 demo_python_3.2.3.py
10: This is a DEBUG-level message
20: This is an INFO-level message
正如你所看到的,我想用'DEBUG'取代'10'的愿望正在被挫败。
我已经尝试在Python3源代码中进行挖掘,看起来PercentStyle实例化在我之后破坏了self._fmt,好吧,我自己克服它。
我的伐木剁停止了能够解决这种皱纹。
任何人都可以推荐另一种方式或者指出我忽略的东西吗?
答案 0 :(得分:12)
通过一些挖掘,我能够修改Python 2解决方案以使用Python 3.在Python2中,有必要临时覆盖Formatter._fmt
。在Python3中,对多种格式字符串类型的支持要求我们暂时覆盖Formatter._style._fmt
。
# 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):
super().__init__(fmt="%(levelno)d: %(msg)s", datefmt=None, style='%')
def format(self, record):
# Save the original format configured by the user
# when the logger formatter was instantiated
format_orig = self._style._fmt
# Replace the original format with one customized by logging level
if record.levelno == logging.DEBUG:
self._style._fmt = MyFormatter.dbg_fmt
elif record.levelno == logging.INFO:
self._style._fmt = MyFormatter.info_fmt
elif record.levelno == logging.ERROR:
self._style._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._style._fmt = format_orig
return result
以下是Halloleo的示例,说明如何在脚本中使用上述内容(来自Python2 version of this question):
fmt = MyFormatter()
hdlr = logging.StreamHandler(sys.stdout)
hdlr.setFormatter(fmt)
logging.root.addHandler(hdlr)
logging.root.setLevel(DEBUG)
答案 1 :(得分:3)
another answer的交叉发布。它不起作用,因为logging.Formatter
的新的(3.2 +,3.4现在)实现现在依赖于格式化样式。这取决于'{'
样式格式,但可以进行调整。可以改进为更一般,并允许选择格式样式和自定义消息作为__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)
答案 2 :(得分:2)
我更喜欢这样做,因为它更短,更简单,并且不需要像“ ERROR”这样的字符串进行硬编码。无需重置._fmt
,因为else:
可以很好地解决此问题。
此外,使用"%(msg)s"
不适用于延迟日志记录!
class Formatter(logging.Formatter):
def format(self, record):
if record.levelno == logging.INFO:
self._style._fmt = "%(message)s"
else:
self._style._fmt = "%(levelname)s: %(message)s"
return super().format(record)
用法示例:
import logging
logger = logging.getLogger()
handler = logging.StreamHandler()
handler.setFormatter(Formatter())
logger.setLevel(logging.DEBUG)
logger.addHandler(handler)
logger.debug('foo')
logger.info('bar %d', 4)
DEBUG: foo
bar 4
如果您要为级别名称上色:
class Formatter(logging.Formatter):
def format(self, record):
if record.levelno == logging.INFO:
self._style._fmt = "%(message)s"
else:
color = {
logging.WARNING: 33,
logging.ERROR: 31,
logging.FATAL: 31,
logging.DEBUG: 36
}.get(record.levelno, 0)
self._style._fmt = f"\033[{color}m%(levelname)s\033[0m: %(message)s"
return super().format(record)
有关颜色编号,请参见https://en.wikipedia.org/wiki/ANSI_escape_code#3/4_bit
答案 3 :(得分:1)
我对这个问题迟到了,但这是我的解决方案。它遵循原始的python 2语法风格。通常,由于添加了样式支持,您应该使用三个新类。它们是:PercentStyle,StrFormatStyle和StringTemplateStyle。
from logging import Formatter, PercentStyle, ERROR, WARNING, INFO, DEBUG
class SrvLogFormat(Formatter):
def __init__(self):
super().__init__(fmt=env.fmt_log, datefmt=env.fmt_log_date)
def format(self, record):
original_style = self._style
if record.levelno == DEBUG:
self._style = PercentStyle(env.fmt_dflt)
if record.levelno == INFO:
self._style = PercentStyle(env.fmt_dflt)
if record.levelno == WARNING:
self._style = PercentStyle(env.fmt_dflt)
if record.levelno == ERROR:
self._style = PercentStyle(env.fmt_err)
result = Formatter.format(self, record)
self._style = original_style
return result
答案 4 :(得分:0)
出于某些奇怪的原因,@ JS和@Evpok的解决方案引发了一些错误(我正在使用Python 3.7,这可能就是原因)。
此解决方案对我有用:
class CustomFormatter(logging.Formatter):
"""Logging Formatter to add colors and count warning / errors"""
FORMATS = {
logging.ERROR: "ERROR: %(msg)s",
logging.WARNING: "WARNING: %(msg)s",
logging.DEBUG: "DBG: %(module)s: %(lineno)d: %(msg)s",
"DEFAULT": "%(msg)s",
}
def format(self, record):
log_fmt = self.FORMATS.get(record.levelno, self.FORMATS['DEFAULT'])
formatter = logging.Formatter(log_fmt)
return formatter.format(record)
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
logger_ch = logging.StreamHandler()
logger_ch.setLevel(logging.INFO)
logger_ch.setFormatter(CustomFormatter())
logger.addHandler(logger_ch)
答案 5 :(得分:0)
import logging
from logging import DEBUG, INFO, WARN, ERROR
class LogFormatter(logging.Formatter):
formats = {
DEBUG: "DEBUG: %(msg)s",
INFO: "%(msg)s",
WARN: "WARNING: %(msg)s",
ERROR: "ERROR: %(msg)s"
}
def format(self, record):
return LogFormatter.formats.get(
record.levelno, self._fmt) % record.__dict__
在logging/__init__.py的源码中,PercentStyle的_format
方法很简单
def _format(self, record):
return self._fmt % record.__dict__
因此使用 %
运算符也可以。
答案 6 :(得分:0)
这个 Python3 问题和它的 Python2 问题的答案都存在重大缺陷:
_fmt
成员,因此这是线程不安全的。try
/finally
括起来。确实,该成员根本不应该被修改。解决此问题的一种简单方法是实例化一个或多个不可变的代理格式化程序并根据需要遵循它们。此外,通过二分而不是相等来选择它们,以支持任意级别:
import logging
from bisect import bisect
from logging import getLogger, Formatter, LogRecord, StreamHandler
from typing import Dict
class LevelFormatter(Formatter):
def __init__(self, formats: Dict[int, str], **kwargs):
super().__init__()
if 'fmt' in kwargs:
raise ValueError(
'Format string must be passed to level-surrogate formatters, '
'not this one'
)
self.formats = sorted(
(level, Formatter(fmt, **kwargs)) for level, fmt in formats.items()
)
def format(self, record: LogRecord) -> str:
idx = bisect(self.formats, (record.levelno,), hi=len(self.formats)-1)
level, formatter = self.formats[idx]
return formatter.format(record)
def test():
handler = StreamHandler()
handler.setFormatter(
LevelFormatter(
{
logging.INFO: '%(levelname)s (info): %(message)s',
logging.WARNING: '%(levelname)s: (warning): %(message)s',
}
)
)
handler.setLevel(logging.DEBUG)
logger = getLogger('test_logger')
logger.setLevel(logging.DEBUG)
logger.addHandler(handler)
logger.debug('mdebug')
logger.info('minfo')
logger.log(logging.INFO + 1, 'higher minfo')
logger.warning('mwarning')
logger.error('merror')
logger.critical('mcritical')
test()
输出
DEBUG (info): mdebug
INFO (info): minfo
Level 21: (warning): higher minfo
WARNING: (warning): mwarning
ERROR: (warning): merror
CRITICAL: (warning): mcritical