from __future__ import division, print_function, absolute_import
import os
import logging
from datetime import datetime
from ..modules.patterns import Singleton
class SilenceableStreamHandler(logging.StreamHandler):
def __init__(self, *args, **kwargs):
super(SilenceableStreamHandler, self).__init__(*args, **kwargs)
self.silenced = False
def emit(self, record):
if not self.silenced:
super(SilenceableStreamHandler, self).emit(record)
class SilenceableFileHandler(logging.FileHandler):
def __init__(self, *args, **kwargs):
super(SilenceableFileHandler, self).__init__(*args, **kwargs)
self.silenced = False
def emit(self, record):
if not self.silenced:
super(SilenceableFileHandler, self).emit(record)
[docs]class LoggingMgr(object):
"""The logging manager :class:`.Singleton` class.
The logger manager can be included as a member to any class to
manager logging of information. Each logger is identified by
the module id (`mid`), with which the logger settings can be
changed.
By default a logger with log level LOG_INFO that is output to the stdout
is created.
Attributes
----------
LOG_TYPE_STREAM=0
Log only to output stream (stdout).
LOG_TYPE_FILE=1
Log only to an output file.
LOG_TYPE_ALL=2
Log to both output stream (stdout) and file.
LOG_DEBUG=10
Detailed information, typically of interest only when diagnosing problems.
LOG_INFO=20
Confirmation that things are working as expected.
LOG_WARNING=30
An indication that something unexpected happened, or indicative
of some problem in the near future. The software is still working as expected.
LOG_ERROR=40
Due to a more serious problem, the software has not been able to perform some
function.
LOG_CRITICAL=50
A serious error, indicating that the problem itself may be unable to continue
running.
See Also
--------
:mod:`logging`
Examples
--------
>>> from mlpy.tools.log import LoggingMgr
>>> logger = LoggingMgr().get_logger('my_id')
>>> logger.info('This is a useful information.')
This gets a new logger. If a logger with the module id `my_id` already exists
that logger will be returned, otherwise a logger with the default settings is
created.
>>> LoggingMgr().add_handler('my_id', htype=LoggingMgr.LOG_TYPE_FILE)
This adds a new handler for the logger with module id `my_id` writing the logs
to a file.
>>> LoggingMgr().remove_handler('my_id', htype=LoggingMgr.LOG_TYPE_STREAM)
This removes the stream handler from the logger with module id `my_id`.
>>> LoggingMgr().change_level('my_id', LoggingMgr.LOG_DEBUG, LoggingMgr.LOG_TYPE_ALL)
This changes the log level for all attached handlers of the logger identified by
`my_id` to LOG_DEBUG.
"""
__metaclass__ = Singleton
LOG_TYPE_STREAM = 0
LOG_TYPE_FILE = 1
LOG_TYPE_ALL = 2
LOG_DEBUG = logging.DEBUG
LOG_INFO = logging.INFO
LOG_WARNING = logging.WARNING
LOG_ERROR = logging.ERROR
LOG_CRITICAL = logging.CRITICAL
def __init__(self):
self._loggers = {}
self._verbosity = {}
self._filename = None
[docs] def get_verbosity(self, mid):
""" Gets the verbosity.
The current setting of the verbosity of the logger identified
by `mid` is returned.
Parameters
----------
mid : str
The module id of the logger to change the verbosity of.
Returns
-------
bool :
Whether to turn the verbosity on or off.
"""
return self._verbosity[mid]
[docs] def set_verbosity(self, mid, value):
"""Sets the verbosity.
Turn logging on/off for logger identified by `mid`.
Parameters
----------
mid : str
The module id of the logger to change the verbosity of.
value : bool
Whether to turn the verbosity on or off.
"""
handlers = self._loggers[mid].handlers
for hdl in handlers:
hdl.silenced = value
[docs] def get_logger(self, mid, level=LOG_INFO, htype=LOG_TYPE_STREAM, fmt=None, verbose=True, filename=None):
"""Get the logger instance with the identified `mid`.
If a logger with the `mid` does not exist, a new logger will be created with the given settings.
By default only a stream handler is attached to the logger.
Parameters
----------
mid : str
The module id of the logger.
level : int, optional
The top level logging level. Default is LOG_INFO.
htype : int, optional
The logging type of handler. Default is LOG_TYPE_STREAM.
fmt : str, optional
The format in which the information is presented.
Default is "[%(levelname)-8s ] %(name)s: %(funcName)s: %(message)s"
verbose : bool, optional
The verbosity setting of the logger. Default is True
filename : str, optional
The name of the file the file handler writes the logs to.
Default is a generated filename.
Returns
-------
The logging instance.
"""
if mid not in self._loggers:
logger = logging.getLogger(mid)
logger.setLevel(level)
self._loggers[mid] = logger
self._verbosity[mid] = verbose if verbose is not None else True
self.add_handler(mid, htype, level, fmt, filename)
return self._loggers[mid]
[docs] def add_handler(self, mid, htype=LOG_TYPE_STREAM, hlevel=LOG_INFO, fmt=None, filename=None):
"""Add a handler to the logger.
Parameters
----------
mid : str
The module id of the logger
htype : int, optional
The logging type to add to the handler. Default is LOG_TYPE_STREAM.
hlevel : int, optional
The logging level. Default is LOG_INFO.
fmt : str, optional
The format in which the information is presented.
Default is "[%(levelname)-8s ] %(name)s: %(funcName)s: %(message)s"
filename : str, optional
The name of the file the file handler writes the logs to.
Default is a generated filename.
"""
if fmt is None:
fmt = "[%(levelname)-8s ] %(name)s: %(funcName)s: %(message)s"
formatter = logging.Formatter(fmt)
if htype == self.LOG_TYPE_STREAM or htype == self.LOG_TYPE_ALL:
handler = SilenceableStreamHandler()
self._add_handler(mid, hlevel, handler, formatter)
if htype == self.LOG_TYPE_FILE or htype == self.LOG_TYPE_ALL:
if self._filename is None:
if not os.path.exists("logs"):
os.makedirs("logs")
dt = datetime.now().strftime("%Y-%m-%d %H-%M-%S")
self._filename = "logs\logfile " + dt + ".log"
filename = filename if filename is not None else self._filename
handler = SilenceableFileHandler(filename)
self._add_handler(mid, hlevel, handler, formatter)
[docs] def remove_handler(self, mid, htype):
"""Remove handlers.
Removes all handlers of the given handler type from the logger.
Parameters
----------
mid : str
The module id of the logger
htype : int
The logging type to remove from the handler.
"""
handlers = self._loggers[mid].handlers
for hdl in handlers:
if htype == self.LOG_TYPE_FILE and isinstance(hdl, logging.FileHandler):
self._loggers[mid].removeHandler(hdl)
elif htype == self.LOG_TYPE_STREAM and isinstance(hdl, logging.StreamHandler):
self._loggers[mid].removeHandler(hdl)
[docs] def change_level(self, mid, hlevel, htype=LOG_TYPE_ALL):
"""Set the log level for a handler.
Parameters
----------
mid : str
The module id of the logger
hlevel : int
The logging level.
htype : int, optional
The logging type of handler for which to change the
log level. Default is LOG_TYPE_ALL.
"""
handlers = self._loggers[mid].handlers
if hlevel < self._loggers[mid].level:
self._loggers[mid].level = hlevel
for hdl in handlers:
if htype == self.LOG_TYPE_ALL:
hdl.level = hlevel
elif htype == self.LOG_TYPE_FILE and isinstance(hdl, logging.FileHandler):
hdl.level = hlevel
elif htype == self.LOG_TYPE_STREAM and isinstance(hdl, logging.StreamHandler):
hdl.level = hlevel
def _add_handler(self, mid, hlevel, handler, formatter):
handler.setLevel(hlevel)
handler.setFormatter(formatter)
self._loggers[mid].addHandler(handler)