In Python, when working on code—whether it's a script or an application—logging variables used in functions is essential for debugging, troubleshooting, and tracing.
The cleanest and most minimal solution for this is to use decorators.
Introduction to Decorators
Decorators dynamically alter the functionality of a function, method, or class without directly modifying the source code. We’ll show how to implement logging for function variables using decorators.
We start by importing the logging
module and initializing a logger with the module name (__name__
):
import logging
import sys
logger = logging.getLogger(__name__)
Setting Up the Logger
Next, we set up the logger, configure the logging level, and format the output to show relevant details:
import logging
import sys
logger = logging.getLogger()
def init_logger(logger):
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
handler = logging.StreamHandler(sys.stdout)
handler.setLevel(logging.DEBUG)
formatter = logging.Formatter("[%(asctime)s][file:%(filename)s] %(levelname)s %(message)s")
handler.setFormatter(formatter)
logger.addHandler(handler)
Implementing the log_content Decorator Now, we create a log_content class decorator that tracks local variables and logs their names and values during the execution of a function:
class log_content(object):
def __init__(self, func):
init_logger(logger)
self._locals = {}
self.func = func
self.name = func.__name__
def __call__(self, *args, **kwargs):
def tracer(frame, event, arg):
if event == "return":
self._locals = frame.f_locals.copy()
# Tracer is activated on the next call, return, or exception
sys.setprofile(tracer)
try:
# Trace the function call
res = self.func(*args, **kwargs)
finally:
# Disable tracer and restore the old one
sys.setprofile(None)
print(self._locals)
for variable, value in self._locals.items():
logger.debug("[@%s] variable %s = %s", self.name, variable, value)
return res
def clear_locals(self):
self._locals = {}
@property
def locals(self):
return self._locals
Usage Example Here’s how you can use the log_content decorator in a Python function:
@log_content
def sample_function(x, y):
z = x + y
return z
When sample_function is called, it will log the values of the local variables x, y, and z.