How can I color Python logging output?
PythonLoggingColorsPython Problem Overview
Some time ago, I saw a Mono application with colored output, presumably because of its log system (because all the messages were standardized).
Now, Python has the logging
module, which lets you specify a lot of options to customize output. So, I'm imagining something similar would be possible with Python, but I canโt find out how to do this anywhere.
Is there any way to make the Python logging
module output in color?
What I want (for instance) errors in red, debug messages in blue or yellow, and so on.
Of course this would probably require a compatible terminal (most modern terminals are); but I could fallback to the original logging
output if color isn't supported.
Any ideas how I can get colored output with the logging module?
Python Solutions
Solution 1 - Python
I already knew about the color escapes, I used them in my bash prompt a while ago. Thanks anyway.
What I wanted was to integrate it with the logging module, which I eventually did after a couple of tries and errors.
Here is what I end up with:
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)
And to use it, create your own 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)
Just in case anyone else needs it.
Be careful if you're using more than one logger or handler: ColoredFormatter
is changing the record object, which is passed further to other handlers or propagated to other loggers. If you have configured file loggers etc. you probably don't want to have the colors in the log files. To avoid that, it's probably best to simply create a copy of record
with copy.copy()
before manipulating the levelname attribute, or to reset the levelname to the previous value, before returning the formatted string (credit to Michael in the comments).
Solution 2 - Python
2022 solution, no additional packages required, Python 3
Define a class
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)
Instantiate logger
# 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)
And use!
logger.debug("debug message")
logger.info("info message")
logger.warning("warning message")
logger.error("error message")
logger.critical("critical message")
For windows
This solution works on Mac OS, IDE terminals. Looks like the Windows command prompt doesn't have colors at all by default. Here are instructions on how to enable them, which I haven't try https://www.howtogeek.com/322432/how-to-customize-your-command-prompts-color-scheme-with-microsofts-colortool/
Solution 3 - Python
Years ago I wrote a colored stream handler for my own use. Then I came across this page and found a collection of code snippets that people are copy/pasting :-(. My stream handler currently only works on UNIX (Linux, Mac OS X) but the advantage is that it's available on PyPI (and GitHub) and it's dead simple to use. It also has a Vim syntax mode :-). In the future I might extend it to work on Windows.
To install the package:
$ pip install coloredlogs
To confirm that it works:
$ coloredlogs --demo
To get started with your own code:
$ python
> import coloredlogs, logging
> coloredlogs.install()
> logging.info("It works!")
2014-07-30 21:21:26 peter-macbook root[7471] INFO It works!
The default log format shown in the above example contains the date, time, hostname, the name of the logger, the PID, the log level and the log message. This is what it looks like in practice:
NOTE: When using Git Bash w/ MinTTY
Git Bash on windows has some documented quirks: https://stackoverflow.com/questions/48199794/winpty-and-git-bash
Which for ANSI escape codes and for ncurses style character rewriting and animations, you need to prefix commands with winpty
.
$ winpty coloredlogs --demo
$ winpty python your_colored_logs_script.py
Solution 4 - Python
Update: Because this is an itch that I've been meaning to scratch for so long, I went ahead and wrote a library for lazy people like me who just want simple ways to do things: zenlog
Colorlog is excellent for this. It's [available on PyPI][1] (and thus installable through pip install colorlog
) and is [actively maintained][2].
Here's a quick copy-and-pasteable snippet to set up logging and print decent-looking log messages:
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")
Output:
[1]: https://pypi.python.org/pypi/colorlog "Colorlog on PyPI" [2]: https://github.com/borntyping/python-colorlog "Colorlog GitHub page"
Solution 5 - Python
Quick and dirty solution for predefined log levels and without defining a new class.
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))
Solution 6 - Python
Here is a solution that should work on any platform. If it doesn't just tell me and I will update it.
How it works: on platform supporting ANSI escapes is using them (non-Windows) and on Windows it does use API calls to change the console colors.
The script does hack the logging.StreamHandler.emit method from standard library adding a wrapper to it.
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())
Solution 7 - Python
Well, I guess I might as well add my variation of the colored logger.
This is nothing fancy, but it is very simple to use and does not change the record object, thereby avoids logging the ANSI escape sequences to a log file if a file handler is used. It does not effect the log message formatting.
If you are already using the logging module's Formatter, all you have to do to get colored level names is to replace your counsel handlers Formatter with the ColoredFormatter. If you are logging an entire app you only need to do this for the top level logger.
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 = ('{0}{1}m{2}{3}') \
.format(PREFIX, seq, levelname, SUFFIX)
colored_record.levelname = colored_levelname
return Formatter.format(self, colored_record)
Example usage
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")
Results
Terminal output
app.log content
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
Of course you can get as fancy as you want with formatting the terminal and log file outputs. Only the log level will be colorized.
I hope somebody finds this useful and it is not just too much more of the same. :)
The Python example files can be downloaded from this GitHub Gist: https://gist.github.com/KurtJacobson/48e750701acec40c7161b5a2f79e6bfd
Solution 8 - Python
You can import the colorlog module and use its ColoredFormatter
for colorizing log messages.
Example
Boilerplate for main module:
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__)
The code only enables colors in log messages, if the colorlog module is installed and if the output actually goes to a terminal. This avoids escape sequences being written to a file when the log output is redirected.
Also, a custom color scheme is setup that is better suited for terminals with dark background.
Some example logging calls:
log.debug ('Hello Debug')
log.info ('Hello Info')
log.warn ('Hello Warn')
log.error ('Hello Error')
log.critical('Hello Critical')
Output:
Solution 9 - Python
I updated the example from airmind supporting tags for foreground and background. Just use the color variables $BLACK - $WHITE in your log formatter string. To set the background just use $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
So now you can simple do the following in your config file:
[formatter_colorFormatter]
class=logging.ColorFormatter
format= $COLOR%(levelname)s $RESET %(asctime)s $BOLD$COLOR%(name)s$RESET %(message)s
Solution 10 - Python
I modified the original example provided by Sorin and subclassed StreamHandler
to a ColorizedConsoleHandler
.
The downside of their solution is that it modifies the message, and because that is modifying the actual logmessage any other handlers will get the modified message as well.
This resulted in logfiles with colorcodes in them in our case because we use multiple loggers.
The class below only works on platforms that support ANSI, but it should be trivial to add the Windows colorcodes to it.
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)
Solution 11 - Python
Look at the following solution. The stream handler should be the thing doing the colouring, then you have the option of colouring words rather than just the whole line (with the Formatter).
http://plumberjack.blogspot.com/2010/12/colorizing-logging-output-in-terminals.html
Solution 12 - Python
Now there is a released PyPi module for customizable colored logging output:
https://pypi.python.org/pypi/rainbow_logging_handler/
and
https://github.com/laysakura/rainbow_logging_handler
-
Supports Windows
-
Supports Django
-
Customizable colors
As this is distributed as a Python egg, it is very easy to install for any Python application.
Solution 13 - Python
What about highlighting also log message arguments with alternating colors, in addition to coloring by level? I recently wrote simple code for that. Another advantage is that log call is made with Python 3 brace-style formatting. ("{}"
).
See latest code and examples here: https://github.com/davidohana/colargulog
Sample Logging code:
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)
Output:
Implementation:
"""
colargulog - Python3 Logging with Colored Arguments and new string formatting style
Written by [email protected]
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
Solution 14 - Python
Install the colorlog package, you can use colors in your log messages immediately:
- Obtain a
logger
instance, exactly as you would normally do. - Set the logging level. You can also use the constants like
DEBUG
andINFO
from the logging module directly. - Set the message formatter to be the
ColoredFormatter
provided by thecolorlog
library.
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")
UPDATE: extra info
Just update 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'))
Package:
pip install colorlog
output:
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
Solution 15 - Python
A simple but very flexible tool for coloring ANY terminal text is 'colout'.
pip install colout
myprocess | colout REGEX_WITH_GROUPS color1,color2...
Where any text in the output of 'myprocess' which matches group 1 of the regex will be colored with color1, group 2 with color2, etc.
For example:
tail -f /var/log/mylogfile | colout '^(\w+ \d+ [\d:]+)|(\w+\.py:\d+ .+\(\)): (.+)$' white,black,cyan bold,bold,normal
i.e. the first regex group (parens) matches the initial date in the logfile, the second group matches a python filename, line number and function name, and the third group matches the log message that comes after that. I also use a parallel sequence of 'bold/normals' as well as the sequence of colors. This looks like:
Note that lines or parts of lines which don't match any of my regex are still echoed, so this isn't like 'grep --color' - nothing is filtered out of the output.
Obviously this is flexible enough that you can use it with any process, not just tailing logfiles. I usually just whip up a new regex on the fly any time I want to colorize something. For this reason, I prefer colout to any custom logfile-coloring tool, because I only need to learn one tool, regardless of what I'm coloring: logging, test output, syntax highlighting snippets of code in the terminal, etc.
It also avoids actually dumping ANSI codes in the logfile itself, which IMHO is a bad idea, because it will break things like grepping for patterns in the logfile unless you always remember to match the ANSI codes in your grep regex.
Solution 16 - Python
There are tons of responses. But none is talking about decorators. So here's mine.
Because it is a lot more simple.
There's no need to import anything, nor to write any subclass:
#!/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__)
This set the errors in red, debug messages in blue, and so on. Like asked in the question.
We could even adapt the wrapper to take a color
argument to dynamicaly set the message's color using logger.debug("message", color=GREY)
EDIT: So here's the adapted decorator to set colors at runtime:
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)
Solution 17 - Python
Another minor remix of airmind's approach that keeps everything in one class:
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)
To use attach the formatter to a handler, something like:
handler.setFormatter(ColorFormatter())
logger.addHandler(handler)
Solution 18 - Python
Use the rich library
Rich supplies a logging handler which will format and colorize text written by Python's logging module.
It is easy to use and customizable + works in cmd.exe, Windows Terminal, ConEmu and Jupyter Notebook! (I tried many packages I tell ya, only rich's color works in the notebook.).
Rich also comes with many other fancy features.
Installation
pip install rich
Minimal example:
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...")
Solution 19 - Python
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)))`
Solution 20 - Python
coloredlogs
Instalation
pip install coloredlogs
Usage
Minimal usage:
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')
Start from message level debug:
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')
Hide messages from libraries:
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')
Format log messages:
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')
Available format attributes:
%(asctime)s
- Time as human-readable string, when logging call was issued%(created)f
- Time as float when logging call was issued%(filename)s
- File name%(funcName)s
- Name of function containing the logging call%(hostname)s
- System hostname%(levelname)s
- Text logging level%(levelno)s
- Integer logging level%(lineno)d
- Line number where the logging call was issued%(message)s
- Message passed to logging call (same as%(msg)s
)%(module)s
- File name without extension where the logging call was issued%(msecs)d
- Millisecond part of the time when logging call was issued%(msg)s
- Message passed to logging call (same as%(message)s
)%(name)s
- Logger name%(pathname)s
- Full pathname to file containing the logging call%(process)d
- Process ID%(processName)s
- Process name%(programname)s
- System programname%(relativeCreated)d
- Time as integer in milliseconds when logging call was issued, relative to the time when logging module was loaded%(thread)d
- Thread ID%(threadName)s
- Thread name%(username)s
- System username
Sources:
Solution 21 - Python
Solution using standard Python3 logging library
I am pretty excited to share this flexible solution for log coloring. I think it is an improvement to this solution by @SergeyPleshakov. I leveraged the log record's extra kwargs to set a log prefix and suffix. Then we just default the prefix and suffix to start and end with terminal color codes that correspond with the log level.
bonus feature โจ ๏ฐ โจ
The extra prefix
and suffix
can be overridden by the log call to be whatever. You want your debug log to be prefixed with a ๏, why not. You want a one of the info logs to be Green instead of the default, go for it!
Define the terminal Color
and ColorLogFormatter
classes
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)
Instantiate logger
logger = logging.getLogger('bobcat')
logger.setLevel('DEBUG')
stream_handler = logging.StreamHandler()
stream_handler.setFormatter(ColorLogFormatter())
logger.addHandler(stream_handler)
And use!
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")
and Voilร !
Solution 22 - Python
Here's my solution:
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])
Solution 23 - Python
FriendlyLog is another alternative. It works with Python 2 & 3 under Linux, Windows and MacOS.
Solution 24 - Python
This is another Python3 variant of airmind's example. I wanted some specific features I didn't see in the other examples
- use colors for the terminal but do not write non-printable characters in the file handlers (I defined 2 formatters for this)
- ability to override the color for a specific log message
- configure the logger from a file (yaml in this case)
Notes: I used colorama but you could modify this so it is not required. Also for my testing I was just running python file so my class is in module __main__
You would have to change (): __main__.ColoredFormatter
to whatever your module is.
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.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")
output:
Solution 25 - Python
Emoji
You can use colors for text as others mentioned in their answers to have colorful text with a background or foreground color.
But you can use emojis instead! for example, you can use โ ๏ธ
for warning messages and ๐
for error messages.
Or simply use these notebooks as a color:
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")
๏ Bonus:
This method also helps you to quickly scan and find logs directly in the source code.
How to open emoji picker?
mac os: control + command + space
windows: win + .
linux: control + . or control + ;
Solution 26 - Python
The bit I had trouble with was setting up the formatter properly:
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)
And then to use:
import coloured_log
import logging
coloured_log.init()
logging.info("info")
logging.debug("debug")
coloured_log.close() # restore colours
Solution 27 - Python
Just answered the same on similar question: https://stackoverflow.com/questions/2330245/python-change-text-color-in-shell/16630004#16630004
The idea is to use the clint library. Which has support for MAC, Linux and Windows shells (CLI).
Solution 28 - Python
While the other solutions seem fine they have some issues. Some do colour the whole lines which some times is not wanted and some omit any configuration you might have all together. The solution below doesn't affect anything but the message itself.
Code
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)
Example
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')
Output
[17:01:36]:WARNING:this should be yellow
[17:01:37]:ERROR :this should be red
As you see, everything else still gets outputted and remain in their initial color. If you want to change anything else than the message you can simply pass the color codes to log_format
in the example.
Solution 29 - Python
I have two submissions to add, one of which colorizes just the message (ColoredFormatter), and one of which colorizes the entire line (ColorizingStreamHandler). These also include more ANSI color codes than previous solutions.
Some content has been sourced (with modification) from: The post above, and http://plumberjack.blogspot.com/2010/12/colorizing-logging-output-in-terminals.html.
Colorizes the message only:
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
Colorizes the whole line:
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
Solution 30 - Python
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)
Usage
Logger("File Name").info("This shows green text")
Solution 31 - Python
This is an Enum containing the colour codes:
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'
This may be applied to the names of each log level. Be aware that this is a monstrous hack.
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))
Note that your log formatter must include the name of the log level
%(levelname)
for example:
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'
},
Solution 32 - Python
The following solution works with python 3 only, but for me it looks most clear.
The idea is to use log record factory to add 'colored' attributes to log record objects and than use these 'colored' attributes in log format.
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()
You can easily modify this example to create other colored attributes (f.e. message_c) and then use these attributes to get colored text (only) where you want.
(handy trick I discovered recently: I have a file with colored debug logs and whenever I want temporary increase the log level of my application I just tail -f
the log file in different terminal and see debug logs on screen w/o changing any configuration and restarting application)
Solution 33 - Python
A handy bash script with tput colors
# Simple using tput
bold=$(tput bold)
reset=$(tput sgr0)
fblack=$(tput setaf 0)
fred=$(tput setaf 1)
fgreen=$(tput setaf 2)
fyellow=$(tput setaf 3)
fblue=$(tput setaf 4)
fmagenta=$(tput setaf 5)
fcyan=$(tput setaf 6)
fwhite=$(tput setaf 7)
fnotused=$(tput setaf 8)
freset=$(tput setaf 9)
bblack=$(tput setab 0)
bred=$(tput setab 1)
bgreen=$(tput setab 2)
byellow=$(tput setab 3)
bblue=$(tput setab 4)
bmagenta=$(tput setab 5)
bcyan=$(tput setab 6)
bwhite=$(tput setab 7)
bnotused=$(tput setab 8)
breset=$(tput setab 9)
# 0 - Emergency (emerg) $fred # something is wrong... go red
# 1 - Alerts (alert) $fred # something is wrong... go red
# 2 - Critical (crit) $fred # something is wrong... go red
# 3 - Errors (err) $fred # something is wrong... go red
# 4 - Warnings (warn) $fyellow # yellow yellow dirty logs
# 5 - Notification (notice) $fwhite # common stuff
# 6 - Information (info) $fblue # sky is blue
# 7 - Debug (debug) $fgreen # lot of stuff to read... go green
Solution 34 - Python
This is a slight variation on @Sergey Pleshakov's excellent answer which applies color only to the levels and uses basicConfig
as expected:
class CustomFormatter(logging.Formatter):
white = "\x1b[97;20m"
grey = "\x1b[38;20m"
green = "\x1b[32;20m"
cyan = "\x1b[36;20m"
yellow = "\x1b[33;20m"
red = "\x1b[31;20m"
bold_red = "\x1b[31;1m"
reset = "\x1b[0m"
fmt = "%(asctime)s - {}%(levelname)-8s{} - %(name)s.%(funcName)s - %(message)s"
FORMATS = {
logging.DEBUG: fmt.format(grey, reset),
logging.INFO: fmt.format(green, reset),
logging.WARNING: fmt.format(yellow, reset),
logging.ERROR: fmt.format(red, reset),
logging.CRITICAL: fmt.format(bold_red, reset),
}
def format(self, record):
log_fmt = self.FORMATS.get(record.levelno)
formatter = logging.Formatter(log_fmt, datefmt="%H:%M:%S")
return formatter.format(record)
handler = logging.StreamHandler()
handler.setFormatter(CustomFormatter())
logging.basicConfig(
level=logging.DEBUG,
handlers=[handler]
)
Solution 35 - Python
to anyone who feels the same need, i recommend my own package TCPrint.
it's based on Colorama (which provides cross-platform compatibility), but unlike it uses familiar <tags>
to mark up text colors and includes tags for logging
installation:
pip install ctprint
Coloring:
from ctprint inport ctp, ctdecode, cterr, ctlog
# print colored text
ctp('<bw> black text on white background /> default formating')
Error handling:
# print error message
try:
1/0 # any broken line
except Exception as _ex:
cterr(_ex)
Variables logging:
var0 = var1 = 0
# print varName-varValue pairs
def example_ctlog():
var2 = 'string val'
var3 = {'ctp_string': '<bg_red><red>red text on red background (NO) >'}
# out of the function, var0=var2 - nothing problems.
ctlog(var0=var0, var1=var1, var2=var2, var3=var3)
And more:
ctp.help() # print help dialog with all supported tags and functions
in addition to logging functions and color tags, there are <error>
and <log>
quick tags designed for marking up user outputs
if u need to speed up logging and improve readability in the terminal (vscode/pycharm/cmd/bash etc), and not customize the command-line interface in the style of ๏, or ๏ฆ, u know what to do. CTPrint was created for the first
luck!
Solution 36 - Python
Solution 37 - Python
Just another solution, with the colors of ZetaSyanthis:
def config_log(log_level):
def set_color(level, code):
level_fmt = "\033[1;" + str(code) + "m%s\033[1;0m"
logging.addLevelName( level, level_fmt % logging.getLevelName(level) )
std_stream = sys.stdout
isatty = getattr(std_stream, 'isatty', None)
if isatty and isatty():
levels = [logging.DEBUG, logging.CRITICAL, logging.WARNING, logging.ERROR]
for idx, level in enumerate(levels):
set_color(level, 30 + idx )
set_color(logging.DEBUG, 0)
logging.basicConfig(stream=std_stream, level=log_level)
call it once from your __main__
function. I have something like this there:
options, arguments = p.parse_args()
log_level = logging.DEBUG if options.verbose else logging.WARNING
config_log(log_level)
it also verifies that the output is a console, otherwise no colors are used.
Solution 38 - Python
The easiest solution is probably Colorama.
Install Colorama onto your computer:
pip install colorama
Then add it to your Python program:
import colorama
print(Fore.GREEN + "test123")
If you need to multi-color things, use YAChalk.
Install YAChalk onto your computer:
pip install yachalk
Add it to your Python program:
from yachalk import chalk
print(chalk.blue("This is blue and {chalk.red("this is red")})
Solution 39 - Python
Simplified, logging lib only:
class handler(logging.StreamHandler):
colors = {
logging.DEBUG: '\033[37m',
logging.INFO: '\033[36m',
logging.WARNING: '\033[33m',
logging.ERROR: '\033[31m',
logging.CRITICAL: '\033[101m',
}
reset = '\033[0m'
fmtr = logging.Formatter('%(levelname)s %(message)s')
def format(self, record):
color = self.colors[record.levelno]
log = self.fmtr.format(record)
reset = self.reset
return color + log + reset
logging.basicConfig(level=logging.DEBUG, handlers=[handler()])
Solution 40 - Python
Use the tornado library
The Tornado web framework provides some utilities, including the tornado.log.LogFormatter
formatter which can be used without the rest of the framework.
Color support on Windows versions that do not support ANSI color codes is enabled by use of the colorama library. Applications that wish to use this must first initialize colorama with a call to colorama.init
.
import logging
import tornado.log
# import colorama # uncomment on some Windows versions
# colorama.init()
consoleHandler = logging.StreamHandler()
consoleHandler.setFormatter(tornado.log.LogFormatter())
logging.basicConfig(level=logging.DEBUG, handlers=[consoleHandler])
logger = logging.getLogger("test")
logger.info("hello world")