首页 文章

如何为Python日志输出着色?

提问于
浏览
269

前段时间,我看到一个带有彩色输出的Mono应用程序,可能是因为它的日志系统(因为所有的消息都是标准化的) .

现在,Python有 logging 模块,它允许您指定许多选项来自定义输出 . 所以,我想象Python可能会有类似的东西,但我无法在任何地方找到如何做到这一点 .

有没有办法让Python logging 模块输出颜色?

我想要的(例如)红色错误,蓝色或黄色调试消息,等等 .

当然这可能需要一个兼容的终端(大多数现代终端);但如果不支持颜色,我可以回退到原来的 logging 输出 .

有关如何使用记录模块获得彩色输出的任何想法?

24 回答

  • 2

    我从airmind支持前景和背景标签更新了示例 . 只需在日志格式化程序字符串中使用颜色变量$ BLACK - $ WHITE . 设置背景只需使用$ BG-BLACK - $ BG-WHITE .

    import logging
    
    BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8)
    
    COLORS = {
        'WARNING'  : YELLOW,
        'INFO'     : WHITE,
        'DEBUG'    : BLUE,
        'CRITICAL' : YELLOW,
        'ERROR'    : RED,
        'RED'      : RED,
        'GREEN'    : GREEN,
        'YELLOW'   : YELLOW,
        'BLUE'     : BLUE,
        'MAGENTA'  : MAGENTA,
        'CYAN'     : CYAN,
        'WHITE'    : WHITE,
    }
    
    RESET_SEQ = "\033[0m"
    COLOR_SEQ = "\033[1;%dm"
    BOLD_SEQ  = "\033[1m"
    
    class ColorFormatter(logging.Formatter):
    
        def __init__(self, *args, **kwargs):
            # can't do super(...) here because Formatter is an old school class
            logging.Formatter.__init__(self, *args, **kwargs)
    
        def format(self, record):
            levelname = record.levelname
            color     = COLOR_SEQ % (30 + COLORS[levelname])
            message   = logging.Formatter.format(self, record)
            message   = message.replace("$RESET", RESET_SEQ)\
                               .replace("$BOLD",  BOLD_SEQ)\
                               .replace("$COLOR", color)
            for k,v in COLORS.items():
                message = message.replace("$" + k,    COLOR_SEQ % (v+30))\
                                 .replace("$BG" + k,  COLOR_SEQ % (v+40))\
                                 .replace("$BG-" + k, COLOR_SEQ % (v+40))
            return message + RESET_SEQ
    
    logging.ColorFormatter = ColorFormatter
    

    所以现在您可以在配置文件中简单地执行以下操作:

    [formatter_colorFormatter]
    class=logging.ColorFormatter
    format= $COLOR%(levelname)s $RESET %(asctime)s $BOLD$COLOR%(name)s$RESET %(message)s
    
  • 1

    我有两个要添加的提交,其中一个只为消息着色(ColoredFormatter),其中一个着色整行(ColorizingStreamHandler) . 这些还包括比以前的解决方案更多的ANSI色码 .

    一些内容来自(修改)来自:上面的帖子,和http://plumberjack.blogspot.com/2010/12/colorizing-logging-output-in-terminals.html .

    仅为消息着色:

    class ColoredFormatter(logging.Formatter):
        """Special custom formatter for colorizing log messages!"""
    
        BLACK = '\033[0;30m'
        RED = '\033[0;31m'
        GREEN = '\033[0;32m'
        BROWN = '\033[0;33m'
        BLUE = '\033[0;34m'
        PURPLE = '\033[0;35m'
        CYAN = '\033[0;36m'
        GREY = '\033[0;37m'
    
        DARK_GREY = '\033[1;30m'
        LIGHT_RED = '\033[1;31m'
        LIGHT_GREEN = '\033[1;32m'
        YELLOW = '\033[1;33m'
        LIGHT_BLUE = '\033[1;34m'
        LIGHT_PURPLE = '\033[1;35m'
        LIGHT_CYAN = '\033[1;36m'
        WHITE = '\033[1;37m'
    
        RESET = "\033[0m"
    
        def __init__(self, *args, **kwargs):
            self._colors = {logging.DEBUG: self.DARK_GREY,
                            logging.INFO: self.RESET,
                            logging.WARNING: self.BROWN,
                            logging.ERROR: self.RED,
                            logging.CRITICAL: self.LIGHT_RED}
            super(ColoredFormatter, self).__init__(*args, **kwargs)
    
        def format(self, record):
            """Applies the color formats"""
            record.msg = self._colors[record.levelno] + record.msg + self.RESET
            return logging.Formatter.format(self, record)
    
        def setLevelColor(self, logging_level, escaped_ansi_code):
            self._colors[logging_level] = escaped_ansi_code
    

    整色线着色:

    class ColorizingStreamHandler(logging.StreamHandler):
    
        BLACK = '\033[0;30m'
        RED = '\033[0;31m'
        GREEN = '\033[0;32m'
        BROWN = '\033[0;33m'
        BLUE = '\033[0;34m'
        PURPLE = '\033[0;35m'
        CYAN = '\033[0;36m'
        GREY = '\033[0;37m'
    
        DARK_GREY = '\033[1;30m'
        LIGHT_RED = '\033[1;31m'
        LIGHT_GREEN = '\033[1;32m'
        YELLOW = '\033[1;33m'
        LIGHT_BLUE = '\033[1;34m'
        LIGHT_PURPLE = '\033[1;35m'
        LIGHT_CYAN = '\033[1;36m'
        WHITE = '\033[1;37m'
    
        RESET = "\033[0m"
    
        def __init__(self, *args, **kwargs):
            self._colors = {logging.DEBUG: self.DARK_GREY,
                            logging.INFO: self.RESET,
                            logging.WARNING: self.BROWN,
                            logging.ERROR: self.RED,
                            logging.CRITICAL: self.LIGHT_RED}
            super(ColorizingStreamHandler, self).__init__(*args, **kwargs)
    
        @property
        def is_tty(self):
            isatty = getattr(self.stream, 'isatty', None)
            return isatty and isatty()
    
        def emit(self, record):
            try:
                message = self.format(record)
                stream = self.stream
                if not self.is_tty:
                    stream.write(message)
                else:
                    message = self._colors[record.levelno] + message + self.RESET
                    stream.write(message)
                stream.write(getattr(self, 'terminator', '\n'))
                self.flush()
            except (KeyboardInterrupt, SystemExit):
                raise
            except:
                self.handleError(record)
    
        def setLevelColor(self, logging_level, escaped_ansi_code):
            self._colors[logging_level] = escaped_ansi_code
    
  • -1

    我已经知道了颜色逃逸,我刚才在我的bash提示中使用过它们 . 不管怎么说,还是要谢谢你 .
    我想要的是将它与日志记录模块集成,我最终在经过几次尝试和错误之后做了 .
    这是我最终得到的:

    BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8)
    
    #The background is set with 40 plus the number of the color, and the foreground with 30
    
    #These are the sequences need to get colored ouput
    RESET_SEQ = "\033[0m"
    COLOR_SEQ = "\033[1;%dm"
    BOLD_SEQ = "\033[1m"
    
    def formatter_message(message, use_color = True):
        if use_color:
            message = message.replace("$RESET", RESET_SEQ).replace("$BOLD", BOLD_SEQ)
        else:
            message = message.replace("$RESET", "").replace("$BOLD", "")
        return message
    
    COLORS = {
        'WARNING': YELLOW,
        'INFO': WHITE,
        'DEBUG': BLUE,
        'CRITICAL': YELLOW,
        'ERROR': RED
    }
    
    class ColoredFormatter(logging.Formatter):
        def __init__(self, msg, use_color = True):
            logging.Formatter.__init__(self, msg)
            self.use_color = use_color
    
        def format(self, record):
            levelname = record.levelname
            if self.use_color and levelname in COLORS:
                levelname_color = COLOR_SEQ % (30 + COLORS[levelname]) + levelname + RESET_SEQ
                record.levelname = levelname_color
            return logging.Formatter.format(self, record)
    

    要使用它,请创建自己的Logger:

    # Custom logger class with multiple destinations
    class ColoredLogger(logging.Logger):
        FORMAT = "[$BOLD%(name)-20s$RESET][%(levelname)-18s]  %(message)s ($BOLD%(filename)s$RESET:%(lineno)d)"
        COLOR_FORMAT = formatter_message(FORMAT, True)
        def __init__(self, name):
            logging.Logger.__init__(self, name, logging.DEBUG)                
    
            color_formatter = ColoredFormatter(self.COLOR_FORMAT)
    
            console = logging.StreamHandler()
            console.setFormatter(color_formatter)
    
            self.addHandler(console)
            return
    
    
    logging.setLoggerClass(ColoredLogger)
    

    以防万一其他人需要它 .

    如果您使用多个 Logger 或处理程序,请小心: ColoredFormatter 正在更改记录对象,该记录对象将进一步传递给其他处理程序或传播到其他记录程序 . 如果你已经配置了文件 Logger 等,你可能最好在操作levelname属性之前简单地创建一个带有 copy.copy()record 副本,或者在返回格式化字符串之前将levelname重置为前一个值(信用证为Michael in评论) .

  • 5

    几年前我写了一个彩色的流处理程序供我自己使用 . 然后我遇到了这个页面,发现了一些人们正在复制/粘贴的代码片段:-( . 我的流处理程序目前只适用于UNIX(Linux,Mac OS X),但优点是它是available on PyPI(和GitHub)和它很简单易用 . 它还有一个Vim语法模式:-) . 将来我可以将其扩展到Windows上 .

    To install the package:

    $ pip install coloredlogs
    

    To confirm that it works:

    $ coloredlogs --demo
    

    To get started with your own code:

    $ python
    > import coloredlogs, logging
    > coloredlogs.install()
    > logging.info("It works!")
    2014-07-30 21:21:26 peter-macbook root[7471] INFO It works!
    

    上例中显示的默认日志格式包含日期,时间,主机名, Logger 名称,PID,日志级别和日志消息 . 这就是它在实践中的样子:

    Screenshot of coloredlogs output

  • 47

    这是一个适用于任何平台的解决方案 . 如果它不只是告诉我,我会更新它 .

    工作原理:在支持ANSI转义的平台上使用它们(非Windows),在Windows上,它确实使用API调用来更改控制台颜色 .

    该脚本会破坏标准库中的logging.StreamHandler.emit方法,为其添加包装器 .

    TestColorer.py

    # Usage: add Colorer.py near you script and import it.
    import logging
    import Colorer
    
    logging.warn("a warning")
    logging.error("some error")
    logging.info("some info")
    

    Colorer.py

    #!/usr/bin/env python
    # encoding: utf-8
    import logging
    # now we patch Python code to add color support to logging.StreamHandler
    def add_coloring_to_emit_windows(fn):
            # add methods we need to the class
        def _out_handle(self):
            import ctypes
            return ctypes.windll.kernel32.GetStdHandle(self.STD_OUTPUT_HANDLE)
        out_handle = property(_out_handle)
    
        def _set_color(self, code):
            import ctypes
            # Constants from the Windows API
            self.STD_OUTPUT_HANDLE = -11
            hdl = ctypes.windll.kernel32.GetStdHandle(self.STD_OUTPUT_HANDLE)
            ctypes.windll.kernel32.SetConsoleTextAttribute(hdl, code)
    
        setattr(logging.StreamHandler, '_set_color', _set_color)
    
        def new(*args):
            FOREGROUND_BLUE      = 0x0001 # text color contains blue.
            FOREGROUND_GREEN     = 0x0002 # text color contains green.
            FOREGROUND_RED       = 0x0004 # text color contains red.
            FOREGROUND_INTENSITY = 0x0008 # text color is intensified.
            FOREGROUND_WHITE     = FOREGROUND_BLUE|FOREGROUND_GREEN |FOREGROUND_RED
           # winbase.h
            STD_INPUT_HANDLE = -10
            STD_OUTPUT_HANDLE = -11
            STD_ERROR_HANDLE = -12
    
            # wincon.h
            FOREGROUND_BLACK     = 0x0000
            FOREGROUND_BLUE      = 0x0001
            FOREGROUND_GREEN     = 0x0002
            FOREGROUND_CYAN      = 0x0003
            FOREGROUND_RED       = 0x0004
            FOREGROUND_MAGENTA   = 0x0005
            FOREGROUND_YELLOW    = 0x0006
            FOREGROUND_GREY      = 0x0007
            FOREGROUND_INTENSITY = 0x0008 # foreground color is intensified.
    
            BACKGROUND_BLACK     = 0x0000
            BACKGROUND_BLUE      = 0x0010
            BACKGROUND_GREEN     = 0x0020
            BACKGROUND_CYAN      = 0x0030
            BACKGROUND_RED       = 0x0040
            BACKGROUND_MAGENTA   = 0x0050
            BACKGROUND_YELLOW    = 0x0060
            BACKGROUND_GREY      = 0x0070
            BACKGROUND_INTENSITY = 0x0080 # background color is intensified.     
    
            levelno = args[1].levelno
            if(levelno>=50):
                color = BACKGROUND_YELLOW | FOREGROUND_RED | FOREGROUND_INTENSITY | BACKGROUND_INTENSITY 
            elif(levelno>=40):
                color = FOREGROUND_RED | FOREGROUND_INTENSITY
            elif(levelno>=30):
                color = FOREGROUND_YELLOW | FOREGROUND_INTENSITY
            elif(levelno>=20):
                color = FOREGROUND_GREEN
            elif(levelno>=10):
                color = FOREGROUND_MAGENTA
            else:
                color =  FOREGROUND_WHITE
            args[0]._set_color(color)
    
            ret = fn(*args)
            args[0]._set_color( FOREGROUND_WHITE )
            #print "after"
            return ret
        return new
    
    def add_coloring_to_emit_ansi(fn):
        # add methods we need to the class
        def new(*args):
            levelno = args[1].levelno
            if(levelno>=50):
                color = '\x1b[31m' # red
            elif(levelno>=40):
                color = '\x1b[31m' # red
            elif(levelno>=30):
                color = '\x1b[33m' # yellow
            elif(levelno>=20):
                color = '\x1b[32m' # green 
            elif(levelno>=10):
                color = '\x1b[35m' # pink
            else:
                color = '\x1b[0m' # normal
            args[1].msg = color + args[1].msg +  '\x1b[0m'  # normal
            #print "after"
            return fn(*args)
        return new
    
    import platform
    if platform.system()=='Windows':
        # Windows does not support ANSI escapes and we are using API calls to set the console color
        logging.StreamHandler.emit = add_coloring_to_emit_windows(logging.StreamHandler.emit)
    else:
        # all non-Windows platforms are supporting ANSI escapes so we use them
        logging.StreamHandler.emit = add_coloring_to_emit_ansi(logging.StreamHandler.emit)
        #log = logging.getLogger()
        #log.addFilter(log_filter())
        #//hdlr = logging.StreamHandler()
        #//hdlr.setFormatter(formatter())
    
  • 52

    针对预定义日志级别的快速而脏的解决方案,无需定义新类 .

    logging.addLevelName( logging.WARNING, "\033[1;31m%s\033[1;0m" % logging.getLevelName(logging.WARNING))
    logging.addLevelName( logging.ERROR, "\033[1;41m%s\033[1;0m" % logging.getLevelName(logging.ERROR))
    
  • 14

    Update :因为这是一个让我长时间搔痒的痒,我继续为像我这样只想简单做事的懒人写了一个图书馆:zenlog

    Colorlog非常适合这种情况 . 它是available on PyPI(因此可以通过 pip install colorlog 安装)并且是actively maintained .

    这是一个快速复制和粘贴的代码片段,用于设置日志记录和打印体面的日志消息:

    import logging
    LOG_LEVEL = logging.DEBUG
    LOGFORMAT = "  %(log_color)s%(levelname)-8s%(reset)s | %(log_color)s%(message)s%(reset)s"
    from colorlog import ColoredFormatter
    logging.root.setLevel(LOG_LEVEL)
    formatter = ColoredFormatter(LOGFORMAT)
    stream = logging.StreamHandler()
    stream.setLevel(LOG_LEVEL)
    stream.setFormatter(formatter)
    log = logging.getLogger('pythonConfig')
    log.setLevel(LOG_LEVEL)
    log.addHandler(stream)
    
    log.debug("A quirky message only developers care about")
    log.info("Curious users might want to know this")
    log.warn("Something is wrong and any user should be informed")
    log.error("Serious stuff, this is red for a reason")
    log.critical("OH NO everything is on fire")
    

    输出:

    Colorlog output

  • 0

    您可以导入colorlog模块并使用其 ColoredFormatter 来着色日志消息 .

    示例

    主模块的锅炉板:

    import logging
    import os
    import sys
    try:
        import colorlog
    except ImportError:
        pass
    
    def setup_logging():
        root = logging.getLogger()
        root.setLevel(logging.DEBUG)
        format      = '%(asctime)s - %(levelname)-8s - %(message)s'
        date_format = '%Y-%m-%d %H:%M:%S'
        if 'colorlog' in sys.modules and os.isatty(2):
            cformat = '%(log_color)s' + format
            f = colorlog.ColoredFormatter(cformat, date_format,
                  log_colors = { 'DEBUG'   : 'reset',       'INFO' : 'reset',
                                 'WARNING' : 'bold_yellow', 'ERROR': 'bold_red',
                                 'CRITICAL': 'bold_red' })
        else:
            f = logging.Formatter(format, date_format)
        ch = logging.StreamHandler()
        ch.setFormatter(f)
        root.addHandler(ch)
    
    setup_logging()
    log = logging.getLogger(__name__)
    

    如果安装了colorlog模块并且输出实际上发送到终端,则代码仅启用日志消息中的颜色 . 这样可以避免在重定向日志输出时将转义序列写入文件 .

    此外,设置自定义颜色方案,更适合具有深色背景的终端 .

    一些示例记录调用:

    log.debug   ('Hello Debug')
    log.info    ('Hello Info')
    log.warn    ('Hello Warn')
    log.error   ('Hello Error')
    log.critical('Hello Critical')
    

    输出:

    enter image description here

  • 9

    请看以下解决方案 . 流处理程序应该是着色的东西,然后你可以选择着色单词而不是整行(使用Formatter) .

    http://plumberjack.blogspot.com/2010/12/colorizing-logging-output-in-terminals.html

  • 10

    我将Sorin提供的原始示例和子类化的StreamHandler修改为ColorizedConsoleHandler .

    该他们的解决方案的缺点是它修改了消息,因为这正在修改实际的logmessage,任何其他处理程序也将获得修改后的消息 .

    由于我们使用多个 Logger ,因此在我们的案例中导致了带有颜色代码的日志文件 .

    下面的类只适用于支持ansi的平台,但是向它添加windows颜色代码应该是微不足道的 .

    import copy
    import logging
    
    
    class ColoredConsoleHandler(logging.StreamHandler):
        def emit(self, record):
            # Need to make a actual copy of the record
            # to prevent altering the message for other loggers
            myrecord = copy.copy(record)
            levelno = myrecord.levelno
            if(levelno >= 50):  # CRITICAL / FATAL
                color = '\x1b[31m'  # red
            elif(levelno >= 40):  # ERROR
                color = '\x1b[31m'  # red
            elif(levelno >= 30):  # WARNING
                color = '\x1b[33m'  # yellow
            elif(levelno >= 20):  # INFO
                color = '\x1b[32m'  # green
            elif(levelno >= 10):  # DEBUG
                color = '\x1b[35m'  # pink
            else:  # NOTSET and anything else
                color = '\x1b[0m'  # normal
            myrecord.msg = color + str(myrecord.msg) + '\x1b[0m'  # normal
            logging.StreamHandler.emit(self, myrecord)
    
  • 1

    现在有一个已发布的PyPi模块可用于可自定义的彩色日志输出:

    https://pypi.python.org/pypi/rainbow_logging_handler/

    https://github.com/laysakura/rainbow_logging_handler

    • 支持Windows

    • 支持Django

    • 可自定义的颜色

    由于它是作为Python egg分发的,因此很容易为任何Python应用程序安装 .

  • 1

    airmind方法的另一个小混音,将所有内容保持在一个类中:

    class ColorFormatter(logging.Formatter):
      FORMAT = ("[$BOLD%(name)-20s$RESET][%(levelname)-18s]  "
                "%(message)s "
                "($BOLD%(filename)s$RESET:%(lineno)d)")
    
      BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8)
    
      RESET_SEQ = "\033[0m"
      COLOR_SEQ = "\033[1;%dm"
      BOLD_SEQ = "\033[1m"
    
      COLORS = {
        'WARNING': YELLOW,
        'INFO': WHITE,
        'DEBUG': BLUE,
        'CRITICAL': YELLOW,
        'ERROR': RED
      }
    
      def formatter_msg(self, msg, use_color = True):
        if use_color:
          msg = msg.replace("$RESET", self.RESET_SEQ).replace("$BOLD", self.BOLD_SEQ)
        else:
          msg = msg.replace("$RESET", "").replace("$BOLD", "")
        return msg
    
      def __init__(self, use_color=True):
        msg = self.formatter_msg(self.FORMAT, use_color)
        logging.Formatter.__init__(self, msg)
        self.use_color = use_color
    
      def format(self, record):
        levelname = record.levelname
        if self.use_color and levelname in self.COLORS:
          fore_color = 30 + self.COLORS[levelname]
          levelname_color = self.COLOR_SEQ % fore_color + levelname + self.RESET_SEQ
          record.levelname = levelname_color
        return logging.Formatter.format(self, record)
    

    要使用将格式化程序附加到处理程序,例如:

    handler.setFormatter(ColorFormatter())
    logger.addHandler(handler)
    
  • 156
    import logging
    import sys
    
    colors = {'pink': '\033[95m', 'blue': '\033[94m', 'green': '\033[92m', 'yellow': '\033[93m', 'red': '\033[91m',
          'ENDC': '\033[0m', 'bold': '\033[1m', 'underline': '\033[4m'}
    
    logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
    
    
    def str_color(color, data):
        return colors[color] + str(data) + colors['ENDC']
    
    params = {'param1': id1, 'param2': id2}
    
    logging.info('\nParams:' + str_color("blue", str(params)))`
    
  • 12

    用于着色任何终端文本的简单但非常灵活的工具是'colout' .

    pip install colout
    myprocess | colout REGEX_WITH_GROUPS color1,color2...
    

    'myprocess'输出中与正则表达式组1匹配的任何文本将使用color1,第2组使用color2等进行着色 .

    例如:

    tail -f /var/log/mylogfile | colout '^(\w+ \d+ [\d:]+)|(\w+\.py:\d+ .+\(\)): (.+)$' white,black,cyan bold,bold,normal
    

    即第一个正则表达式组(parens)与日志文件中的初始日期匹配,第二个组匹配python文件名,行号和函数名称,第三个组匹配之后的日志消息 . 我还使用'粗体/法线'的并行序列以及颜色序列 . 这看起来像:

    logfile with colored formatting

    请注意,与我的任何正则表达式不匹配的行或部分行仍然会回显,因此这不像'grep --color' - 没有任何内容从输出中过滤掉 .

    显然这很灵活,你可以在任何进程中使用它,而不仅仅是拖尾日志文件 . 我通常只要想要为某些东西着色,就可以动态地制作一个新的正则表达式 . 出于这个原因,我更喜欢colout到任何自定义日志文件着色工具,因为我只需要学习一个工具,无论我着色的是什么:日志,测试输出,语法突出显示终端中的代码片段等 .

    它还避免在日志文件本身中实际转储ANSI代码,这是一个坏主意,因为它会破坏日志文件中的模式,除非你总是记得匹配grep正则表达式中的ANSI代码 .

  • 5

    有很多回应 . 但没有人在谈论装饰者 . 所以这是我的 .

    因为它更简单 .

    不需要导入任何东西,也不需要编写任何子类:

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    
    
    import logging
    
    
    NO_COLOR = "\33[m"
    RED, GREEN, ORANGE, BLUE, PURPLE, LBLUE, GREY = \
        map("\33[%dm".__mod__, range(31, 38))
    
    logging.basicConfig(format="%(message)s", level=logging.DEBUG)
    logger = logging.getLogger(__name__)
    
    # the decorator to apply on the logger methods info, warn, ...
    def add_color(logger_method, color):
      def wrapper(message, *args, **kwargs):
        return logger_method(
          # the coloring is applied here.
          color+message+NO_COLOR,
          *args, **kwargs
        )
      return wrapper
    
    for level, color in zip((
      "info", "warn", "error", "debug"), (
      GREEN, ORANGE, RED, BLUE
    )):
      setattr(logger, level, add_color(getattr(logger, level), color))
    
    # this is displayed in red.
    logger.error("Launching %s." % __file__)
    

    这将错误设置为红色,将调试消息设置为蓝色,等等 . 就像问题中的问题一样 .

    我们甚至可以调整包装器来获取 color 参数来动态设置消息的颜色使用 logger.debug("message", color=GREY)

    编辑:所以这里是适应装饰器在运行时设置颜色:

    def add_color(logger_method, _color):
      def wrapper(message, *args, **kwargs):
        color = kwargs.pop("color", _color)
        if isinstance(color, int):
          color = "\33[%dm" % color
        return logger_method(
          # the coloring is applied here.
          color+message+NO_COLOR,
          *args, **kwargs
        )
      return wrapper
    
    # blah blah, apply the decorator...
    
    # this is displayed in red.
    logger.error("Launching %s." % __file__)
    # this is displayed in blue
    logger.error("Launching %s." % __file__, color=34)
    # and this, in grey
    logger.error("Launching %s." % __file__, color=GREY)
    
  • 5

    好吧,我想我也可以添加我的彩色 Logger 的变化 .

    这并不奇怪,但它使用起来非常简单,并且不会更改记录对象,从而避免在使用文件处理程序时将ANSI转义序列记录到日志文件中 . 它不会影响日志消息格式 .

    如果您已经在使用logging module's Formatter,那么要获得彩色级别名称,您只需要使用ColoredFormatter替换您的律师处理程序Formatter . 如果您要记录整个应用程序,则只需要为顶级 Logger 执行此操作 .

    colored_log.py

    #!/usr/bin/env python
    
    from copy import copy
    from logging import Formatter
    
    MAPPING = {
        'DEBUG'   : 37, # white
        'INFO'    : 36, # cyan
        'WARNING' : 33, # yellow
        'ERROR'   : 31, # red
        'CRITICAL': 41, # white on red bg
    }
    
    PREFIX = '\033['
    SUFFIX = '\033[0m'
    
    class ColoredFormatter(Formatter):
    
        def __init__(self, patern):
            Formatter.__init__(self, patern)
    
        def format(self, record):
            colored_record = copy(record)
            levelname = colored_record.levelname
            seq = MAPPING.get(levelname, 37) # default white
            colored_levelname = ('{0}{1}m{2}{3}') \
                .format(PREFIX, seq, levelname, SUFFIX)
            colored_record.levelname = colored_levelname
            return Formatter.format(self, colored_record)
    

    示例用法

    app.py

    #!/usr/bin/env python
    
    import logging
    from colored_log import ColoredFormatter
    
    # Create top level logger
    log = logging.getLogger("main")
    
    # Add console handler using our custom ColoredFormatter
    ch = logging.StreamHandler()
    ch.setLevel(logging.DEBUG)
    cf = ColoredFormatter("[%(name)s][%(levelname)s]  %(message)s (%(filename)s:%(lineno)d)")
    ch.setFormatter(cf)
    log.addHandler(ch)
    
    # Add file handler
    fh = logging.FileHandler('app.log')
    fh.setLevel(logging.DEBUG)
    ff = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    fh.setFormatter(ff)
    log.addHandler(fh)
    
    # Set log level
    log.setLevel(logging.DEBUG)
    
    # Log some stuff
    log.debug("app has started")
    log.info("Logging to 'app.log' in the script dir")
    log.warning("This is my last warning, take heed")
    log.error("This is an error")
    log.critical("He's dead, Jim")
    
    # Import a sub-module 
    import sub_module
    

    sub_module.py

    #!/usr/bin/env python
    
    import logging
    log = logging.getLogger('main.sub_module')
    
    log.debug("Hello from the sub module")
    

    结果

    Terminal output

    Terminal output

    app.log 内容

    2017-09-29 00:32:23,434 - main - DEBUG - app has started
    2017-09-29 00:32:23,434 - main - INFO - Logging to 'app.log' in the script dir
    2017-09-29 00:32:23,435 - main - WARNING - This is my last warning, take heed
    2017-09-29 00:32:23,435 - main - ERROR - This is an error
    2017-09-29 00:32:23,435 - main - CRITICAL - He's dead, Jim
    2017-09-29 00:32:23,435 - main.sub_module - ERROR - Hello from the sub module
    

    当然,您可以根据需要设置格式化终端和日志文件输出 . 只有日志级别才会着色 .

    我希望有人觉得这很有用,而且不仅仅是太多了 . :)

    Python示例文件可以从这个GitHub Gist下载:https://gist.github.com/KurtJacobson/48e750701acec40c7161b5a2f79e6bfd

  • 0

    这是我的解决方案:

    class ColouredFormatter(logging.Formatter):
        RESET = '\x1B[0m'
        RED = '\x1B[31m'
        YELLOW = '\x1B[33m'
        BRGREEN = '\x1B[01;32m'  # grey in solarized for terminals
    
        def format(self, record, colour=False):
            message = super().format(record)
    
            if not colour:
                return message
    
            level_no = record.levelno
            if level_no >= logging.CRITICAL:
                colour = self.RED
            elif level_no >= logging.ERROR:
                colour = self.RED
            elif level_no >= logging.WARNING:
                colour = self.YELLOW
            elif level_no >= logging.INFO:
                colour = self.RESET
            elif level_no >= logging.DEBUG:
                colour = self.BRGREEN
            else:
                colour = self.RESET
    
            message = colour + message + self.RESET
    
            return message
    
    
    class ColouredHandler(logging.StreamHandler):
        def __init__(self, stream=sys.stdout):
            super().__init__(stream)
    
        def format(self, record, colour=False):
            if not isinstance(self.formatter, ColouredFormatter):
                self.formatter = ColouredFormatter()
    
            return self.formatter.format(record, colour)
    
        def emit(self, record):
            stream = self.stream
            try:
                msg = self.format(record, stream.isatty())
                stream.write(msg)
                stream.write(self.terminator)
                self.flush()
            except Exception:
                self.handleError(record)
    
    
    h = ColouredHandler()
    h.formatter = ColouredFormatter('{asctime} {levelname:8} {message}', '%Y-%m-%d %H:%M:%S', '{')
    logging.basicConfig(level=logging.DEBUG, handlers=[h])
    
  • 0

    我遇到麻烦的是正确设置格式化程序:

    class ColouredFormatter(logging.Formatter):    
        def __init__(self, msg):
            logging.Formatter.__init__(self, msg)
            self._init_colour = _get_colour()
    
        def close(self):
            # restore the colour information to what it was
            _set_colour(self._init_colour)
    
        def format(self, record):        
            # Add your own colourer based on the other examples
            _set_colour( LOG_LEVEL_COLOUR[record.levelno] )
            return logging.Formatter.format(self, record)         
    
    def init():
        # Set up the formatter. Needs to be first thing done.
        rootLogger = logging.getLogger()
        hdlr = logging.StreamHandler()
        fmt = ColouredFormatter('%(message)s')
        hdlr.setFormatter(fmt)
        rootLogger.addHandler(hdlr)
    

    然后使用:

    import coloured_log
    import logging
    
    coloured_log.init()
    logging.info("info")    
    logging.debug("debug")    
    
    coloured_log.close()    # restore colours
    
  • 1

    虽然其他解决方案似乎很好,但他们有一些问题 . 有些人会对整行进行着色,有些时候不需要,有些则省略了你们可能拥有的任何配置 . 以下解决方案不会影响除消息本身之外的任何内容 .

    Code

    class ColoredFormatter(logging.Formatter):
        def format(self, record):
            if record.levelno == logging.WARNING:
                record.msg = '\033[93m%s\033[0m' % record.msg
            elif record.levelno == logging.ERROR:
                record.msg = '\033[91m%s\033[0m' % record.msg
            return logging.Formatter.format(self, record)
    

    Example

    logger = logging.getLogger('mylogger')
    handler = logging.StreamHandler()
    
    log_format = '[%(asctime)s]:%(levelname)-7s:%(message)s'
    time_format = '%H:%M:%S'
    formatter = ColoredFormatter(log_format, datefmt=time_format)
    handler.setFormatter(formatter)
    logger.addHandler(handler)
    
    logger.warn('this should be yellow')
    logger.error('this should be red')
    

    Output

    [17:01:36]:WARNING:this should be yellow
    [17:01:37]:ERROR  :this should be red
    

    如您所见,其他所有内容仍然会输出并保持其初始颜色 . 如果您想要更改除消息之外的任何内容,您只需将颜色代码传递给 log_format 即可例 .

  • 9

    这是包含颜色代码的枚举:

    class TerminalColour:
        """
        Terminal colour formatting codes
        """
        # https://stackoverflow.com/questions/287871/print-in-terminal-with-colors
        MAGENTA = '\033[95m'
        BLUE = '\033[94m'
        GREEN = '\033[92m'
        YELLOW = '\033[93m'
        RED = '\033[91m'
        GREY = '\033[0m'  # normal
        WHITE = '\033[1m'  # bright white
        UNDERLINE = '\033[4m'
    

    这可以应用于每个日志级别的名称 . Be aware that this is a monstrous hack.

    logging.addLevelName(logging.INFO, "{}{}{}".format(TerminalColour.WHITE, logging.getLevelName(logging.INFO), TerminalColour.GREY))
    logging.addLevelName(logging.WARNING, "{}{}{}".format(TerminalColour.YELLOW, logging.getLevelName(logging.WARNING), TerminalColour.GREY))
    logging.addLevelName(logging.ERROR, "{}{}{}".format(TerminalColour.RED, logging.getLevelName(logging.ERROR), TerminalColour.GREY))
    logging.addLevelName(logging.CRITICAL, "{}{}{}".format(TerminalColour.MAGENTA, logging.getLevelName(logging.CRITICAL), .GREY))
    

    请注意,您的日志格式化程序必须包含日志级别的名称

    %(levelname)
    

    例如:

    LOGGING = {
    ...
            'verbose': {
                'format': '%(asctime)s %(levelname)s %(name)s:%(lineno)s %(module)s %(process)d %(thread)d %(message)s'
            },
            'simple': {
                'format': '[%(asctime)s] %(levelname)s %(name)s %(message)s'
            },
    
  • 6

    使用pyfancy .

    例:

    print(pyfancy.RED + "Hello Red!" + pyfancy.END)
    
  • 66

    只是另一种解决方案,ZetaSyanthis的颜色:

    def config_log(log_level):
    
        def set_color(level, code):
            level_fmt = "\033[1;" + str(code) + "m%s\033[1;0m" 
            logging.addLevelName( level, level_fmt % logging.getLevelName(level) )
    
        std_stream = sys.stdout
        isatty = getattr(std_stream, 'isatty', None)
        if isatty and isatty():
            levels = [logging.DEBUG, logging.CRITICAL, logging.WARNING, logging.ERROR]
            for idx, level in enumerate(levels):
                set_color(level, 30 + idx )
            set_color(logging.DEBUG, 0)
        logging.basicConfig(stream=std_stream, level=log_level)
    

    __main__ 函数中调用一次 . 我有类似的东西:

    options, arguments = p.parse_args()
    log_level = logging.DEBUG if options.verbose else logging.WARNING
    config_log(log_level)
    

    它还验证输出是否为控制台,否则不使用任何颜色 .

  • 3
    import logging
    
    logging.basicConfig(filename="f.log" filemode='w', level=logging.INFO,
                        format = "%(logger_name)s %(color)s  %(message)s %(endColor)s")
    
    
    class Logger(object):
        __GREEN = "\033[92m"
        __RED = '\033[91m'
        __ENDC = '\033[0m'
    
        def __init__(self, name):
            self.logger = logging.getLogger(name)
            self.extra={'logger_name': name, 'endColor': self.__ENDC, 'color': self.__GREEN}
    
    
        def info(self, msg):
            self.extra['color'] = self.__GREEN
            self.logger.info(msg, extra=self.extra)
    
        def error(self, msg):
            self.extra['color'] = self.__RED
            self.logger.error(msg, extra=self.extra)
    

    用法

    Logger("File Name").info("This shows green text")

  • 95

    在类似问题上回答相同:Python | change text color in shell

    想法是使用clint库 . 它支持MAC,Linux和Windows shell(CLI) .

相关问题