def log_future_exceptions(logger, f, ignore=()):
    """Log any exceptions set to a future

    Parameters
    ----------
    logger : logging.Logger instance
        logger.exception(...) is called if the future resolves with an exception
    f : Future object
        Future to be monitored for exceptions
    ignore : Exception or tuple of Exception
        Exptected exception(s) to ignore, i.e. they will not be logged.

    Notes
    -----
    This is useful when an async task is started for its side effects without waiting for
    the result. The problem is that if the future's resolution is not checked for
    exceptions, unhandled exceptions in the async task will be silently ignored.

    """
    def log_cb(f):
        try:
            f.result()
        except ignore:
            pass
        except Exception:
            logger.exception('Unhandled exception returned by future')
    f.add_done_callback(log_cb)