import logging from logging.handlers import RotatingFileHandler import sys 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 customLogger(object): _logger = None def __new__(cls, name:str, log_file_name:str, level=logging.DEBUG): if cls._logger is None: cls._logger = logging.getLogger(name) # Define a Logging Format _format = _format = ColorizedArgsFormatter('%(asctime)s - %(levelname)-8s ' + name + ' %(filename)s in function %(funcName)s:%(lineno)d %(message)s') _file_format = LogfileFormatter('%(asctime)s - %(levelname)-8s ' + name + ' %(filename)s in function %(funcName)s:%(lineno)d %(message)s') # Create Console Output _handler = logging.StreamHandler(sys.stdout) # Create Logfile Output _file_hanlder = RotatingFileHandler(log_file_name, maxBytes=10240000, backupCount=3 ) # Add the Format to the Handler _handler.setFormatter(_format) _file_hanlder.setFormatter(_file_format) # Set Loglevel to the Desired One. _handler.setLevel(level) _file_hanlder.setLevel(level) # Finally add the Handler to the Logger: cls._logger.addHandler(_handler) cls._logger.addHandler(_file_hanlder) # Set the Log Level of the Logger. cls._logger.setLevel(level) # Put any initialization here. return cls._logger 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) record.msg = orig_msg record.args = orig_args return formatted class LogfileFormatter(logging.Formatter): def __init__(self, fmt:str): super().__init__() self.formatter = logging.Formatter(fmt) @staticmethod def rewrite_record(record: logging.LogRecord): msg = record.msg record.msg = 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) 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