from typing import Union import logging LOGGING_FORMATTER = logging.Formatter("%(name)s - %(levelname)s - %(message)s") def add_root_logger_handler( logger: logging.Logger, formatter: logging.Formatter = LOGGING_FORMATTER, ) -> None: """ Recursively climb logger parent tree to add handler with given formatter to top level logger. Will raise UserWarning if it reaches RootLogger to prevent session modification :param logger: Logger object to find top level parent from :param formatter: Formatter object to describe logging statements """ if isinstance(logger.parent, logging.RootLogger): if len(logger.handlers) == 0: handler = logging.StreamHandler() handler.setFormatter(formatter) logger.addHandler(handler) elif isinstance(logger, logging.RootLogger) or logger.parent is None: raise UserWarning( "Failure in add_root_logger_handler, " "protecting root logger from modification" ) else: add_root_logger_handler(logger=logger.parent, formatter=formatter) class AbstractLoggingClass: """ Base class to ensure logging is setup """ def __init__(self, loglevel: Union[int, str] = logging.WARNING) -> None: """ Set up logging and fetch class level logger for use by subclasses :param loglevel: Verbosity of logging statements """ module_logger = logging.getLogger(self.__class__.__module__) add_root_logger_handler(module_logger) self.logger = module_logger.getChild(self.__class__.__name__) if loglevel is not None: self.logger.setLevel(loglevel)