import sys
import logging
import datetime
import collections
from swamp import __version__
from swamp.logger.colorformat import ColorFormatter
[docs]class SwampLogger(logging.Logger):
"""Class that implements methods to assist with SWAMP logging messages.
:param str name: name to identify the logger
:param bool silent: if True, messages are not logged
:param list args: arguments passed to :py:obj:`~logging.Logger`
:param dict kwargs: arguments passed to :py:obj:`~logging.Logger`
:ivar list msg_record: a list with all the messaged recorded into the log (even if `silent` is set to True)
"""
def __init__(self, name, silent=False, *args, **kwargs):
self.silent = silent
self.msg_record = []
super(SwampLogger, self).__init__(name, *args, **kwargs)
def __repr__(self):
return 'SwampLogger(name=%s, silent=%s)' % (self.name, self.silent)
# ------------------ Properties ------------------
@property
def greeting_msg(self):
"""A greeting message to appear when the logger initiates, informs of the current time and SWAMP version"""
return """\n#########################################################################
#########################################################################
#########################################################################
# SWAMP - Solving structures With Alpha helical Membrane Pairs #
#########################################################################\n\n
SWAMP version %s
Current time: %s\n
""" % (__version__, self.now)
@property
def error_header(self):
"""Header to highlight error messages"""
return """
\033[91m\033[1m**********************************************************************
******************* SWAMP ERROR *******************
**********************************************************************
\033[0m
"""
@property
def logging_levels(self):
"""A dictionary with the different logger levels that can be set"""
return {
"notset": logging.NOTSET,
"info": logging.INFO,
"debug": logging.DEBUG,
"warning": logging.WARNING,
"error": logging.ERROR,
"critical": logging.CRITICAL,
}
@property
def record(self):
"""Named tuple to be used as a template for the records stored at \
:py:attr:`~swamp.logger.swamplogger.SwampLogger.msg_record`"""
return collections.namedtuple('record', ['msg', 'level'])
@property
def msg(self):
"""Property to be used as the format for logged messages"""
return '%s {}: {}' % self.now
@property
def now(self):
"""Current time"""
now = datetime.datetime.now()
return now.strftime("%b-%d-%Y %H:%M:%S")
# ------------------ Methods ------------------
[docs] def error(self, msg, *args, **kwargs):
"""Extend :py:func:`logging.Logger.error` to include the \
:py:attr:`~swamp.logger.swamplogger.SwampLogger.error_header`, check if \
:py:attr:`~swamp.logger.swamplogger.SwampLogger.silent` and format the message"""
msg = self.msg.format('ERROR', msg)
if not self.silent:
super(SwampLogger, self).error(self.error_header)
super(SwampLogger, self).error(msg, *args, **kwargs)
self.msg_record.append(self.record(msg=self.error_header, level=logging.ERROR))
self.msg_record.append(self.record(msg=msg, level=logging.ERROR))
[docs] def warning(self, msg, *args, **kwargs):
"""Extend :py:func:`logging.Logger.warning` to check if \
:py:attr:`~swamp.logger.swamplogger.SwampLogger.silent` and format the message"""
msg = self.msg.format('WARNING', msg)
if not self.silent:
super(SwampLogger, self).warning(msg, *args, **kwargs)
self.msg_record.append(self.record(msg=msg, level=logging.WARNING))
[docs] def info(self, msg, *args, **kwargs):
"""Extend :py:func:`logging.Logger.info` to check if \
:py:attr:`~swamp.logger.swamplogger.SwampLogger.silent` and and format the message"""
if isinstance(msg, collections.Iterable) and '****' not in msg and '####' not in msg:
msg = self.msg.format('INFO', msg)
if not self.silent:
super(SwampLogger, self).info(msg, *args, **kwargs)
self.msg_record.append(self.record(msg=msg, level=logging.INFO))
[docs] def debug(self, msg, *args, **kwargs):
"""Extend :py:func:`logging.Logger.debug` to check if \
:py:attr:`~swamp.logger.swamplogger.SwampLogger.silent` and and format the message"""
msg = self.msg.format('DEBUG', msg)
if not self.silent:
super(SwampLogger, self).debug(msg, *args, **kwargs)
self.msg_record.append(self.record(msg=msg, level=logging.DEBUG))
[docs] def add_console_handler(self, level='info'):
"""Add the console handler to the :py:obj:`~logging.Logger`"""
ch = logging.StreamHandler(stream=sys.stdout)
ch.setLevel(self.logging_levels.get(level, logging.INFO))
ch.setFormatter(ColorFormatter())
self.addHandler(ch)
[docs] def add_file_handler(self, fname, level='debug'):
"""Add the file handler to the :py:obj:`~logging.Logger`"""
fh = logging.FileHandler(fname)
fh.setLevel(self.logging_levels.get(level, logging.INFO))
fh.setFormatter(logging.Formatter())
self.addHandler(fh)
[docs] def init(self, console_level="info", logfile_level="debug", logfile=None, use_console=True):
"""Method to initiate the :py:obj:`~swamp.logger.swamplogger.SwampLogger`
:param str console_level: indicate the console logging level (default: 'info')
:param str logfile_level: indicate the logfile logging level (default: 'debug')
:param str logfile: file name to create a log file (default: None)
:param bool use_console: indicate whether or not to use the console while logging (default: True)
"""
if use_console:
self.add_console_handler(console_level)
if logfile is not None:
self.add_file_handler(fname=logfile, level=logfile_level)
self.info(self.greeting_msg)
self.debug("File logger level: %s", self.logging_levels.get(logfile_level, logging.INFO))
self.debug("Console logger level: %s", self.logging_levels.get(console_level, logging.INFO))
[docs] def dump_records(self):
"""Log all the records in the :py:attr:`~swamp.logger.swamplogger.SwampLogger.msg_record`"""
current_list = list(self.msg_record)
for record in current_list:
self.log(record.level, record.msg)