#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
**verbose.py**
**Platform:**
Windows, Linux, Mac Os X.
**Description:**
This module defines **Foundations** package verbose and logging objects.
**Others:**
"""
#**********************************************************************************************************************
#*** Future imports.
#**********************************************************************************************************************
from __future__ import unicode_literals
#**********************************************************************************************************************
#*** External imports.
#**********************************************************************************************************************
import functools
import hashlib
import inspect
import itertools
import logging
import sys
import threading
import tempfile
#**********************************************************************************************************************
#*** Internal imports.
#**********************************************************************************************************************
import foundations.trace
from foundations.globals.constants import Constants
#**********************************************************************************************************************
#*** Module attributes.
#**********************************************************************************************************************
__author__ = "Thomas Mansencal"
__copyright__ = "Copyright (C) 2008 - 2013 - Thomas Mansencal"
__license__ = "GPL V3.0 - http://www.gnu.org/licenses/"
__maintainer__ = "Thomas Mansencal"
__email__ = "thomas.mansencal@gmail.com"
__status__ = "Production"
__all__ = ["THREADS_IDENTIFIERS",
"INDENT_LEVEL",
"LOGGER",
"LOGGING_DEFAULT_FORMATTER",
"LOGGING_EXTENDED_FORMATTER",
"LOGGING_STANDARD_FORMATTER",
"TRACER_LOGGING_FUNCTION",
"Streamer"
"StandardOutputStreamer",
"indentMessage",
"tracer",
"installLogger",
"uninstallLogger",
"getLoggingConsoleHandler",
"getLoggingFileHandler",
"getLoggingStreamHandler",
"removeLoggingHandler",
"setVerbosityLevel"]
THREADS_IDENTIFIERS = {}
INDENT_LEVEL = 0
#**********************************************************************************************************************
#*** Logging classes and definitions.
#**********************************************************************************************************************
def _LogRecord_getAttribute(self, attribute):
"""
This definition overrides logging.LogRecord.__getattribute__ method
in order to manipulate requested attributes values.
:param attribute: Attribute name. ( String )
:return: Modified method. ( Object )
"""
if attribute == "__dict__":
threadIdent = threading.currentThread().ident
if not threadIdent in THREADS_IDENTIFIERS:
THREADS_IDENTIFIERS[threadIdent] = (threading.currentThread().name,
hashlib.md5(threading.currentThread().name).hexdigest()[:8])
object.__getattribute__(self, attribute)["threadName"] = THREADS_IDENTIFIERS[threadIdent][1]
return object.__getattribute__(self, attribute)
else:
return object.__getattribute__(self, attribute)
logging.LogRecord.__getattribute__ = _LogRecord_getAttribute
#**********************************************************************************************************************
#*** Module attributes.
#**********************************************************************************************************************
LOGGER = logging.getLogger(Constants.logger)
LOGGING_DEFAULT_FORMATTER = logging.Formatter("%(levelname)-8s: %(message)s")
LOGGING_EXTENDED_FORMATTER = logging.Formatter("%(asctime)s - %(threadName)s - %(levelname)-8s: %(message)s")
LOGGING_STANDARD_FORMATTER = logging.Formatter()
TRACER_LOGGING_FUNCTION = LOGGER.info
#**********************************************************************************************************************
#*** Module classes and definitions.
#**********************************************************************************************************************
[docs]class Streamer(object):
"""
This class is intented to be used as a stream object for :class:`logging.StreamHandler` logging handler.
"""
def __init__(self, stream=None):
"""
.. Sphinx: Statements updated for auto-documentation purpose.
:param stream: Stream object. ( Object )
"""
self.__stream = []
#******************************************************************************************************************
#*** Attributes properties.
#******************************************************************************************************************
@property
def stream(self):
"""
This method is the property for **self.__stream** attribute.
:return: self.__stream. ( List )
"""
return self.__stream
@stream.setter
def stream(self, value):
"""
This method is the setter method for **self.__stream** attribute.
:param value: Attribute value. ( List )
"""
if value is not None:
assert type(value) is list, "'{0}' attribute: '{1}' type is not 'list'!".format("stream", value)
self.__stream = value
@stream.deleter
[docs] def stream(self):
"""
This method is the deleter method for **self.__stream** attribute.
"""
raise Exception("{0} | '{1}' attribute is not deletable!".format(self.__class__.__name__, "stream"))
#******************************************************************************************************************
#*** Class methods.
#******************************************************************************************************************
[docs] def write(self, message):
"""
This method provides write ability to the class.
:param message: Current message. ( String )
"""
self.__stream.append(message)
[docs] def flush(self):
"""
This method flushes the current stream.
"""
pass
[docs]class StandardOutputStreamer(object):
"""
| This class is a redirection object intented to be used for :data:`sys.stdout` and :data:`sys.stderr` streams.
| Logging messages will be written to given logger handlers.
"""
def __init__(self, logger):
"""
.. Sphinx: Statements updated for auto-documentation purpose.
:param logger: Logger. ( Object )
"""
self.__logger = logger
#******************************************************************************************************************
#*** Attributes properties.
#******************************************************************************************************************
@property
def logger(self):
"""
This method is the property for **self.__logger** attribute.
:return: self.__logger. ( Logger )
"""
return self.__logger
@logger.setter
def logger(self, value):
"""
This method is the setter method for **self.__logger** attribute.
:param value: Attribute value. ( Logger )
"""
raise Exception("{0} | '{1}' attribute is read only!".format(self.__class__.__name__, "logger"))
@logger.deleter
[docs] def logger(self):
"""
This method is the deleter method for **self.__logger** attribute.
"""
raise Exception("{0} | '{1}' attribute is not deletable!".format(self.__class__.__name__, "logger"))
#******************************************************************************************************************
#*** Class methods.
#******************************************************************************************************************
[docs] def write(self, message):
"""
This method writes given message to logger handlers.
:param message: Message. ( String )
:return: Method success. ( Boolean )
"""
for handler in self.__logger.__dict__["handlers"]:
handler.stream.write(message)
return True
[docs]def indentMessage(message):
"""
This definition idents given message using the attr`INDENT_LEVEL` attribute value.
:param message: Message to indent. ( String )
:return: indented message. ( String )
"""
return "{0}{1}".format(" " * 4 * INDENT_LEVEL, message)
[docs]def tracer(object):
"""
| This decorator is used for execution tracing.
| Any method / definition decorated will have it's execution traced through debug messages.
| Both object entry and exit are logged.
Entering in an object::
INFO : ---> foundations.environment.getUserApplicationDataDirectory() <<<---
Exiting from an object::
INFO : <--- foundations.environment.getSystemApplicationDataDirectory() ^ '...' --->
:param object: Object to decorate. ( Object )
:return: Object. ( Object )
"""
#*** Sphinx: Decorator commented for auto-documentation purpose. @functools.wraps(object)
#*** Sphinx: Decorator commented for auto-documentation purpose. @functools.partial(foundations.trace.validateTracer, object)
def tracerWrapper(*args, **kwargs):
"""
This decorator is used for execution tracing.
:param \*args: Arguments. ( \* )
:param \*\*kwargs: Keywords arguments. ( \*\* )
:return: Object. ( Object )
"""
global INDENT_LEVEL
traceName = foundations.trace.getTraceName(object)
code = object.func_code
argsCount = code.co_argcount
argsNames = code.co_varnames[:argsCount]
functionDefaults = object.func_defaults or list()
argsDefaults = dict(zip(argsNames[-len(functionDefaults):], functionDefaults))
positionalArgs = map(foundations.trace.formatArgument, zip(argsNames, args))
defaultedArgs = [foundations.trace.formatArgument((name, argsDefaults[name])) \
for name in argsNames[len(args):] if name not in kwargs]
namelessArgs = map(repr, args[argsCount:])
keywordArgs = map(foundations.trace.formatArgument, kwargs.items())
TRACER_LOGGING_FUNCTION(indentMessage("---> {0}({1}) <---".format(traceName,
", ".join(itertools.chain(positionalArgs,
defaultedArgs,
namelessArgs,
keywordArgs)))))
INDENT_LEVEL += 1
value = object(*args, **kwargs)
INDENT_LEVEL -= 1
TRACER_LOGGING_FUNCTION(indentMessage("<--- {0} ^ {1} --->".format(traceName, repr(value))))
return value
return tracerWrapper
[docs]def installLogger(logger=None, module=None):
"""
This definition installs given logger in given module or default logger in caller introspected module.
:param logger: Logger to install. ( Logger )
:param module: Module. ( Module )
:return: Logger. ( Logger )
"""
logger = logging.getLogger(Constants.logger) if logger is None else logger
if module is None:
# Note: inspect.getmodule() can return the wrong module if it has been imported with different relatives paths.
module = sys.modules.get(inspect.currentframe().f_back.f_globals["__name__"])
setattr(module, "LOGGER", logger)
foundations.trace.registerModule(module)
return logger
[docs]def uninstallLogger(logger=None, module=None):
"""
This definition uninstalls given logger in given module or default logger in caller introspected module.
:param logger: Logger to uninstall. ( Logger )
:param module: Module. ( Module )
:return: Definition success. ( Boolean )
"""
logger = logging.getLogger(Constants.logger) if logger is None else logger
if module is None:
# Note: inspect.getmodule() can return the wrong module if it has been imported with different relatives paths.
module = sys.modules.get(inspect.currentframe().f_back.f_globals["__name__"])
hasattr(module, "LOGGER") and delattr(module, "LOGGER")
return True
[docs]def getLoggingConsoleHandler(logger=None, formatter=LOGGING_DEFAULT_FORMATTER):
"""
This definition adds a logging console handler to given logger or default logger.
:param logger: Logger to add the handler to. ( Logger )
:param formatter: Handler formatter. ( F )
:return: Added handler. ( Handler )
"""
logger = LOGGER if logger is None else logger
loggingConsoleHandler = logging.StreamHandler(sys.__stdout__)
loggingConsoleHandler.setFormatter(formatter)
logger.addHandler(loggingConsoleHandler)
return loggingConsoleHandler
[docs]def getLoggingFileHandler(logger=None, file=None, formatter=LOGGING_DEFAULT_FORMATTER):
"""
This definition adds a logging file handler to given logger or default logger using given file.
:param logger: Logger to add the handler to. ( Logger )
:param file: File to verbose into. ( String )
:param formatter: Handler formatter. ( Formatter )
:return: Added handler. ( Handler )
"""
logger = LOGGER if logger is None else logger
file = tempfile.NamedTemporaryFile().name if file is None else file
loggingFileHandler = logging.FileHandler(file)
loggingFileHandler.setFormatter(formatter)
logger.addHandler(loggingFileHandler)
return loggingFileHandler
[docs]def getLoggingStreamHandler(logger=None, formatter=LOGGING_DEFAULT_FORMATTER):
"""
This definition adds a logging stream handler to given logger or default logger using given file.
:param logger: Logger to add the handler to. ( Logger )
:param file: File to verbose into. ( String )
:param formatter: Handler formatter. ( Formatter )
:return: Added handler. ( Handler )
"""
logger = LOGGER if logger is None else logger
loggingStreamHandler = logging.StreamHandler(Streamer())
loggingStreamHandler.setFormatter(formatter)
logger.addHandler(loggingStreamHandler)
return loggingStreamHandler
[docs]def removeLoggingHandler(handler, logger=None):
"""
This definition removes given logging handler from given logger.
:param handler: Handler. ( Handler )
:param logger: Handler logger. ( Logger )
:return: Definition success. ( Boolean )
"""
logger = LOGGER if logger is None else logger
logger.handlers and LOGGER.debug("> Stopping handler: '{0}'.".format(handler))
logger.removeHandler(handler)
return True
[docs]def setVerbosityLevel(verbosityLevel=3, logger=None):
"""
This definition defines logging verbosity level.
Available verbosity levels::
0: Critical.
1: Error.
2: Warning.
3: Info.
4: Debug.
:param verbosityLevel: Verbosity level. ( Integer )
:param logger: Logger to set the verbosity level to. ( Logger )
:return: Definition success. ( Boolean )
"""
logger = LOGGER if logger is None else logger
if verbosityLevel == 0:
logger.setLevel(logging.CRITICAL)
logging.disable(logging.ERROR)
elif verbosityLevel == 1:
logger.setLevel(logging.ERROR)
logging.disable(logging.WARNING)
elif verbosityLevel == 2:
logger.setLevel(logging.WARNING)
logging.disable(logging.INFO)
elif verbosityLevel == 3:
logger.setLevel(logging.INFO)
logging.disable(logging.DEBUG)
elif verbosityLevel == 4:
logger.setLevel(logging.DEBUG)
logging.disable(logging.NOTSET)
return True