configures a logger when required write to stderr or a file

def _configure_logger(fmt, quiet, level, fpath,
    pre_hooks, post_hooks, metric_grouping_interval):
    """
    configures a logger when required write to stderr or a file
    """

    # NOTE not thread safe. Multiple BaseScripts cannot be instantiated concurrently.
    level = getattr(logging, level.upper())

    global _GLOBAL_LOG_CONFIGURED
    if _GLOBAL_LOG_CONFIGURED:
        return

    # since the hooks need to run through structlog, need to wrap them like processors
    def wrap_hook(fn):
        @wraps(fn)
        def processor(logger, method_name, event_dict):
            fn(event_dict)
            return event_dict

        return processor

    processors = define_log_processors()
    processors.extend(
        [ wrap_hook(h) for h in pre_hooks ]
    )
    if metric_grouping_interval:
        processors.append(metrics_grouping_processor)

    log_renderer = define_log_renderer(fmt, fpath, quiet)
    stderr_required = (not quiet)
    pretty_to_stderr = (
        stderr_required
        and (
            fmt == "pretty"
            or (fmt is None and sys.stderr.isatty())
        )
    )

    should_inject_pretty_renderer = (
        pretty_to_stderr
        and not isinstance(log_renderer, structlog.dev.ConsoleRenderer)
    )
    if should_inject_pretty_renderer:
        stderr_required = False
        processors.append(StderrConsoleRenderer())

    processors.append(log_renderer)
    processors.extend(
        [ wrap_hook(h) for h in post_hooks ]
    )

    streams = []
    # we need to use a stream if we are writing to both file and stderr, and both are json
    if stderr_required:
        streams.append(sys.stderr)

    if fpath is not None:
        # TODO handle creating a directory for this log file ?
        # TODO set mode and encoding appropriately
        streams.append(open(fpath, 'a'))

    assert len(streams) != 0, "cannot configure logger for 0 streams"

    stream = streams[0] if len(streams) == 1 else Stream(*streams)
    atexit.register(stream.close)

    # a global level struct log config unless otherwise specified.
    structlog.configure(
        processors=processors,
        context_class=dict,
        logger_factory=LevelLoggerFactory(stream, level=level),
        wrapper_class=BoundLevelLogger,
        cache_logger_on_first_use=True,
    )

    # TODO take care of removing other handlers
    stdlib_root_log = logging.getLogger()
    stdlib_root_log.addHandler(StdlibStructlogHandler())
    stdlib_root_log.setLevel(level)

    _GLOBAL_LOG_CONFIGURED = True