Source code for kerrpy.utils.logging_utils

import logging
import functools
from inspect import isfunction
import sys
import time

def _logging_method( cls_name, method ):
    """
    This decorator acts on a class method and inyects a call to its logger
    (that is supposed to be stored in the "logger" instance) before and after
    the execution of the method.

    :param cls_name: String
        The name of the class that has the method
    :param method: function
        The method to be decorated
    :returns: function
        The decorated function with the logger call inyected

    NOTE: This is supposed to be used in conjunction with the metaclass LoggingClass
    """

    @functools.wraps(method)
    def wrapper(self,*args,**kwargs):
        if getattr(sys.modules[self.__class__.__module__],'__logmodule__',False):
            self.logger.debug("Entering method {} of class {}".format(method.__name__,cls_name))
            start_time =  time.time()
            result = method(self,*args,**kwargs)
            end_time =  time.time()
            self.logger.debug("Exiting method {} of class {}".format(method.__name__,cls_name))
            self.logger.debug("Execution of method {} took {:0.3f} seconds".format(method.__name__,
                end_time - start_time ))

        else:
            result = method(self,*args,**kwargs)


        return result
    return wrapper

def _inyect_logger( __init__ ):
    """
    This decorator acts on the magic __init__ method of a class  and inyects a
    instance of logging.getLogger into the logger attribute of the class (monkey-patching
    the class in the process).

    :param __init__: function
        The __init__ magic method to be decorated
    :returns: function
        The decorated __init__ method with the logger object inyected

    NOTE: This is supposed to be used in conjunction with the metaclass LoggingClass
    """

    @functools.wraps(__init__)
    def wrapper(self,*args,**kwargs):
        self.logger = logging.getLogger(self.__class__.__name__)
        return __init__(self, *args, **kwargs)
    return wrapper

[docs]class LoggingClass(type): """ This metaclass uses powerfull arcane forces to make every method of the class that inherits from this to log the execution and termination of itself. """
[docs] def __new__(meta, name, bases, attrs): for attrname, attr in attrs.items(): if isfunction(attr) : attrs[attrname] = _logging_method(name,attr) cls = super().__new__(meta, name, bases, attrs) if '__init__' in attrs: setattr(cls,'__init__', _inyect_logger(attrs['__init__'])) else: @_inyect_logger def dummy__init__(self,*args,**kwargs): pass # This is a little tricky because in order to preserve the __mro__ we # need to call super() with the correct arguments **but** we cannot # use self.__class__ because that will break class inheritance with # a fancy eternal recursion. My solution? From the deeps of hell: # A cool closure of the cls variable into the __init__! @_inyect_logger def super__init__(self,*args,**kwargs): super(cls,self).__init__(*args,**kwargs) for base in bases: if '__init__' in base.__dict__: setattr(cls,'__init__', super__init__) break else: setattr(cls,'__init__', dummy__init__) return cls