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

现在,Python有了日志记录模块,它允许您指定许多选项来定制输出。所以,我想象类似的事情可能与Python,但我不知道如何在任何地方做到这一点。

是否有办法使Python日志模块输出为彩色?

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

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

有什么想法,我可以得到彩色输出与日志模块?


当前回答

请看下面的解决方案。流处理程序应该是做着色的事情,然后你可以选择着色单词而不仅仅是整行(与Formatter)。

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

其他回答

下面是一个适用于任何平台的解决方案。如果没有,告诉我,我会更新的。

它是如何工作的:在支持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())

Python 3解决方案,不需要额外的包

定义一个类

import logging

class CustomFormatter(logging.Formatter):

    grey = "\x1b[38;20m"
    yellow = "\x1b[33;20m"
    red = "\x1b[31;20m"
    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")

结果:

全配色方案:

Windows:

该解决方案适用于Mac OS、IDE终端。看起来Windows命令提示符在默认情况下根本没有颜色。以下是关于如何启用它们的说明,我还没有尝试过https://www.howtogeek.com/322432/how-to-customize-your-command-prompts-color-scheme-with-microsofts-colortool/

几年前,我为自己编写了一个彩色流处理程序。然后我偶然发现了这个页面,发现了一组人们复制/粘贴的代码片段:-(。我的流处理程序目前只适用于UNIX (Linux, Mac OS X),但优点是它可以在PyPI(和GitHub)上使用,而且使用起来非常简单。它也有一个Vim语法模式:-)。将来我可能会把它扩展到Windows上。

安装软件包:

$ pip install coloredlogs

要确认它是否有效:

$ coloredlogs --demo

要开始使用自己的代码:

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

上面示例中显示的默认日志格式包含日期、时间、主机名、记录器名称、PID、日志级别和日志消息。这是它在实践中的样子:

注意:当使用Git Bash与MinTTY

Git Bash在windows上有一些记录的怪癖: Winpty和Git Bash

对于ANSI转义代码和ncurses风格的字符重写和动画,你需要用winpty作为命令的前缀。

$ winpty coloredlogs --demo
$ winpty python your_colored_logs_script.py

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

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

and

https://github.com/laysakura/rainbow_logging_handler

支持Windows 支持Django 可定制的颜色

由于它是作为Python卵分发的,因此非常容易为任何Python应用程序安装它。

您可以导入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')

输出: