def setup_logger(config):
    """Configure the logging environment.

    Notice:
        By default logging will go to STDOUT and messages for unhandled
        exceptions or crashes will go to STDERR. If log_file and/or log_server
        is set then we don't log to STDOUT. Messages for unhandled exceptions
        or crashes can only go to either STDERR or to stderr_file or to
        stderr_log_server.

    Arguments:
        config (obj): A configparser object which holds our configuration.

    Returns:
        A logger with all possible handlers configured.

    """
    logger = logging.getLogger(PROGRAM_NAME)
    num_level = getattr(
        logging,
        config.get('daemon', 'loglevel').upper(),  # pylint: disable=no-member
        None
    )
    logger.setLevel(num_level)
    lengths = []
    for section in config:
        lengths.append(len(section))

    width = sorted(lengths)[-1] + 1

    def log_format():
        """Produce a log format line."""
        supported_keys = [
            'asctime',
            'levelname',
            'process',
            # 'funcName',
            # 'lineno',
            'threadName',
            'message',
        ]

        return ' '.join(['%({0:s})'.format(i) for i in supported_keys])

    custom_format = log_format()
    json_formatter = CustomJsonFormatter(custom_format,
                                         prefix=PROGRAM_NAME + ': ')
    formatter = logging.Formatter(
        '%(asctime)s {program}[%(process)d] %(levelname)-8s '
        '%(threadName)-{width}s %(message)s'
        .format(program=PROGRAM_NAME, width=width)
    )

    # Register logging handlers based on configuration.
    if config.has_option('daemon', 'log_file'):
        file_handler = logging.handlers.RotatingFileHandler(
            config.get('daemon', 'log_file'),
            maxBytes=config.getint('daemon', 'log_maxbytes'),
            backupCount=config.getint('daemon', 'log_backups')
        )

        if config.getboolean('daemon', 'json_log_file'):
            file_handler.setFormatter(json_formatter)
        else:
            file_handler.setFormatter(formatter)
        logger.addHandler(file_handler)

    if config.has_option('daemon', 'log_server'):
        udp_handler = logging.handlers.SysLogHandler(
            (
                config.get('daemon', 'log_server'),
                config.getint('daemon', 'log_server_port')
            )
        )

        if config.getboolean('daemon', 'json_log_server'):
            udp_handler.setFormatter(json_formatter)
        else:
            udp_handler.setFormatter(formatter)
        logger.addHandler(udp_handler)

    # Log to STDOUT if and only if log_file and log_server aren't enabled
    if (not config.has_option('daemon', 'log_file')
            and not config.has_option('daemon', 'log_server')):
        stream_handler = logging.StreamHandler()
        if config.getboolean('daemon', 'json_stdout'):
            stream_handler.setFormatter(json_formatter)
        else:
            stream_handler.setFormatter(formatter)
        logger.addHandler(stream_handler)

    # We can redirect STDERR only to one destination.
    if config.has_option('daemon', 'stderr_file'):
        sys.stderr = CustomRotatingFileLogger(
            filepath=config.get('daemon', 'stderr_file'),
            maxbytes=config.getint('daemon', 'log_maxbytes'),
            backupcount=config.getint('daemon', 'log_backups')
        )
    elif (config.has_option('daemon', 'stderr_log_server')
          and not config.has_option('daemon', 'stderr_file')):
        sys.stderr = CustomUdpLogger(
            server=config.get('daemon', 'log_server'),
            port=config.getint('daemon', 'log_server_port')
        )
    else:
        print('messages for unhandled exceptions will go to STDERR')

    return logger
Configure the logging environment.

    Notice:
        By default logging will go to STDOUT and messages for unhandled
        exceptions or crashes will go to STDERR. If log_file and/or log_server
        is set then we don't log to STDOUT. Messages for unhandled exceptions
        or crashes can only go to either STDERR or to stderr_file or to
        stderr_log_server.

    Arguments:
        config (obj): A configparser object which holds our configuration.

    Returns:
        A logger with all possible handlers configured.