def funclog(logger):
    """A decorator function that provides debug input/output logging."""

    # check if logger is from structlog
    use_structlog = False
    if STRUCTLOG:
        if isinstance(logger, structlog._config.BoundLoggerLazyProxy):
            real_logger = logger
            use_structlog = True
    # If a Logger object is passed in, use that.  Otherwise, get the default
    # Logger.
    if use_structlog:
        pass
    elif isinstance(logger, Logger):
        real_logger = logger
    else:
        real_logger = getLogger()

    # __qualname__ is prettier but it didn't get added until 3.5
    name_attr = '__name__' if sys.version_info < (3, 5) else '__qualname__'

    def get_arg_string(args, kwargs):
        """Convert args and kwargs to a pretty string."""
        return ', '.join(["'{}'".format(a) if type(a) == str else
                          '{}'.format(a) for a in args] +
                         ["{}='{}'".format(a, v) if type(v) == str else
                          '{}={}'.format(a, v) for a, v in sorted(kwargs.items())])

    def real_decorator(fn):
        @functools.wraps(fn)
        def wrapper(*args, **kwargs):
            frame_info = inspect.getframeinfo(inspect.stack()[1][0])
            filename = os.path.basename(frame_info.filename)
            lineno = frame_info.lineno
            func_name = getattr(fn, name_attr)
            arg_string = get_arg_string(args, kwargs)
            source_info = '{}:{}:{}({})'.format(filename, lineno, func_name,
                                                arg_string)
            if use_structlog:
                real_logger.debug(u'calling', source_info=source_info)
            else:
                real_logger.debug(u'calling %s', source_info)
            try:
                res = fn(*args, **kwargs)
            except Exception as e:
                if use_structlog:
                    real_logger.exception(
                        u'{} threw exception'.format(source_info), e=e)
                else:
                    real_logger.exception(
                        u'%s threw exception:\n%s', source_info, e)
                raise
            if use_structlog:
                real_logger.debug(u'{} returned'.format(source_info), res=res)
            else:
                real_logger.debug(u'%s returned: %s', source_info, res)
            return res
        return wrapper

    if type(logger) == type(real_decorator):
        return real_decorator(logger)

    return real_decorator