ChatGPT解决这个技术问题 Extra ChatGPT

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

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

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

有什么方法可以使 Python logging 模块以彩色输出?

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

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

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

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

G
Guillaume Algis

我已经知道颜色转义了,我不久前在我的 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 重置为以前的值(注释中的 Michael )。


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

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

https://i.stack.imgur.com/klCcc.png

https://i.stack.imgur.com/iZU5S.png

对于窗户

此解决方案适用于 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 上不起作用,请告诉我。
J
Josh Peak

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

https://i.stack.imgur.com/AgqpA.png

注意:使用带有 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 的调用无效。例如,这使得无法使用自定义格式化程序。
仅供参考:新版本的 colourlogs 包使用自定义格式化程序来注入 ANSI 转义序列。这个自定义格式化程序支持用户定义的日志格式,其方式与 Python 的日志记录模块相同。但是,我看不到如何将 colourlogs 与用户定义的格式化程序结合使用,这与包的设计背道而驰。
做得太好了!!到今天为止,它像梦一样在 Windows 中运行 :)
M
Michael Schock

更新:因为这是我长久以来一直想从头开始的痒,所以我继续为像我这样只想用简单方法做事的懒人编写了一个库: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")

输出:

https://github.com/ManufacturaInd/python-zenlog/raw/master/colorlogs.png


很好的答案; +1。但是可以修剪代码示例(真的需要对 setLevel 的三个调用吗?)
如果我对答案进行了足够长的时间,我希望我能找到这样的答案。 ☺ 我希望@airmind 会考虑将此作为公认的答案,以便未来的工作聪明的人可以找到似乎是具有最佳惰性的最佳库。 😉
我只是为 OUTPUT 的消息示例投票赞成这个 ^^
谢谢!真的很有用,对我来说就像一个魅力!
m
moka

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

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(): 中。在这种情况下,如果您将输出重定向到文件,则文件将不包含这些转义字符。
D
Dave

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

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

该脚本确实破解了标准库中的 logging.StreamHandler.emit 方法,为其添加了一个包装器。

测试着色器.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")

着色器.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 中的那一点让我很担心。
这会导致实际日志文件中日志消息的开头和结尾出现颜色代码。
K
KCJ

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

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

如果您已经在使用 logging module's Formatter,那么您只需将您的顾问处理程序 Formatter 替换为 ColoredFormatter 即可获得彩色级别名称。如果您正在记录整个应用程序,您只需为顶级记录器执行此操作。

彩色日志.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)

示例用法

应用程序.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

子模块.py

#!/usr/bin/env python

import logging
log = logging.getLogger('main.sub_module')

log.debug("Hello from the sub module")

结果

终端输出

https://i.stack.imgur.com/OsmlG.png

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 = ('{0}{1}m{2}{3}').format(self.PREFIX, seq, colored_record.getMessage(), self.SUFFIX)
如何使 print() 语句丰富多彩?
m
maxschlepzig

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

输出:

https://i.stack.imgur.com/lWWpI.png


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

我更新了 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 对我来说效果很好
C
Cristian Ciupitu

我修改了 Sorin 提供的 original example 并将 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)

N
Nick

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

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


您可以在此 gist(由博客作者维护)中找到更新的实现。我正在使用它并且工作得很好。感谢分享。
M
Mikko Ohtamaa

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

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

https://github.com/laysakura/rainbow_logging_handler

支持视窗

支持 Django

可定制的颜色

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


d
dux2

除了按级别着色之外,突出显示还使用交替颜色记录消息参数怎么样?我最近为此编写了简单的代码。另一个优点是日志调用是使用 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)

输出:

https://i.stack.imgur.com/0Or6e.png

执行:

"""
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}\\1{ColorCodes.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

M
Milovan Tomašević

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

获取一个记录器实例,就像你通常做的那样。

设置日志记录级别。您还可以直接使用来自日志记录模块的 DEBUG 和 INFO 等常量。

将消息格式化程序设置为 colorlog 库提供的 ColoredFormatter。

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

https://i.stack.imgur.com/sXE9G.png

更新:额外信息

只需更新 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'))

https://i.stack.imgur.com/UMeCl.png

包裹:

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

J
Jonathan Hartley

为任何终端文本着色的简单但非常灵活的工具是“colout”。

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

与正则表达式的第 1 组匹配的“myprocess”输出中的任何文本将用 color1 着色,第 2 组用 color2 着色,等等。

例如:

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

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

https://i.stack.imgur.com/99ztB.png

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

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

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


l
lain

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

因为它要简单得多。

无需导入任何内容,也无需编写任何子类:

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

C
Cristian Ciupitu

使用丰富的库

Rich 提供了一个 logging handler,它将格式化和着色由 Python 的日志记录模块编写的文本。

它易于使用且可自定义 + 适用于 cmd.exe、Windows TerminalConEmu 和 Jupyter Notebook! (我试过很多我告诉你的包,只有 rich 的颜色在笔记本中有效。)。

Rich 还带有许多其他精美的功能。

安装

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

https://i.stack.imgur.com/rn3ZO.png


或者只需执行 python -m rich.logging 即可查看不同用例的示例 :-)
g
gravitation

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)

T
ToTamire

彩色原木

安装

pip install coloredlogs

用法

最少使用:

import logging
import coloredlogs

coloredlogs.install()  # install a handler on the root logger

logging.debug('message with level debug')
logging.info('message with level info')
logging.warning('message with level warning')
logging.error('message with level error')
logging.critical('message with level critical')

https://i.stack.imgur.com/A1axw.png

从消息级调试开始:

import logging
import coloredlogs

coloredlogs.install(level='DEBUG')  # install a handler on the root logger with level debug

logging.debug('message with level debug')
logging.info('message with level info')
logging.warning('message with level warning')
logging.error('message with level error')
logging.critical('message with level critical')

https://i.stack.imgur.com/S3AAi.png

隐藏库中的消息:

import logging
import coloredlogs

logger = logging.getLogger(__name__)  # get a specific logger object
coloredlogs.install(level='DEBUG')  # install a handler on the root logger with level debug
coloredlogs.install(level='DEBUG', logger=logger)  # pass a specific logger object

logging.debug('message with level debug')
logging.info('message with level info')
logging.warning('message with level warning')
logging.error('message with level error')
logging.critical('message with level critical')

https://i.stack.imgur.com/S3AAi.png

格式化日志消息:

import logging
import coloredlogs

logger = logging.getLogger(__name__)  # get a specific logger object
coloredlogs.install(level='DEBUG')  # install a handler on the root logger with level debug
coloredlogs.install(level='DEBUG', logger=logger)  # pass a specific logger object
coloredlogs.install(
    level='DEBUG', logger=logger,
    fmt='%(asctime)s.%(msecs)03d %(filename)s:%(lineno)d %(levelname)s %(message)s'
)

logging.debug('message with level debug')
logging.info('message with level info')
logging.warning('message with level warning')
logging.error('message with level error')
logging.critical('message with level critical')

https://i.stack.imgur.com/0aBWR.png

可用的格式属性:

%(asctime)s - 发出记录调用时作为人类可读字符串的时间

%(created)f - 发出记录调用时的浮点时间

%(filename)s - 文件名

%(funcName)s - 包含日志调用的函数名称

%(hostname)s - 系统主机名

%(levelname)s - 文本记录级别

%(levelno)s - 整数日志记录级别

%(lineno)d - 发出记录调用的行号

%(message)s - 传递给日志调用的消息(与 %(msg)s 相同)

%(module)s - 发出日志调用的文件名,不带扩展名

%(msecs)d - 发出记录调用时的毫秒部分

%(msg)s - 传递给记录调用的消息(与 %(message)s 相同)

%(name)s - 记录器名称

%(pathname)s - 包含日志调用的文件的完整路径名

%(process)d - 进程 ID

%(processName)s - 进程名称

%(programname)s - 系统程序名

%(relativeCreated)d - 发出记录调用时的整数毫秒数,相对于加载记录模块的时间

%(thread)d - 线程 ID

%(threadName)s - 线程名称

%(username)s - 系统用户名

资料来源:

Coloredlogs package

Logging library


我已经在谷歌上搜索了至少一个小时,但不知道如何在 Python 中更改颜色。它是否作为参数传递给 .install()?令人沮丧的是文档中没有示例,它显示为环境变量,而不是代码。
@user7660047 安装有色日志后,最少使用的代码应该会显示颜色。如果不是,也许你的命令行程序不支持颜色。
它显示颜色正常,我似乎无法弄清楚如何更改它们。
S
Serhii Khachko
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代码的好例子! PS你的最后一行让我有点担心,因为它还不知道whether logging outside of a function definition is safe in Python
a
aidanmelen

使用标准 Python3 日志库的解决方案

我很高兴能分享这个灵活的日志着色解决方案。我认为这是@SergeyPleshakov 对这个solution 的改进。我利用日志记录的 extra kwargs 设置日志前缀和后缀。然后我们只是默认前缀和后缀以与日志级别对应的终端颜色代码开始和结束。

奖励功能✨🍰✨

额外的 prefixsuffix 可以被日志调用覆盖为任何内容。您希望调试日志以 🐛 为前缀,为什么不呢。您希望其中一个信息日志为绿色而不是默认值,那就去吧!

定义终端 ColorColorLogFormatter

import logging


class Color:
    """A class for terminal color codes."""

    BOLD = "\033[1m"
    BLUE = "\033[94m"
    WHITE = "\033[97m"
    GREEN = "\033[92m"
    YELLOW = "\033[93m"
    RED = "\033[91m"
    BOLD_WHITE = BOLD + WHITE
    BOLD_BLUE = BOLD + BLUE
    BOLD_GREEN = BOLD + GREEN
    BOLD_YELLOW = BOLD + YELLOW
    BOLD_RED = BOLD + RED
    END = "\033[0m"


class ColorLogFormatter(logging.Formatter):
    """A class for formatting colored logs."""

    FORMAT = "%(prefix)s%(msg)s%(suffix)s"

    LOG_LEVEL_COLOR = {
        "DEBUG": {'prefix': '', 'suffix': ''},
        "INFO": {'prefix': '', 'suffix': ''},
        "WARNING": {'prefix': Color.BOLD_YELLOW, 'suffix': Color.END},
        "ERROR": {'prefix': Color.BOLD_RED, 'suffix': Color.END},
        "CRITICAL": {'prefix': Color.BOLD_RED, 'suffix': Color.END},
    }

    def format(self, record):
        """Format log records with a default prefix and suffix to terminal color codes that corresponds to the log level name."""
        if not hasattr(record, 'prefix'):
            record.prefix = self.LOG_LEVEL_COLOR.get(record.levelname.upper()).get('prefix')
        
        if not hasattr(record, 'suffix'):
            record.suffix = self.LOG_LEVEL_COLOR.get(record.levelname.upper()).get('suffix')

        formatter = logging.Formatter(self.FORMAT)
        return formatter.format(record)

实例化记录器

logger = logging.getLogger('bobcat')
logger.setLevel('DEBUG')

stream_handler = logging.StreamHandler()
stream_handler.setFormatter(ColorLogFormatter())
logger.addHandler(stream_handler)

并使用!

    logger.debug("This is debug", extra={'prefix': '🐛 '})
    logger.info("This is info")
    logger.info("This is a green info", extra={'prefix': Color.GREEN, 'suffix': Color.END})
    logger.warning("This is warning")
    logger.error("This is error")
    logger.critical("This is critical")

瞧!

https://i.stack.imgur.com/ZQJpg.png


它可能也是唯一适用于 Python 2.7 的版本!
v
veegee

这是我的解决方案:

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

S
SebiSebi

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


期待新 PR 减少模块路径杂乱
S
Scott

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

为终端使用颜色,但不要在文件处理程序中写入不可打印的字符(我为此定义了 2 个格式化程序)

能够覆盖特定日志消息的颜色

从文件配置记录器(在本例中为 yaml)

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

pip install colorama pyyaml

日志记录.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]

主文件

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.color}{msg}{Style.RESET_ALL}"
            if levelname in COLORS:
                return f"{COLORS[levelname]}{msg}{Style.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.BRIGHT}{Back.RED}"})
logger.info("Test INFO")
logger.debug("Test DEBUG")
logger.warning("Test WARN")

输出:

https://i.stack.imgur.com/AsvRt.png


M
Mojtaba Hosseini

表情符号

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

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

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

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 + ;


N
Nick

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

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),但添加了一些东西。最麻烦的是知道如何正确连接格式化程序。
请参阅“管道工千斤顶”解决方案。我认为这是解决问题的更好方法(即处理程序应该进行着色)。 stackoverflow.com/questions/384076/…
P
Pithikos

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

代码

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
Z
ZetaSyanthis

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

C
Community

刚刚在类似问题上回答了同样的问题:Python | change text color in shell

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


a
aristotll
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 以包含其他属性,如文件号、模块 ..