# pylint:disable=invalid-name import logging from termcolor import colored def addLoggingLevel(levelName, levelNum, methodName=None): """ Comprehensively adds a new logging level to the `logging` module and the currently configured logging class. `levelName` becomes an attribute of the `logging` module with the value `levelNum`. `methodName` becomes a convenience method for both `logging` itself and the class returned by `logging.getLoggerClass()` (usually just `logging.Logger`). If `methodName` is not specified, `levelName.lower()` is used. To avoid accidental clobberings of existing attributes, this method will raise an `AttributeError` if the level name is already an attribute of the `logging` module or if the method name is already present Example ------- >>> addLoggingLevel('TRACE', logging.DEBUG - 5) >>> logging.getLogger(__name__).setLevel("TRACE") >>> logging.getLogger(__name__).trace('that worked') >>> logging.trace('so did this') >>> logging.TRACE 5 """ if not methodName: methodName = levelName.lower() if hasattr(logging, levelName): raise AttributeError(f"{levelName} already defined in logging module") if hasattr(logging, methodName): raise AttributeError(f"{methodName} already defined in logging module") if hasattr(logging.getLoggerClass(), methodName): raise AttributeError(f"{methodName} already defined in logger class") # This method was inspired by the answers to Stack Overflow post # http://stackoverflow.com/q/2183233/2988730, especially # http://stackoverflow.com/a/13638084/2988730 def logForLevel(self, message, *args, **kwargs): if self.isEnabledFor(levelNum): # pylint:disable=protected-access self._log(levelNum, message, args, **kwargs) def logToRoot(message, *args, **kwargs): logging.log(levelNum, message, *args, **kwargs) logging.addLevelName(levelNum, levelName) setattr(logging, levelName, levelNum) setattr(logging.getLoggerClass(), methodName, logForLevel) setattr(logging, methodName, logToRoot) addLoggingLevel("TRACE", logging.DEBUG - 5) class CustomFormatter(logging.Formatter): def __init__(self, fmt): super().__init__() self.fmt = fmt self.FORMATS = { logging.TRACE: colored(self.fmt), logging.DEBUG: colored(self.fmt, "white"), logging.INFO: colored(self.fmt, "blue"), logging.WARNING: colored(self.fmt, "yellow"), logging.ERROR: colored(self.fmt, "red"), logging.CRITICAL: colored(self.fmt, "red", attrs=["bold"]), } def format(self, record: logging.LogRecord): log_fmt = self.FORMATS.get(record.levelno) formatter = logging.Formatter(log_fmt) return formatter.format(record)