Disclaimer: this post was heaviliy inspired by the 2 following gists:
color_log.py
usingtermcolor
recipescolorlog.py
usingcolorama
I loved the simplicity of the first, not having to manipulate any of the logging internal API, but I prefer colorama
over termcolor
.
Without further ado, there is my solution :
import logging, sys
from colorama import Back, Fore, Style
LOG_FORMAT = "%(asctime)s - pid:%(process)s %(filename)s:%(lineno)d %(levelname)8s| %(message)s"
class ColorLogsWrapper(object):
COLOR_MAP = {
'debug': Fore.CYAN,
'info': Fore.GREEN,
'warning': Fore.YELLOW,
'error': Fore.RED,
'critical': Back.RED,
}
def __init__(self, logger):
self.logger = logger
def __getattr__(self, attr_name):
if attr_name == 'warn':
attr_name = 'warning'
if attr_name not in 'debug info warning error critical':
return getattr(self.logger, attr_name)
log_level = getattr(logging, attr_name.upper())
# mimicking logging/__init__.py behaviour
if not self.logger.isEnabledFor(log_level):
return
def wrapped_attr(msg, *args, **kwargs):
style_prefix = self.COLOR_MAP[attr_name]
msg = style_prefix + msg + Style.RESET_ALL
# We call _.log directly to not increase the callstack
# so that Logger.findCaller extract the corrects filename/lineno
return self.logger._log(log_level, msg, args, **kwargs)
return wrapped_attr
logging.basicConfig(stream=sys.stderr, format=LOG_FORMAT, level=logging.DEBUG)
LOGGER = ColorLogsWrapper(logging.getLogger(__name__))
LOGGER.debug('Debug')
LOGGER.info('Info')
LOGGER.warn('Warning')
LOGGER.error('Error')
LOGGER.critical('Critical')
Output:
$ py colored_logger.py 2015-02-17 10:38:04,802 - pid:16089 colored_logger.py:39 DEBUG| Debug 2015-02-17 10:38:04,802 - pid:16089 colored_logger.py:40 INFO| Info 2015-02-17 10:38:04,802 - pid:16089 colored_logger.py:41 WARNING| Warning 2015-02-17 10:38:04,802 - pid:16089 colored_logger.py:42 ERROR| Error 2015-02-17 10:38:04,802 - pid:16089 colored_logger.py:43 CRITICAL| Critical