如何为Python日志输出着色?

試著忘記壹切 提交于 2020-03-14 20:51:02

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

现在,Python具有logging模块,该模块可让您指定许多选项来定制输出。 因此,我在想使用Python可能会发生类似的事情,但是我找不到在任何地方执行此操作的方法。

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

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

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

有什么想法可以使用日志记录模块获取彩色输出吗?


#1楼

这是一个可以在任何平台上运行的解决方案。 如果不只是告诉我,我会进行更新。

工作原理:在支持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")

着色器

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

#2楼

刚刚回答了同样的类似的问题: Python的| 更改外壳中的文本颜色

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


#3楼

多年前,我写了一个彩色的流处理程序供自己使用。 然后,我浏览了此页面,发现了人们正在复制/粘贴的代码片段集合:-(。我的流处理程序当前仅在UNIX(Linux,Mac OS X)上可用,但是优点是可以在PyPI (和GitHub)上使用 ),而且使用起来非常简单,还具有Vim语法模式:-)。 将来我可能会将其扩展到Windows上。

要安装软件包:

$ pip install coloredlogs

确认它有效:

$ coloredlogs --demo

要开始使用自己的代码:

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

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

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

Windows上的Git Bash有一些已记录的怪癖: Winpty和Git Bash

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

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

#4楼

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

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

https://github.com/laysakura/rainbow_logging_handler

  • 支持Windows

  • 支持Django

  • 可自定义的颜色

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


#5楼

我修改了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)
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!