Last active
March 1, 2024 05:29
-
-
Save grahamcrowell/61d9eb67a802f8827917a4e2d5567cd7 to your computer and use it in GitHub Desktop.
python snippet for coloured logging
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"""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() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() |
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)
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
shameless copy of stack overflow answer
copy snippet and call: