How can I color Python logging output?
前一段时间,我看到一个输出为彩色的mono应用程序,大概是因为它的日志系统(因为所有消息都是标准化的)。
现在,python有了
有没有办法让python
我想要的(例如)红色的错误,蓝色或黄色的调试消息,等等。
当然,这可能需要一个兼容的终端(大多数现代终端都是);但是如果不支持颜色,我可以退回到原来的
我有什么想法可以用日志模块得到彩色输出吗?
我已经知道颜色转义,不久前我在bash提示中使用了它们。无论如何,谢谢。我想要的是将它与日志模块集成,这是我经过多次尝试和错误后最终完成的。以下是我的结论:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | 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) |
要使用它,请创建您自己的记录器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | # 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) |
号
以防其他人需要。
如果使用多个记录器或处理程序,请小心:
几年前,我为自己编写了一个彩色的流处理程序。然后我浏览了一下这个页面,发现了一组代码片段,人们正在复制/粘贴这些代码片段:-(。我的流处理程序目前只在Unix(Linux、Mac OS X)上工作,但其优点是它在Pypi(和Github)上可用,而且使用起来非常简单。它还具有vim语法模式:-)。将来我可能会将其扩展到Windows上。
要安装软件包:
1 | $ pip install coloredlogs |
号
确认其工作:
1 | $ coloredlogs --demo |
号
要开始使用自己的代码:
1 2 3 4 5 | $ python > import coloredlogs, logging > coloredlogs.install() > logging.info("It works!") 2014-07-30 21:21:26 peter-macbook root[7471] INFO It works! |
号
上面示例中显示的默认日志格式包含日期、时间、主机名、记录器名称、PID、日志级别和日志消息。这就是实际情况:
。
这里有一个可以在任何平台上工作的解决方案。如果它不只是告诉我,我会更新它。
工作原理:在支持ansi转义的平台上使用它们(非Windows),在Windows上使用api调用来更改控制台颜色。
该脚本确实对标准库中的logging.streamHandler.emit方法进行了黑客攻击,并向其添加了包装。
测试颜色.py
1 2 3 4 5 6 7 | # 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | #!/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()) |
。
快速而肮脏的解决方案,用于预定义的日志级别,而不定义新的类。
1 2 | 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)) |
号
更新:因为这是一个我一直想要抓取的痒,我继续写了一个图书馆,为像我这样的懒惰的人,他们只想简单的方法做事情:zenlog
ColorLog非常适合这样做。它在PYPI上可用(因此可以通过
下面是一个快速复制和可粘贴的代码片段,用于设置日志记录并打印美观的日志消息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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") |
输出:
。
我更新了airmind的示例,支持前台和后台的标签。只需在日志格式化程序字符串中使用颜色变量$black-$white。要设置背景,只需使用$bg-black-$bg-white。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | 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 |
。
现在您可以在配置文件中简单地执行以下操作:
1 2 3 | [formatter_colorFormatter] class=logging.ColorFormatter format= $COLOR%(levelname)s $RESET %(asctime)s $BOLD$COLOR%(name)s$RESET %(message)s |
。
您可以导入colorlog模块,并使用它的
主模块样板:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | 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模块,并且输出实际到终端,则该代码只启用日志消息中的颜色。这样可以避免在重定向日志输出时将转义序列写入文件。
此外,还设置了一个自定义配色方案,它更适合于深色背景的终端。
一些日志记录调用示例:
1 2 3 4 5 | log.debug ('Hello Debug') log.info ('Hello Info') log.warn ('Hello Warn') log.error ('Hello Error') log.critical('Hello Critical') |
号
输出:
氧化镁
好吧,我想我也可以添加我的彩色记录器的变体。
这并不奇怪,但使用起来非常简单,并且不会更改记录对象,因此,如果使用了文件处理程序,则可以避免将ansi转义序列记录到日志文件中。它不影响日志消息格式。
如果您已经在使用日志模块的格式化程序,那么要获得彩色级别名称,只需用彩色格式化程序替换顾问处理程序格式化程序。如果您正在记录一个完整的应用程序,您只需要为顶级日志记录程序执行此操作。
彩色日志.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | #!/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) |
。示例用法
App.Py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | #!/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
1 2 3 4 5 6 | #!/usr/bin/env python import logging log = logging.getLogger('main.sub_module') log.debug("Hello from the sub module") |
。结果
终端输出
。
app.log内容
1 2 3 4 5 6 | 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 |
。
当然,通过格式化终端和日志文件输出,您可以随心所欲地进行格式化。只有日志级别才会上色。
我希望有人会发现这很有用,而且不仅仅是太多相同的东西。:)
python示例文件可以从此Github Gist下载:https://gist.github.com/kurtjacobson/48e70701ace40c7161b5a2f79e6bfd
我将Sorin和子类StreamHandler提供的原始示例修改为ColorizedConsolehandler。
他们的解决方案的缺点是它修改了消息,并且因为这是修改实际的日志消息,所以任何其他处理程序都将获得修改后的消息。
在我们的例子中,这导致了日志文件中带有颜色代码,因为我们使用了多个记录器。
下面的类只在支持ANSI的平台上工作,但是向其添加Windows颜色代码应该是很简单的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | 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) |
看看下面的解决方案。流处理程序应该是进行着色的对象,然后您可以选择对单词着色,而不仅仅是整行(使用格式化程序)。
http://plumberjack.blogspot.com/2010/12/colorizing-logging-output-in-terminals.html
现在有一个可定制彩色日志输出的已发布PYPI模块:
https://pypi.python.org/pypi/rainbow_日志处理程序/
和
https://github.com/laysakura/rainbow_日志处理程序
支持Windows
支持Django
可定制的颜色
由于它是作为一个python egg发布的,所以很容易为任何python应用程序安装。
有大量的反应。但没有人在谈论装饰师。这是我的。
因为这要简单得多。
不需要导入任何内容,也不需要编写任何子类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | #!/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__) |
这会将错误设置为红色,调试消息设置为蓝色,等等。就像问题中问的那样。
我们甚至可以调整包装器,将
编辑:下面是在运行时设置颜色的调整后的装饰器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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) |
。
Airmind方法的另一个微小的混合,它将所有东西保持在一个类中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | 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) |
要使用将格式化程序附加到处理程序,请执行以下操作:
1 2 | handler.setFormatter(ColorFormatter()) logger.addHandler(handler) |
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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(' Params:' + str_color("blue", str(params)))` |
。
"colout"是一个简单但非常灵活的工具,用于为任何终端文本着色。
1 2 | pip install colout myprocess | colout REGEX_WITH_GROUPS color1,color2... |
"myprocess"输出中与regex的组1匹配的任何文本将用color1着色,组2用color2着色,等等。
例如:
1 | tail -f /var/log/mylogfile | colout '^(\w+ \d+ [\d:]+)|(\w+\.py:\d+ .+\(\)): (.+)$' white,black,cyan bold,bold,normal |
。
即,第一个regex组(parens)与日志文件中的初始日期匹配,第二个组与python文件名、行号和函数名匹配,第三个组与后面的日志消息匹配。我还使用了"粗体/法线"的平行序列以及颜色序列。这看起来像:
氧化镁
请注意,与任何regex都不匹配的行或部分行仍然会被回送,因此这不像"grep——color"——输出中没有过滤出任何内容。
显然,这足够灵活,您可以将其用于任何进程,而不仅仅是跟踪日志文件。每当我想给某个东西上色时,我都会快速地生成一个新的正则表达式。因此,比起任何自定义日志文件着色工具,我更喜欢colout,因为我只需要学习一个工具,而不管我在着色什么:日志记录、测试输出、在终端中突出显示代码片段的语法等等。
它还避免在日志文件本身中实际转储ansi代码,这是一个坏主意,因为它会破坏诸如对日志文件中的模式进行grepping之类的操作,除非您总是记住匹配grep regex中的ansi代码。
我的解决方案是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | 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]) |
号
这是一个包含颜色代码的枚举:
1 2 3 4 5 6 7 8 9 10 11 12 13 | class TerminalColour: """ Terminal colour formatting codes """ # https://stackoverflow.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' |
这可以应用于每个日志级别的名称。注意这是一个可怕的黑客。
1 2 3 4 | 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)) |
。
请注意,日志格式化程序必须包含日志级别的名称
1 | %(levelname) |
号
例如:
1 2 3 4 5 6 7 8 | 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' }, |
号
虽然其他的解决方案看起来不错,但也有一些问题。有些人会给整条线上色,而有些人则忽略了所有可能有的配置。下面的解决方案只影响消息本身。
代码
1 2 3 4 5 6 7 | 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) |
。
例子
1 2 3 4 5 6 7 8 9 10 11 | 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') |
产量
1 2 | [17:01:36]:WARNING:this should be yellow [17:01:37]:ERROR :this should be red |
。
如你所见,其他的一切仍然会被输出并保持其初始颜色。如果您想更改消息以外的任何内容,只需将颜色代码传递给示例中的
我有两个提交要添加,其中一个只对消息着色(coloredformatter),另一个对整行着色(colorzingstreamhandler)。与以前的解决方案相比,这些解决方案还包括更多的ANSI颜色代码。
一些内容(经过修改)来源于:上面的文章,以及http://plumberjack.blogspot.com/2010/12/colorizing-logging-output-in-terminals.html。
只对邮件着色:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | 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 |
。
为整行着色:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | 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', ' ')) 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 |
我遇到的问题是如何正确设置格式化程序:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 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) |
然后使用:
1 2 3 4 5 6 7 8 | import coloured_log import logging coloured_log.init() logging.info("info") logging.debug("debug") coloured_log.close() # restore colours |
。
使用PyFancy。
例子:
1 | print(pyfancy.RED +"Hello Red!" + pyfancy.END) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 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) |
用法
另一种解决方案,用泽塔菌素的颜色:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | def config_log(log_level): def set_color(level, code): level_fmt ="\033[1;" + str(code) +"m%s\033[1;0m" logging.addLevelName( level, level_fmt % logging.getLevelName(level) ) std_stream = sys.stdout isatty = getattr(std_stream, 'isatty', None) if isatty and isatty(): levels = [logging.DEBUG, logging.CRITICAL, logging.WARNING, logging.ERROR] for idx, level in enumerate(levels): set_color(level, 30 + idx ) set_color(logging.DEBUG, 0) logging.basicConfig(stream=std_stream, level=log_level) |
号
从您的
1 2 3 | options, arguments = p.parse_args() log_level = logging.DEBUG if options.verbose else logging.WARNING config_log(log_level) |
它还验证输出是否为控制台,否则不使用颜色。
在类似的问题上也回答了同样的问题:python在shell中更改文本颜色
我们的想法是利用克林特图书馆。它支持Mac、Linux和Windows Shell(CLI)。