A python decorator can be a handy way to quickly get a grasp of what is going on with your code. Below is a function decorator for logging when a python function is called, what it has been called with, what it returned, or what error it has raised.

from functools import wraps

def logged(log='trace'):
    def wrap(function):
        def wrapper(*args, **kwargs):
            logger = logging.getLogger(log)
            logger.debug("Calling function '{}' with args={} kwargs={}"
                             .format(function.__name__, args, kwargs))
                response = function(*args, **kwargs)
            except Exception as error:
                logger.debug("Function '{}' raised {} with error '{}'"
                raise error
            logger.debug("Function '{}' returned {}"
            return response
        return wrapper
    return wrap

To enable logging for a particular function simple do the following:

def add(a, b):
    return a + b

You will also need to set up the python logger, a simple setup to log to stdout would look like this:

import logging
import sys

logger = logging.getLogger()
handler = logging.StreamHandler(sys.stdout)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

Then output should look something like this:

2015-11-13 21:52:25,815 - trace - DEBUG - Calling function 'add' with args=(1, 2) kwargs={}
2015-11-13 21:52:25,815 - trace - DEBUG - Function 'add' returned 3

Leaving a lot of logged decorators in place could have a performance hit but a nice solution might be to decide whether to wrap the function based on the logging level at the time.