Log the given message with the given log level using a logger named based 
    on the scope of the calling code.  This saves you time because you will be 
    able to see where all your log messages are being generated from without 
    having to type anything.  This function is meant to be called by one or 
    more wrapper functions, so the `frame_depth` argument is provided to 
    specify which scope should be used to name the logger.

def _log(level, message, frame_depth=2, **kwargs):
    """
    Log the given message with the given log level using a logger named based 
    on the scope of the calling code.  This saves you time because you will be 
    able to see where all your log messages are being generated from without 
    having to type anything.  This function is meant to be called by one or 
    more wrapper functions, so the `frame_depth` argument is provided to 
    specify which scope should be used to name the logger.
    """
    import inspect

    try:
        # Inspect variables two frames up from where we currently are (by 
        # default).  One frame up is assumed to be one of the helper methods 
        # defined in this module, so we aren't interested in that.  Two frames 
        # up should be the frame that's actually trying to log something.

        frame = inspect.stack()[frame_depth][0]
        frame_below = inspect.stack()[frame_depth-1][0]
        
        # Collect all the variables in the scope of the calling code, so they 
        # can be substituted into the message.

        scope = {}
        scope.update(frame.f_globals)
        scope.update(frame.f_locals)

        # If the calling frame is inside a class (deduced based on the presence 
        # of a 'self' variable), name the logger after that class.  Otherwise 
        # if the calling frame is inside a function, name the logger after that 
        # function.  Otherwise name it after the module of the calling scope.

        self = frame.f_locals.get('self')
        function = inspect.getframeinfo(frame).function
        module = frame.f_globals['__name__']

        if self is not None:
            name = '.'.join([
                    self.__class__.__module__,
                    self.__class__.__name__
            ])

        elif function != '<module>':
            name = '.'.join([module, function])

        else:
            name = module

        # Trick the logging module into reading file names and line numbers 
        # from the correct frame by monkey-patching logging.currentframe() with 
        # a function that returns the frame below the real calling frame (this 
        # indirection is necessary because the logging module will look one 
        # frame above whatever this function returns).  Undo all this after 
        # logging our message, to avoid interfering with other loggers.

        with _temporarily_set_logging_frame(frame_below):
            logger = logging.getLogger(name)
            logger.log(level, message.format(**scope), **kwargs)

    finally:
        try: del frame
        except UnboundLocalError: pass