Skip to content

Instantly share code, notes, and snippets.

@grahamcrowell
Last active March 1, 2024 05:29
Show Gist options
  • Save grahamcrowell/61d9eb67a802f8827917a4e2d5567cd7 to your computer and use it in GitHub Desktop.
Save grahamcrowell/61d9eb67a802f8827917a4e2d5567cd7 to your computer and use it in GitHub Desktop.
python snippet for coloured logging
def config_color_logging():
"""Configure and return a colored console logger.
see https://github.com/borntyping/python-colorlog#usage
see https://docs.python.org/3/library/logging.html#logrecord-attributes
:return: logger
"""
import colorlog
import os
from logging import INFO
formatter = colorlog.ColoredFormatter(
"%(log_color)s%(levelname)-8s%(reset)s %(message_log_color)s%(message)s",
datefmt=None,
reset=True,
# set style of log prefix
log_colors={
'DEBUG': 'cyan',
'INFO': 'green',
'WARNING': 'yellow',
'ERROR': 'red',
'CRITICAL': 'red,bg_red,fg_bold_black',
},
# set style of log message
secondary_log_colors={'message': {
'DEBUG': 'cyan',
'INFO': 'green',
'WARNING': 'yellow',
'ERROR': 'red',
'CRITICAL': 'fg_bold_red'
}},
style='%'
)
handler = colorlog.StreamHandler()
handler.setFormatter(formatter)
project_name = os.path.split(os.path.dirname(__file__))[1]
_logger = colorlog.getLogger(project_name)
_logger.setLevel(INFO)
_logger.addHandler(handler)
return _logger
logger = config_color_logging()
"""Logging wrapper
based on: https://towardsdatascience.com/using-wrappers-to-log-in-python-ccffe4c46b54
docs: https://docs.python.org/3/library/inspect.html
"""
import logging
import logging.config
import sys
from uuid import uuid4
(LOG_STDOUT_VERBOSITY, LOGGER_NAME) = (logging.DEBUG, "testing leedcode")
LOGGING = {'version': 1, 'disable_existing_loggers': True,
'level': logging.DEBUG}
logging.config.dictConfig(LOGGING)
def _get_logger():
_logger = logging.Logger(name=LOGGER_NAME)
console_handler = logging.StreamHandler(stream=sys.stdout)
console_handler.setLevel(LOG_STDOUT_VERBOSITY)
_logger.addHandler(console_handler)
return _logger
logger = _get_logger() # pylint: disable=invalid-name
def entering(func, call_hash, *args, **kwargs):
"""
Function entry logging
:param func: function being called
:param call_hash: unique id of a call
:param args: args passed into function
:param kwargs: kwargs passed into function
:return:
"""
arg_strings = list(map(lambda arg: str(arg), args))
kwarg_strings = [
f"{arg_name}={arg_value}"
for arg_name, arg_value in kwargs.items()
]
args_str = ", ".join([*arg_strings, *kwarg_strings])
enter_message = f"{repr(call_hash)}-> {func.__name__}({args_str}) @ " + \
f"{func.__code__.co_filename}:{func.__code__.co_firstlineno}"
logger.info(enter_message)
def exiting(func, call_hash):
""" Post function logging """
logger.info(f"{repr(call_hash)}<- {func.__name__}")
def wrap(pre=entering, post=exiting):
""" Wrapper """
def decorate(func):
""" Decorator """
def call(*args, **kwargs):
""" Actual wrapping """
call_hash = uuid4().hex[:3]
pre(func, call_hash, *args, **kwargs)
result = func(*args, **kwargs)
post(func, call_hash)
return result
return call
return decorate
def log_args():
"""wrapper that logs function/method calls.
Returns:
Callable: decorator
"""
return wrap()
import logging
def get_coloured_logger(log_level):
"""wraps all logging setup into single method call
see: https://stackoverflow.com/questions/384076/how-can-i-color-python-logging-output/384125#384125
"""
# These are the sequences need to get colored output
RESET_SEQ = "\033[0m"
COLOR_SEQ = "\033[1;%dm"
BOLD_SEQ = "\033[1m"
# The background is set with 40 plus the number of the color,
# The foreground is 30 plus the number of the color
# see: https://en.wikipedia.org/wiki/ANSI_escape_code#Colors
BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = map(lambda idx: idx + 30, range(8))
# edit values here to change colouring
COLORS = {
'DEBUG': BLUE,
'INFO': WHITE,
'WARNING': YELLOW,
'CRITICAL': YELLOW,
'ERROR': RED
}
def formatter_message(message, use_color=True):
"""conditionally wraps message text with colour escape sequence"""
if use_color:
message = message.replace("$RESET", RESET_SEQ).replace("$BOLD",
BOLD_SEQ)
else:
message = message.replace("$RESET", "").replace("$BOLD", "")
return message
class ColoredFormatter(logging.Formatter):
"""Custom logging.Formatter class"""
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 % COLORS[levelname] + levelname + RESET_SEQ
record.levelname = levelname_color
return logging.Formatter.format(self, record)
class ColoredLogger(logging.Logger):
"""Custom logger class with multiple destinations"""
FORMAT = "[$BOLD%(name)s$RESET][%(levelname)-10s] %(message)s ($BOLD%(filename)s$RESET:%(lineno)d)"
COLOR_FORMAT = formatter_message(FORMAT, True)
def __init__(self, name):
logging.Logger.__init__(self, name, log_level)
color_formatter = ColoredFormatter(self.COLOR_FORMAT)
console = logging.StreamHandler()
console.setFormatter(color_formatter)
self.addHandler(console)
return
# globally set logger class
logging.setLoggerClass(ColoredLogger)
import os.path
# logger name is same as script name
logger_name = os.path.split(__file__)[1]
# create instance of custom logger
logger = logging.getLogger(logger_name)
return logger
# ... code that uses logger
def main():
# make logger available globally
global logger
log_level = logging.DEBUG
# create logger
logger = get_coloured_logger(log_level)
if __name__ == '__main__':
main()
@grahamcrowell
Copy link
Author

grahamcrowell commented May 18, 2018

shameless copy of stack overflow answer

copy snippet and call:

get_coloured_logger(logging.DEBUG)

@grahamcrowell
Copy link
Author

grahamcrowell commented May 18, 2018

Simple black and white logger

add to root __init__.py

import logging


def get_plain_logger(log_level):
    import os.path
    # logger name is same as script name
    logger_name = os.path.split(os.path.dirname(__file__))[1]
    # create logger
    _logger = logging.getLogger(logger_name)
    # logger handles messages of all severity (ie. >= 10)
    _logger.setLevel(logging.DEBUG)
    # create console log handler
    console_handler = logging.StreamHandler()
    # set console log handler severity
    console_handler.setLevel(log_level)
    # define format of log messages
    formatter = logging.Formatter('%(levelname)-7s- %(asctime)s - %(filename)s:%(lineno)d %(message)s')
    # attach formatter to console_handler
    console_handler.setFormatter(formatter)
    # add console_handler to logger
    _logger.addHandler(console_handler)
    return _logger


logger = get_plain_logger(logging.DEBUG)

@grahamcrowell
Copy link
Author

grahamcrowell commented Jun 2, 2018

for available formatter attributes: formatter docs
for adding class name: stackoverflow inheritance
for logging config file: blog tutorial

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment