如何为 Python 日志记录输出着色?

Posted

技术标签:

【中文标题】如何为 Python 日志记录输出着色?【英文标题】:How can I color Python logging output? 【发布时间】:2010-09-27 21:33:15 【问题描述】:

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

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

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

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

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

任何想法如何使用日志记录模块获得彩色输出?

【问题讨论】:

您应该指定您想要一个多平台解决方案 - Linux 和 Windows。 如果你使用 Eclipse/PyDev 相关:Colorize logs in eclipse console 或许你也可以使用colorlog 你也可以试试我写的chromalog 支持所有操作系统和Python版本(2.7和3.*) 在日志文件中实际转储 ANSI 代码的解决方案是一个坏主意,当您在六个月内寻找某些东西但忘记在您的正则表达式模式中允许 ANSI 字符时,它们会抓住您。下面有一些解决方案可以在查看日志时添加颜色,而不是在写入日志时添加颜色... 【参考方案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)

以防万一其他人需要它。

如果您使用多个记录器或处理程序,请小心:ColoredFormatter 正在更改记录对象,该对象被进一步传递给其他处理程序或传播到其他记录器。如果您配置了文件记录器等,您可能不希望在日志文件中有颜色。为避免这种情况,最好在操作 levelname 属性之前简单地使用 copy.copy() 创建 record 的副本,或者在返回格式化字符串之前将 levelname 重置为以前的值(归功于 cmets 中的 Michael )。

【讨论】:

黄色、白色、蓝色等在哪里定义? @Swaroop - 这些是 ANSI 转义码,您可以在 Google 上阅读,或在此处找到:en.wikipedia.org/wiki/ANSI_escape_code,或者pueblo.sourceforge.net/doc/manual/ansi_color_codes.html 我不认为你应该为此创建一个记录器子类 - 只要创建一个专门的Formatter 并指定它在StreamHandler 上的使用,你的答案就很好。但是不需要记录器子类。事实上,使用记录器类会为每个创建的记录器添加一个处理程序,这不是您通常想要的。 @simon: plumberjack.blogspot.co.uk/2010/12/… ColoredFormatter 的附注。它正在更改记录对象,该对象被进一步传递给其他处理程序或传播到其他记录器。如果您配置了文件记录器等,您可能不希望在日志文件中有颜色。为避免这种情况,最好在操作 levelname 属性之前简单地使用copy.copy() 创建record 的副本,或者在返回格式化字符串之前将 levelname 重置为以前的值。【参考方案2】:

几年前,我编写了一个彩色流处理程序供我自己使用。然后我看到了这个页面,发现了一组人们正在复制/粘贴的代码 sn-ps :-(。我的流处理程序目前仅适用于 UNIX(Linux、Mac OS X),但优点是它是 available on 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、日志级别和日志消息。这就是它在实践中的样子:

注意:使用带有 MinTTY 的 Git Bash 时

Windows 上的 Git Bash 有一些记录在案的怪癖: Winpty and Git Bash

对于 ANSI 转义码和 ncurses 样式的字符重写和动画,您需要在命令前加上 winpty

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

【讨论】:

很有趣,我只是想在这个帖子中添加一个指向“pypi.python.org/pypi/coloredlogs/0.4.7”的链接! 由于某种原因,我在使用coloredlogs.install() 时不断收到AttributeError: 'module' object has no attribute 'install'。你能用最新版本确认一下吗? 这看起来很漂亮。不幸的是,它破坏了很多东西。特别是,它使对 logging.basicConfig 的调用无效。例如,这使得无法使用自定义格式化程序。 @Clément:两个(重叠的?)问题:(1)“对 logging.basicConfig 的调用无效”是什么意思,(2)替代方案是什么? logging.basicConfig()coloredlogs.install() 都安装了一个记录到控制台的流处理程序,所以如果没有“无效”,你会得到重复的消息...... 干得好!!到今天为止,它在 Windows 中像梦一样工作:)【参考方案3】:

2021 解决方案,无需额外包,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")

结果

完整的配色方案

适用于窗户

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

【讨论】:

我运行了测试(python 3.7,windows),但日志没有显示颜色:←[38;21m2019-11-12 19:29:50,994 - My_app - DEBUG - debug message (test_colored_log.py:43)←[0m ←[38;21m2019-11-12 19:29:50,994 - My_app - INFO - info message (test_colored_log.py:44)←[0m ←[33;21m2019-11-12 19:29:50,994 - My_app - WARNING - warning message (test_colored_log.py:45)←[0m ←[31;21m2019-11-12 19:29:50,994 - My_app - ERROR - error message (test_colored_log.py:46)←[0m ←[31;1m2019-11-12 19:29:50,994 - My_app - CRITICAL - critical message (test_colored_log.py:47)←[0m 我非常喜欢这个答案,所以我为它做了一个repo,有一些增量和一个 ansi 颜色的备忘单。 嗨,我在 Ubuntu 上运行它并且工作正常。唯一的问题是,这在我的终端中添加了双下划线,对此有什么想法吗? 哎呀,刚刚玩弄它并找到了解决方案,只需将...21m 更改为20m 似乎在我的工作中很完美。以防万一有人遇到同样的问题。 @SergeyPleshakov 在 Ubuntu 上测试。如果这在 Windows 上不起作用,请告诉我。【参考方案4】:

更新:因为这是我一直想从头开始的痒,所以我继续为像我这样只想用简单方法做事的懒人编写了一个库: zenlog

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

这是一个快速复制和粘贴的 sn-p,用于设置日志记录并打印看起来不错的日志消息:

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

输出:

【讨论】:

很好的答案; +1。虽然代码示例可以被修剪(真的需要对setLevel 的三个调用吗?) 我希望如果我深入研究答案足够长的时间,我会找到这样的答案。 ☺ 我希望@airmind 会考虑将其作为公认的答案,以便未来聪明的工作人员可以找到似乎是具有最佳惰性的最佳库。 ? 我刚刚为 OUTPUT 的消息示例投票了这个 ^^ 谢谢!真的很有用,对我来说就像一个魅力!【参考方案5】:

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

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

【讨论】:

我基于这个写了一个StreamHandler类,见gist.github.com/mooware/a1ed40987b6cc9ab9c65。 这对我有用!第 90 行:应该是args[1].msg = color + str(args[1].msg) + '\x1b[0m' # normal 我喜欢这个解决方案。目前使用它。我看到有一个属性 _set_color,有没有办法为特定的日志消息执行此操作? edit,哦,这只是 Windows 机器的补丁。为不同的用例添加自定义会很好。 +1 表示 ANSI 颜色。在 xterm 中,您甚至可以一次获得 256 种颜色,并且可以动态定义调色板!但是请注意,所有对日志记录函数的调用都应在函数定义中以避免潜在的import lock problems when logging outside of a function definition。您的代码看起来大多不错; TestColorer.py 中的那一点点让我很担心。 这会导致实际日志文件中日志消息的开头和结尾出现颜色代码。【参考方案6】:

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

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

【讨论】:

@spiderplant0 导入日志; # 粘贴来自@ABC 的代码;尝试使用 logging.warning('这是一个测试')。您将看到“警告:这是一个测试”的大写部分是彩色的。顺便说一句,它仅适用于 linux 由于只有日志级别名称是彩色的,因此您必须确保将日志级别名称打印到控制台。这对我来说不是开箱即用的。这些方面的东西会有所帮助:logging.basicConfig(format='%(asctime)s [%(name)s] [%(levelname)s] %(message)s') 当然%(levelnames)s 很重要。 应用和理解的最简单、最干净的解决方案。 只需在 Linux 控制台中尝试。 echo -e "Normal texst \033[1;31mred bold text\033[0m normal text again"。 echo -e 选项将“\033”解释为 Escape ASCII 符号的八进制形式。这个特殊符号使一些兼容的终端将后续字符(到 char m 包括在内)解释为特殊命令。 en.wikipedia.org/wiki/ANSI_escape_code 小改进:将此代码放入if sys.sdterr.isatty():。在这种情况下,如果您将输出重定向到文件,则该文件将不包含这些转义字符。【参考方案7】:

好吧,我想我不妨添加我的彩色记录器的变体。

这没什么花哨的,但使用起来非常简单,并且不会更改记录对象,因此如果使用文件处理程序,则可以避免将 ANSI 转义序列记录到日志文件中。它不影响日志消息格式。

如果您已经在使用logging module's Formatter,您只需将您的顾问处理程序 Formatter 替换为 ColoredFormatter 即可。如果您正在记录整个应用程序,您只需为***记录器执行此操作。

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 = ('01m23') \
            .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")

结果

终端输出

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 - DEBUG - Hello from the sub module

当然,您可以根据需要对终端和日志文件输出进行格式化。只有日志级别会被着色。

我希望有人觉得这很有用,而且它不仅仅是太多相同的东西。 :)

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

【讨论】:

顺便说一句,要为消息本身添加颜色,只需在 return 之前添加这一行:colored_record.msg = ('01m23').format(self.PREFIX, seq, colored_record.getMessage(), self.SUFFIX) 我怎样才能使print() 声明变得丰富多彩?【参考方案8】:

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

输出:

【讨论】:

也可以使用colorlog.basicConfig 代替logging.basicConfig 有一些不错的默认值 作为记录,colorlog 并不总是直接在 Windows 平台上工作(如指定的,colorama 依赖项是必需的)。即使这样,我也很难让它在 Anaconda/Spyder 环境中工作。例如,您可能需要在 escape_code.py 中指定 colorama.init(strip=False)(如该线程中所示 github.com/spyder-ide/spyder/issues/1917)【参考方案9】:

我更新了 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

【讨论】:

很大的改进。然而,关于super 的评论仅适用于我猜的一些古老的 Python 版本?因为这个答案是从 2010 年开始的。它对我来说很好用 Python 2.7【参考方案10】:

我修改了Sorin提供的原始示例并将StreamHandler子类化为ColorizedConsoleHandler。

他们的解决方案的缺点是它修改了消息,因为这是修改实际的日志消息,所以任何其他处理程序也将获得修改后的消息。

在我们的例子中,这导致日志文件中包含颜色代码,因为我们使用了多个记录器。

下面的类只在支持 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)

【讨论】:

【参考方案11】:

看下面的解决方案。流处理程序应该是进行着色的东西,然后您可以选择为单词着色,而不仅仅是整行(使用格式化程序)。

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

【讨论】:

您可以在gist(由博客作者维护)中找到更新的实现。我正在使用它并且工作得很好。感谢分享。【参考方案12】:

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

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

https://github.com/laysakura/rainbow_logging_handler

支持 Windows

支持 Django

可自定义的颜色

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

【讨论】:

【参考方案13】:

除了按级别着色之外,如何使用交替颜色突出显示日志消息参数?我最近为此编写了简单的代码。另一个优点是日志调用是使用 Python 3 大括号样式格式进行的。 ("")。

在此处查看最新代码和示例:https://github.com/davidohana/colargulog

示例记录代码:

root_logger = logging.getLogger()
console_handler = logging.StreamHandler(stream=sys.stdout)
console_format = "%(asctime)s - %(levelname)-8s - %(name)-25s - %(message)s"
colored_formatter = ColorizedArgsFormatter(console_format)
console_handler.setFormatter(colored_formatter)
root_logger.addHandler(console_handler)

logger = logging.getLogger(__name__)
logger.info("Hello World")
logger.info("Request from  handled in :.3f ms", socket.gethostname(), 11)
logger.info("Request from  handled in :.3f ms", "127.0.0.1", 33.1)
logger.info("My favorite drinks are , , , ", "milk", "wine", "tea", "beer")
logger.debug("this is a  message", logging.getLevelName(logging.DEBUG))
logger.info("this is a  message", logging.getLevelName(logging.INFO))
logger.warning("this is a  message", logging.getLevelName(logging.WARNING))
logger.error("this is a  message", logging.getLevelName(logging.ERROR))
logger.critical("this is a  message", logging.getLevelName(logging.CRITICAL))
logger.info("Does old-style formatting also work? %s it is, but no colors (yet)", True)

输出:

实施:

"""
colargulog - Python3 Logging with Colored Arguments and new string formatting style

Written by david.ohana@ibm.com
License: Apache-2.0
"""

import logging
import logging.handlers
import re


class ColorCodes:
    grey = "\x1b[38;21m"
    green = "\x1b[1;32m"
    yellow = "\x1b[33;21m"
    red = "\x1b[31;21m"
    bold_red = "\x1b[31;1m"
    blue = "\x1b[1;34m"
    light_blue = "\x1b[1;36m"
    purple = "\x1b[1;35m"
    reset = "\x1b[0m"


class ColorizedArgsFormatter(logging.Formatter):
    arg_colors = [ColorCodes.purple, ColorCodes.light_blue]
    level_fields = ["levelname", "levelno"]
    level_to_color = 
        logging.DEBUG: ColorCodes.grey,
        logging.INFO: ColorCodes.green,
        logging.WARNING: ColorCodes.yellow,
        logging.ERROR: ColorCodes.red,
        logging.CRITICAL: ColorCodes.bold_red,
    

    def __init__(self, fmt: str):
        super().__init__()
        self.level_to_formatter = 

        def add_color_format(level: int):
            color = ColorizedArgsFormatter.level_to_color[level]
            _format = fmt
            for fld in ColorizedArgsFormatter.level_fields:
                search = "(%\(" + fld + "\).*?s)"
                _format = re.sub(search, f"color\\1ColorCodes.reset", _format)
            formatter = logging.Formatter(_format)
            self.level_to_formatter[level] = formatter

        add_color_format(logging.DEBUG)
        add_color_format(logging.INFO)
        add_color_format(logging.WARNING)
        add_color_format(logging.ERROR)
        add_color_format(logging.CRITICAL)

    @staticmethod
    def rewrite_record(record: logging.LogRecord):
        if not BraceFormatStyleFormatter.is_brace_format_style(record):
            return

        msg = record.msg
        msg = msg.replace("", "_")
        msg = msg.replace("", "_")
        placeholder_count = 0
        # add ANSI escape code for next alternating color before each formatting parameter
        # and reset color after it.
        while True:
            if "_" not in msg:
                break
            color_index = placeholder_count % len(ColorizedArgsFormatter.arg_colors)
            color = ColorizedArgsFormatter.arg_colors[color_index]
            msg = msg.replace("_", color + "", 1)
            msg = msg.replace("_", "" + ColorCodes.reset, 1)
            placeholder_count += 1

        record.msg = msg.format(*record.args)
        record.args = []

    def format(self, record):
        orig_msg = record.msg
        orig_args = record.args
        formatter = self.level_to_formatter.get(record.levelno)
        self.rewrite_record(record)
        formatted = formatter.format(record)

        # restore log record to original state for other handlers
        record.msg = orig_msg
        record.args = orig_args
        return formatted


class BraceFormatStyleFormatter(logging.Formatter):
    def __init__(self, fmt: str):
        super().__init__()
        self.formatter = logging.Formatter(fmt)

    @staticmethod
    def is_brace_format_style(record: logging.LogRecord):
        if len(record.args) == 0:
            return False

        msg = record.msg
        if '%' in msg:
            return False

        count_of_start_param = msg.count("")
        count_of_end_param = msg.count("")

        if count_of_start_param != count_of_end_param:
            return False

        if count_of_start_param != len(record.args):
            return False

        return True

    @staticmethod
    def rewrite_record(record: logging.LogRecord):
        if not BraceFormatStyleFormatter.is_brace_format_style(record):
            return

        record.msg = record.msg.format(*record.args)
        record.args = []

    def format(self, record):
        orig_msg = record.msg
        orig_args = record.args
        self.rewrite_record(record)
        formatted = self.formatter.format(record)

        # restore log record to original state for other handlers
        record.msg = orig_msg
        record.args = orig_args
        return formatted

【讨论】:

【参考方案14】:

“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 文件名、行号和函数名,第三组匹配之后出现的日志消息。我还使用“粗体/法线”的并行序列以及颜色序列。这看起来像:

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

显然,这足够灵活,您可以在任何进程中使用它,而不仅仅是尾随日志文件。每当我想给某些东西上色时,我通常都会在运行中创建一个新的正则表达式。出于这个原因,与任何自定义日志文件着色工具相比,我更喜欢 colout,因为我只需要学习一个工具,无论我在着色什么:日志记录、测试输出、在终端中突出显示代码的 sn-ps 语法等。

它还避免了在日志文件本身中实际转储 ANSI 代码,恕我直言,这是一个坏主意,因为它会破坏诸如在日志文件中查找模式之类的事情,除非您始终记得在 grep 正则表达式中匹配 ANSI 代码。

【讨论】:

【参考方案15】:

有大量的响应。但是没有人在谈论装饰器。所以这是我的。

因为它要简单得多。

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

#!/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)

【讨论】:

【参考方案16】:

安装 colorlog 包,您可以立即在日志消息中使用颜色:

获取一个logger 实例,就像你通常做的那样。 设置日志记录级别。您还可以使用DEBUG 等常量 和 INFO 直接来自日志记录模块。 将消息格式化程序设置为提供的ColoredFormattercolorlog 图书馆提供。
import colorlog

logger = colorlog.getLogger()
logger.setLevel(colorlog.colorlog.logging.DEBUG)

handler = colorlog.StreamHandler()
handler.setFormatter(colorlog.ColoredFormatter())
logger.addHandler(handler)

logger.debug("Debug message")
logger.info("Information message")
logger.warning("Warning message")
logger.error("Error message")
logger.critical("Critical message")

输出:


更新:额外信息

只需更新ColoredFormatter

handler.setFormatter(colorlog.ColoredFormatter('%(log_color)s [%(asctime)s] %(levelname)s [%(filename)s.%(funcName)s:%(lineno)d] %(message)s', datefmt='%a, %d %b %Y %H:%M:%S'))

输出:


包装:

pip install colorlog

输出:

Collecting colorlog
  Downloading colorlog-4.6.2-py2.py3-none-any.whl (10.0 kB)
Installing collected packages: colorlog
Successfully installed colorlog-4.6.2

【讨论】:

【参考方案17】:

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)

【讨论】:

【参考方案18】:
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)))`

【讨论】:

+1 “明亮”ANSI 颜色的[9*m 代码的好例子!附言你的最后一行让我有点担心,因为它还不知道whether logging outside of a function definition is safe in Python。【参考方案19】:

这是我的解决方案:

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

【讨论】:

【参考方案20】:

FriendlyLog 是另一种选择。它适用于 Linux、Windows 和 MacOS 下的 Python 2 和 3。

【讨论】:

期待新PR减少模块路径混乱【参考方案21】:

这是 airmind 示例的另一个 Python3 变体。我想要一些在其他示例中没有看到的特定功能

为终端使用颜色,但不要在文件处理程序中写入不可打印的字符(我为此定义了 2 个格式化程序) 能够覆盖特定日志消息的颜色 从文件(在本例中为 yaml)配置记录器

注意:我使用了colorama,但你可以修改它,所以它不是必需的。另外,对于我的测试,我只是在运行 python 文件,所以我的课程在模块 __main__ 中,您必须将 (): __main__.ColoredFormatter 更改为您的模块。

pip install colorama pyyaml

logging.yaml

---
version: 1
disable_existing_loggers: False
formatters:
  simple:
    format: "%(threadName)s - %(name)s - %(levelname)s - %(message)s"
  color:
    format: "%(threadName)s - %(name)s - %(levelname)s - %(message)s"
    (): __main__.ColoredFormatter
    use_color: true

handlers:
  console:
    class: logging.StreamHandler
    level: DEBUG
    formatter: color
    stream: ext://sys.stdout

  info_file_handler:
    class: logging.handlers.RotatingFileHandler
    level: INFO
    formatter: simple
    filename: app.log
    maxBytes: 20971520 
    backupCount: 20
    encoding: utf8

  error_file_handler:
    class: logging.handlers.RotatingFileHandler
    level: ERROR
    formatter: simple
    filename: errors.log
    maxBytes: 10485760 
    backupCount: 20
    encoding: utf8

root:
  level: DEBUG
  handlers: [console, info_file_handler, error_file_handler]

main.py

import logging
import logging.config
import os
from logging import Logger

import colorama
import yaml
from colorama import Back, Fore, Style

COLORS = 
    "WARNING": Fore.YELLOW,
    "INFO": Fore.CYAN,
    "DEBUG": Fore.BLUE,
    "CRITICAL": Fore.YELLOW,
    "ERROR": Fore.RED,



class ColoredFormatter(logging.Formatter):
    def __init__(self, *, format, use_color):
        logging.Formatter.__init__(self, fmt=format)
        self.use_color = use_color

    def format(self, record):
        msg = super().format(record)
        if self.use_color:
            levelname = record.levelname
            if hasattr(record, "color"):
                return f"record.colormsgStyle.RESET_ALL"
            if levelname in COLORS:
                return f"COLORS[levelname]msgStyle.RESET_ALL"
        return msg


with open("logging.yaml", "rt") as f:
    config = yaml.safe_load(f.read())
    logging.config.dictConfig(config)

logger: Logger = logging.getLogger(__name__)
logger.info("Test INFO", extra="color": Back.RED)
logger.info("Test INFO", extra="color": f"Style.BRIGHTBack.RED")
logger.info("Test INFO")
logger.debug("Test DEBUG")
logger.warning("Test WARN")

输出:

【讨论】:

【参考方案22】:

表情符号

您可以像其他人在他们的答案中提到的那样为文本使用颜色,以获得带有背景或前景色的彩色文本。

但您可以改用表情符号!例如,您可以将⚠️ 用于警告消息,将? 用于错误消息。

或者简单地将这些笔记本用作颜色:

print("?: error message")
print("?: warning message")
print("?: ok status message")
print("?: action message")
print("?: canceled status message")
print("?: Or anything you like and want to recognize immediately by color")

? 奖励:

此方法还可以帮助您直接在源代码中快速扫描和查找日志。


如何打开表情选择器?

mac os: 控制 + 命令 + 空格

windows: win + .

linux: control + .control + ;

【讨论】:

【参考方案23】:

你真的应该尝试丰富

如果你想要colorsrich 可能就是你要找的包。

它易于使用和可定制 + 适用于cmdwindows terminalcomemu 等和jupyter notebook! (我试过很多包我告诉你,只有 rich 的颜色在笔记本上有效。)。

rich 还带有许多其他精美的功能。查看https://rich.readthedocs.io/en/latest/。

安装

pip install rich

小例子:

import logging
from rich.logging import RichHandler

FORMAT = "%(message)s"
logging.basicConfig(
    level="NOTSET", format=FORMAT, datefmt="[%X]", handlers=[RichHandler()]
)  # set level=20 or logging.INFO to turn of debug
logger = logging.getLogger("rich")

logger.debug("debug...")
logger.info("info...")
logger.warning("warning...")
logger.error("error...")
logger.fatal("fatal...")

【讨论】:

【参考方案24】:

我遇到的问题是正确设置格式化程序:

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

【讨论】:

它应该是伪代码(也缺少_set_colour),但添加了一些东西。最麻烦的是知道如何正确附加格式化程序。 查看“管道工千斤顶”解决方案。我认为这是解决问题的更好方法(即处理程序应该进行着色)。 ***.com/questions/384076/…【参考方案25】:

虽然其他解决方案看起来不错,但它们也存在一些问题。有些确实为整条线着色,而有些则不需要,有些则省略了您可能拥有的任何配置。下面的解决方案只影响消息本身。

代码

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)

示例

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

输出

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

如您所见,其他所有内容仍会输出并保持其初始颜色。如果您想更改消息以外的任何内容,只需将颜色代码传递给示例中的log_format

【讨论】:

当我使用它时,消息会被打印两次。你知道为什么吗? @你能详细说明一下吗?也就是说,您的意思是 [17:01:36]:WARNING:this should be yellowthis should be yellow 或整行打印两次? 抱歉,评论过于简洁。前者发生:[17:01:36]:警告:这应该是黄色的\n这应该是黄色的。但是,我只希望显示格式化的,否则由于冗余日志,它看起来像垃圾。 @MuratKarakuş 在没有全面了解实施的情况下不确定为什么会发生这种情况。如果您使用的是自定义记录器,也许您在某些时候会干扰?一个快速的解决办法是从log_format 中删除7s:%(message)s【参考方案26】:

我有两个提交要添加,一个只对消息着色(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

【讨论】:

【参考方案27】:

刚刚对类似问题的回答相同:Python | change text color in shell

这个想法是使用clint 库。它支持 MAC、Linux 和 Windows shell (CLI)。

【讨论】:

【参考方案28】:
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")

【讨论】:

对于控制台,您可以省略文件名或简单地使用 filename=''。修改 basicConfig 以包含其他属性,例如文件号、模块 ..【参考方案29】:

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

class TerminalColour:
    """
    Terminal colour formatting codes
    """
    # https://***.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'

这可以应用于每个日志级别的名称请注意,这是一个可怕的黑客攻击。

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'
        ,

【讨论】:

【参考方案30】:

以下解决方案仅适用于 python 3,但对我来说它看起来最清楚。

这个想法是使用日志记录工厂将“彩色”属性添加到日志记录对象中,而不是在日志格式中使用这些“彩色”属性。

import logging
logger = logging.getLogger(__name__)

def configure_logging(level):

    # add 'levelname_c' attribute to log resords
    orig_record_factory = logging.getLogRecordFactory()
    log_colors = 
        logging.DEBUG:     "\033[1;34m",  # blue
        logging.INFO:      "\033[1;32m",  # green
        logging.WARNING:   "\033[1;35m",  # magenta
        logging.ERROR:     "\033[1;31m",  # red
        logging.CRITICAL:  "\033[1;41m",  # red reverted
    
    def record_factory(*args, **kwargs):
        record = orig_record_factory(*args, **kwargs)
        record.levelname_c = "".format(
            log_colors[record.levelno], record.levelname, "\033[0m")
        return record

    logging.setLogRecordFactory(record_factory)

    # now each log record object would contain 'levelname_c' attribute
    # and you can use this attribute when configuring logging using your favorite
    # method.
    # for demo purposes I configure stderr log right here

    formatter_c = logging.Formatter("[%(asctime)s] %(levelname_c)s:%(name)s:%(message)s")

    stderr_handler = logging.StreamHandler()
    stderr_handler.setLevel(level)
    stderr_handler.setFormatter(formatter_c)

    root_logger = logging.getLogger('')
    root_logger.setLevel(logging.DEBUG)
    root_logger.addHandler(stderr_handler)


def main():
    configure_logging(logging.DEBUG)

    logger.debug("debug message")
    logger.info("info message")
    logger.critical("something unusual happened")


if __name__ == '__main__':
    main()

您可以轻松修改此示例以创建其他彩色属性(例如 message_c),然后使用这些属性(仅)在您想要的位置获取彩色文本。

(我最近发现的一个小技巧:我有一个带有彩色调试日志的文件,每当我想临时提高我的应用程序的日志级别时,我只需 tail -f 不同终端中的日志文件并在屏幕上查看调试日志更改任何配置并重新启动应用程序)

【讨论】:

以上是关于如何为 Python 日志记录输出着色?的主要内容,如果未能解决你的问题,请参考以下文章

如何为 grails 插件配置日志记录?

如何为一张表启用 sequelize 日志记录

如何为 Spring Security 启用日志记录?

如何为 Spring Security 启用日志记录?

如何为 Apache Hadoop NameNodes 启用 GC 日志记录,同时防止日志文件覆盖和限制磁盘空间使用

如何为 Kafka 生产者配置日志记录?