Module utils.logger

Expand source code
#!/usr/bin/env python3

import re
import logging
import coloredlogs


class Logger(object):
    """
    Wrapper over coloredlogs package that enables colored terminal output for Python logging module
    """

    log_format = '[%(asctime)s] (%(threadName)s) {%(filename)s:%(lineno)d} %(levelname)s - %(message)s'
    log_level = None

    @classmethod
    def setup_logging(cls, loglevel='INFO', logfilter=".*", logfile=""):
        cls.registered_loggers = dict()
        cls.log_level = loglevel
        cls.log_logfilter_regexp = logfilter
        cls.log_logfilter_regexp_compiled = re.compile(logfilter)

        numeric_level = getattr(logging, loglevel.upper(), None)

        if not isinstance(numeric_level, int):
            raise ValueError('Invalid log level: %s' % loglevel)
        if logfile:
            logging.basicConfig(handlers=[logging.FileHandler(logfile), logging.StreamHandler()],
                                level=numeric_level,
                                format=cls.log_format,
                                datefmt='%Y-%m-%d %H:%M:%S',)
        else:
            logging.basicConfig(level=numeric_level,
                                format=cls.log_format,
                                datefmt='%Y-%m-%d %H:%M:%S',)

    @classmethod
    def get(cls, logger_name='default'):
        if logger_name in cls.registered_loggers:
            return cls.registered_loggers[logger_name]
        else:
            return cls(logger_name)

    def __init__(self, logger_name='default'):
        """
        Instantiate logger

        Args:
            logger_name(str): logger name
        """
        if logger_name in self.registered_loggers:
            raise ValueError(f"Logger {logger_name} already exists. Call with Logger.get(\"{logger_name}\")")
        else:
            self.name = logger_name
            self.logger = logging.getLogger(self.name)

            # FIX: instead of registering system logger - register self with custom filtering logic
            # INCORRECT: self.registered_loggers[self.name] = self.logger
            self.registered_loggers[self.name] = self

            coloredlogs.install(
                level=self.log_level,
                logger=self.logger,
                fmt=self.log_format,
                datefmt='%Y-%m-%d %H:%M:%S')

    def log(self, loglevel, msg):
        """
        Main entry point for log text messages.
        The logging module is intended to be thread-safe without any special work needing to be done by its clients
           https://docs.python.org/3/library/logging.html#thread-safety

        Args:
            loglevel(str): logging level is one of case insensitive string: DEBUG, INFO, WARNING, ERROR, CRITICAL
            msg(str): text message to log
        """
        if not self.log_logfilter_regexp_compiled.match(msg):
            return

        loglevel = loglevel.upper()
        if loglevel == 'DEBUG':
            self.logger.debug(msg)
        elif loglevel == 'INFO':
            self.logger.info(msg)
        elif loglevel == 'WARNING':
            self.logger.warning(msg)
        elif loglevel == 'ERROR':
            self.logger.error(msg)
        elif loglevel == 'CRITICAL':
            self.logger.critical(msg)

    def debug(self, msg):
        """
        Log message in debug mode.

        Args:
            msg(str): text message to log
        """
        self.log('debug', msg)

    def info(self, msg):
        """
        Log message in info mode.

        Args:
            msg(str): text message to log
        """
        self.log('info', msg)

    def warning(self, msg):
        """
        Log message in warning mode.

        Args:
            msg(str): text message to log
        """
        self.log('warning', msg)

    def error(self, msg):
        """
        Log message in error mode.

        Args:
            msg(str): text message to log
        """
        self.log('error', msg)

    def critical(self, msg):
        """
        Log message in critical mode.

        Args:
            msg(str): text message to log
        """
        self.log('critical', msg)

Classes

class Logger (logger_name='default')

Wrapper over coloredlogs package that enables colored terminal output for Python logging module

Instantiate logger

Args

logger_name(str): logger name

Expand source code
class Logger(object):
    """
    Wrapper over coloredlogs package that enables colored terminal output for Python logging module
    """

    log_format = '[%(asctime)s] (%(threadName)s) {%(filename)s:%(lineno)d} %(levelname)s - %(message)s'
    log_level = None

    @classmethod
    def setup_logging(cls, loglevel='INFO', logfilter=".*", logfile=""):
        cls.registered_loggers = dict()
        cls.log_level = loglevel
        cls.log_logfilter_regexp = logfilter
        cls.log_logfilter_regexp_compiled = re.compile(logfilter)

        numeric_level = getattr(logging, loglevel.upper(), None)

        if not isinstance(numeric_level, int):
            raise ValueError('Invalid log level: %s' % loglevel)
        if logfile:
            logging.basicConfig(handlers=[logging.FileHandler(logfile), logging.StreamHandler()],
                                level=numeric_level,
                                format=cls.log_format,
                                datefmt='%Y-%m-%d %H:%M:%S',)
        else:
            logging.basicConfig(level=numeric_level,
                                format=cls.log_format,
                                datefmt='%Y-%m-%d %H:%M:%S',)

    @classmethod
    def get(cls, logger_name='default'):
        if logger_name in cls.registered_loggers:
            return cls.registered_loggers[logger_name]
        else:
            return cls(logger_name)

    def __init__(self, logger_name='default'):
        """
        Instantiate logger

        Args:
            logger_name(str): logger name
        """
        if logger_name in self.registered_loggers:
            raise ValueError(f"Logger {logger_name} already exists. Call with Logger.get(\"{logger_name}\")")
        else:
            self.name = logger_name
            self.logger = logging.getLogger(self.name)

            # FIX: instead of registering system logger - register self with custom filtering logic
            # INCORRECT: self.registered_loggers[self.name] = self.logger
            self.registered_loggers[self.name] = self

            coloredlogs.install(
                level=self.log_level,
                logger=self.logger,
                fmt=self.log_format,
                datefmt='%Y-%m-%d %H:%M:%S')

    def log(self, loglevel, msg):
        """
        Main entry point for log text messages.
        The logging module is intended to be thread-safe without any special work needing to be done by its clients
           https://docs.python.org/3/library/logging.html#thread-safety

        Args:
            loglevel(str): logging level is one of case insensitive string: DEBUG, INFO, WARNING, ERROR, CRITICAL
            msg(str): text message to log
        """
        if not self.log_logfilter_regexp_compiled.match(msg):
            return

        loglevel = loglevel.upper()
        if loglevel == 'DEBUG':
            self.logger.debug(msg)
        elif loglevel == 'INFO':
            self.logger.info(msg)
        elif loglevel == 'WARNING':
            self.logger.warning(msg)
        elif loglevel == 'ERROR':
            self.logger.error(msg)
        elif loglevel == 'CRITICAL':
            self.logger.critical(msg)

    def debug(self, msg):
        """
        Log message in debug mode.

        Args:
            msg(str): text message to log
        """
        self.log('debug', msg)

    def info(self, msg):
        """
        Log message in info mode.

        Args:
            msg(str): text message to log
        """
        self.log('info', msg)

    def warning(self, msg):
        """
        Log message in warning mode.

        Args:
            msg(str): text message to log
        """
        self.log('warning', msg)

    def error(self, msg):
        """
        Log message in error mode.

        Args:
            msg(str): text message to log
        """
        self.log('error', msg)

    def critical(self, msg):
        """
        Log message in critical mode.

        Args:
            msg(str): text message to log
        """
        self.log('critical', msg)

Class variables

var log_format
var log_level

Static methods

def get(logger_name='default')
Expand source code
@classmethod
def get(cls, logger_name='default'):
    if logger_name in cls.registered_loggers:
        return cls.registered_loggers[logger_name]
    else:
        return cls(logger_name)
def setup_logging(loglevel='INFO', logfilter='.*', logfile='')
Expand source code
@classmethod
def setup_logging(cls, loglevel='INFO', logfilter=".*", logfile=""):
    cls.registered_loggers = dict()
    cls.log_level = loglevel
    cls.log_logfilter_regexp = logfilter
    cls.log_logfilter_regexp_compiled = re.compile(logfilter)

    numeric_level = getattr(logging, loglevel.upper(), None)

    if not isinstance(numeric_level, int):
        raise ValueError('Invalid log level: %s' % loglevel)
    if logfile:
        logging.basicConfig(handlers=[logging.FileHandler(logfile), logging.StreamHandler()],
                            level=numeric_level,
                            format=cls.log_format,
                            datefmt='%Y-%m-%d %H:%M:%S',)
    else:
        logging.basicConfig(level=numeric_level,
                            format=cls.log_format,
                            datefmt='%Y-%m-%d %H:%M:%S',)

Methods

def critical(self, msg)

Log message in critical mode.

Args

msg(str): text message to log

Expand source code
def critical(self, msg):
    """
    Log message in critical mode.

    Args:
        msg(str): text message to log
    """
    self.log('critical', msg)
def debug(self, msg)

Log message in debug mode.

Args

msg(str): text message to log

Expand source code
def debug(self, msg):
    """
    Log message in debug mode.

    Args:
        msg(str): text message to log
    """
    self.log('debug', msg)
def error(self, msg)

Log message in error mode.

Args

msg(str): text message to log

Expand source code
def error(self, msg):
    """
    Log message in error mode.

    Args:
        msg(str): text message to log
    """
    self.log('error', msg)
def info(self, msg)

Log message in info mode.

Args

msg(str): text message to log

Expand source code
def info(self, msg):
    """
    Log message in info mode.

    Args:
        msg(str): text message to log
    """
    self.log('info', msg)
def log(self, loglevel, msg)

Main entry point for log text messages. The logging module is intended to be thread-safe without any special work needing to be done by its clients https://docs.python.org/3/library/logging.html#thread-safety

Args

loglevel(str): logging level is one of case insensitive string: DEBUG, INFO, WARNING, ERROR, CRITICAL msg(str): text message to log

Expand source code
def log(self, loglevel, msg):
    """
    Main entry point for log text messages.
    The logging module is intended to be thread-safe without any special work needing to be done by its clients
       https://docs.python.org/3/library/logging.html#thread-safety

    Args:
        loglevel(str): logging level is one of case insensitive string: DEBUG, INFO, WARNING, ERROR, CRITICAL
        msg(str): text message to log
    """
    if not self.log_logfilter_regexp_compiled.match(msg):
        return

    loglevel = loglevel.upper()
    if loglevel == 'DEBUG':
        self.logger.debug(msg)
    elif loglevel == 'INFO':
        self.logger.info(msg)
    elif loglevel == 'WARNING':
        self.logger.warning(msg)
    elif loglevel == 'ERROR':
        self.logger.error(msg)
    elif loglevel == 'CRITICAL':
        self.logger.critical(msg)
def warning(self, msg)

Log message in warning mode.

Args

msg(str): text message to log

Expand source code
def warning(self, msg):
    """
    Log message in warning mode.

    Args:
        msg(str): text message to log
    """
    self.log('warning', msg)