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)