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

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

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

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

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

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


当前回答

简化的日志库:

class handler(logging.StreamHandler):
    colors = {
        logging.DEBUG: '\033[37m',
        logging.INFO: '\033[36m',
        logging.WARNING: '\033[33m',
        logging.ERROR: '\033[31m',
        logging.CRITICAL: '\033[101m',
    }
    reset = '\033[0m'
    fmtr = logging.Formatter('%(levelname)s %(message)s')

    def format(self, record):
        color = self.colors[record.levelno]
        log = self.fmtr.format(record)
        reset = self.reset
        return color + log + reset


logging.basicConfig(level=logging.DEBUG, handlers=[handler()])

其他回答

另一个解决方案,用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)

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

如果有人正在寻找一个漂亮的着色以及自定义日志级别着色,你可以看看这个改编的解决方案(它使用moecololelibrary):

#Install moecolor
pip install moecolor
from moecolor import FormatText as ft

class ConsoleFormatter(logging.Formatter):
    default_format = f"[%(asctime)s | %(name)s | %(funcName)s | LN%(lineno)s | %(levelname)s]: %(message)s"
    time_portion =  ft('%(asctime)s', color='purple').text
    format_portion = ' | %(name)s | %(funcName)s | LN%(lineno)d | %(levelname)s]: '
    FORMATS = {
        'DEBUG': time_portion + ft(format_portion, color='yellow').text + ft('%(message)s', color='fff9ae').text,
        'INFO': time_portion + ft(format_portion, color='green').text + ft('%(message)s', color='#d3ffb3').text,
        'WARNING': time_portion + ft(format_portion, color='orange').text + ft('%(message)s', color='#ffc100').text,
        'TIMER': time_portion + ft(format_portion, color='blue').text + ft('%(message)s', color='#00b4d8').text, # Note, this is a custom log level
        'ERROR': time_portion + ft(format_portion, color='red').text + ft('%(message)s', color='#ba262b').text,
        'CRITICAL': time_portion + ft(format_portion, color='#8D0101').text + ft('%(message)s', color='#D5212E').text,
    }

    def format(self, record):
        _format = self.FORMATS.get(record.levelname, self.default_format)
        formatter = logging.Formatter(_format)
        return formatter.format(record)

你可以这样使用它:

console_handler = logging.StreamHandler()
console_handler.setFormatter(ConsoleFormatter())

我从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

简化的日志库:

class handler(logging.StreamHandler):
    colors = {
        logging.DEBUG: '\033[37m',
        logging.INFO: '\033[36m',
        logging.WARNING: '\033[33m',
        logging.ERROR: '\033[31m',
        logging.CRITICAL: '\033[101m',
    }
    reset = '\033[0m'
    fmtr = logging.Formatter('%(levelname)s %(message)s')

    def format(self, record):
        color = self.colors[record.levelno]
        log = self.fmtr.format(record)
        reset = self.reset
        return color + log + reset


logging.basicConfig(level=logging.DEBUG, handlers=[handler()])

快速和肮脏的解决方案预定义的日志级别,而不定义一个新的类。

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))