Instantly share code, notes, and snippets.

Embed
What would you like to do?
Lazy Logging - the below snippet is relevant when you wish to log some debugging information, but that information is relatively costly to generate. Ideally, you'll want to generate that information only when DEBUG level is actually enabled. In other words, only when the message formatting actually occurs.
# IMPLEMENTATION
class LazyLogging(object):
"""
A utility class that wraps a method
"""
def __init__(self, function):
self.function = function
def __str__(self):
return str(self.function())
# USAGE
class MyClass(object):
def __init__(self):
self._lazy_debugging_info = LazyLogging(self._debugging_info)
# implementation detail: it's important to not that even though
# `_debugging_info` is a descriptor (as all the methods), passing
# the value returned from `self._debugging_info` produces a
# bound function object (so no need to pass `self` or anything
# of the sort)
def _debugging_info(self):
# generate some string
# from quite a few fields in this
# class and return
return "my debugging string"
def some_other_method(self):
# do some stuff...
# and debug log
logger.debug("I didn't some stuff. Info: %s", self._lazy_debugging_info)
@schnittstabil

This comment has been minimized.

schnittstabil commented Nov 11, 2016

This seems very inefficient and cumbersome to me. You need to create LazyLogging objects for each object and for each debug generating method (_debugging_info above). Furthermore you must write those debug generating methods to use this approach:

class LazyLogging(object):
    def __init__(self, function):
        self.function = function

    def __str__(self):
        return str(self.function())


class MyClass(object):
    def __init__(self):
        self._lazy_debug_1 = LazyLogging(self._debug_1)
        self._lazy_debug_2 = LazyLogging(self._debug_2)
        self._lazy_debug_3 = LazyLogging(self._debug_3)

    def _debug_1(self):
        return "my debugging string 1"

    def _debug_2(self):
        return "my debugging string 2"

    def _debug_3(self):
        return "my debugging string 3"

    def m_1(self):
        logger.debug("Info: %s", self._lazy_debug_1)

    def m_2(self):
        logger.debug("Info: %s", self._lazy_debug_2)

    def m_3(self):
        logger.debug("Info: %s", self._lazy_debug_3)

These debug generating methods (_debug_*) may fit your needs and your coding style. However, personally, I think it is rather inconvenient. Nevertheless, even in this scenario, I see no need to create those expensive LazyLogging instances:

def lazy_debug(logger, msg, fn):
    if logger.isEnabledFor(logging.DEBUG):
        logger.debug(msg, fn())

class MyClass(object):
    def __init__(self):
        pass

    def _debug_1(self):
        return "my debugging string 1"

    def _debug_2(self):
        return "my debugging string 2"

    def _debug_3(self):
        return "my debugging string 3"

    def m_1(self):
        lazy_debug(logger, "Info: %s", self._debug_1)

    def m_2(self):
        lazy_debug(logger, "Info: %s", self._debug_2)

    def m_3(self):
        lazy_debug(logger, "Info: %s", self._debug_3)

If you want to improve this, you may extend the logger class with a lazy_debug method, which is hard though, IIRC.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment