Skip to content

Instantly share code, notes, and snippets.

@guyarad
Last active November 11, 2016 12:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save guyarad/15ab88ebbf5fdefab9dbceec888a3a37 to your computer and use it in GitHub Desktop.
Save guyarad/15ab88ebbf5fdefab9dbceec888a3a37 to your computer and use it in GitHub Desktop.
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
Copy link

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