#    Licensed under the Apache License, Version 2.0 (the "License"); you may
#    not use this file except in compliance with the License. You may obtain
#    a copy of the License at
#
#         http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
#    License for the specific language governing permissions and limitations
#    under the License.

from oslo_config import cfg

from oslo_log import versionutils

_DEFAULT_LOG_DATE_FORMAT = "%Y-%m-%d %H:%M:%S"

DEFAULT_LOG_LEVELS = [
    'amqp=WARN',
    'boto=WARN',
    'sqlalchemy=WARN',
    'suds=INFO',
    'oslo.messaging=INFO',
    'oslo_messaging=INFO',
    'iso8601=WARN',
    'requests.packages.urllib3.connectionpool=WARN',
    'urllib3.connectionpool=WARN',
    'websocket=WARN',
    'requests.packages.urllib3.util.retry=WARN',
    'urllib3.util.retry=WARN',
    'keystonemiddleware=WARN',
    'routes.middleware=WARN',
    'stevedore=WARN',
    'taskflow=WARN',
    'keystoneauth=WARN',
    'oslo.cache=INFO',
    'oslo_policy=INFO',
    'dogpile.core.dogpile=INFO',
]

_IGNORE_MESSAGE = "This option is ignored if log_config_append is set."

common_cli_opts = [
    cfg.BoolOpt(
        'debug',
        short='d',
        default=False,
        mutable=True,
        help='If set to true, the logging level will be set to '
        'DEBUG instead of the default INFO level.',
    ),
]

logging_cli_opts = [
    cfg.StrOpt(
        'log-config-append',
        metavar='PATH',
        deprecated_name='log-config',
        mutable=True,
        help='The name of a logging configuration file. This file '
        'is appended to any existing logging configuration '
        'files. For details about logging configuration files, '
        'see the Python logging module documentation. Note that '
        'when logging configuration files are used then all '
        'logging configuration is set in the configuration file '
        'and other logging configuration options are ignored '
        '(for example, log-date-format).',
    ),
    cfg.StrOpt(
        'log-date-format',
        default=_DEFAULT_LOG_DATE_FORMAT,
        metavar='DATE_FORMAT',
        help='Defines the format string for %%(asctime)s in log '
        'records. Default: %(default)s . ' + _IGNORE_MESSAGE,
    ),
    cfg.StrOpt(
        'log-file',
        metavar='PATH',
        deprecated_name='logfile',
        help='(Optional) Name of log file to send logging output to. '
        'If no default is set, logging will go to stderr as '
        'defined by use_stderr. ' + _IGNORE_MESSAGE,
    ),
    cfg.StrOpt(
        'log-dir',
        deprecated_name='logdir',
        help='(Optional) The base directory used for relative log_file '
        ' paths. ' + _IGNORE_MESSAGE,
    ),
    cfg.BoolOpt(
        'use-syslog',
        default=False,
        help='Use syslog for logging. '
        'Existing syslog format is DEPRECATED '
        'and will be changed later to honor RFC5424. ' + _IGNORE_MESSAGE,
    ),
    cfg.BoolOpt(
        'use-journal',
        default=False,
        help='Enable journald for logging. '
        'If running in a systemd environment you may wish '
        'to enable journal support. Doing so will use the '
        'journal native protocol which includes structured '
        'metadata in addition to log messages.' + _IGNORE_MESSAGE,
    ),
    cfg.StrOpt(
        'syslog-log-facility',
        default='LOG_USER',
        help='Syslog facility to receive log lines. ' + _IGNORE_MESSAGE,
    ),
    cfg.BoolOpt(
        'use-json',
        default=False,
        help='Use JSON formatting for logging. ' + _IGNORE_MESSAGE,
    ),
]

generic_log_opts = [
    cfg.BoolOpt(
        'use_stderr',
        default=False,
        help='Log output to standard error. ' + _IGNORE_MESSAGE,
    ),
    cfg.BoolOpt(
        'log_color',
        default=False,
        help='(Optional) Set the \'color\' key according to log '
        'levels. This option takes effect only when logging '
        'to stderr or stdout is used. ' + _IGNORE_MESSAGE,
    ),
    cfg.IntOpt(
        'log_rotate_interval',
        default=1,
        help='The amount of time before the log files are rotated. '
        'This option is ignored unless log_rotation_type is set '
        'to "interval".',
    ),
    cfg.StrOpt(
        'log_rotate_interval_type',
        choices=['Seconds', 'Minutes', 'Hours', 'Days', 'Weekday', 'Midnight'],
        ignore_case=True,
        default='days',
        help='Rotation interval type. The time of the last file '
        'change (or the time when the service was started) is '
        'used when scheduling the next rotation.',
    ),
    cfg.IntOpt(
        'max_logfile_count',
        default=30,
        help='Maximum number of rotated log files.',
    ),
    cfg.IntOpt(
        'max_logfile_size_mb',
        default=200,
        help='Log file maximum size in MB. This option is ignored if '
        '"log_rotation_type" is not set to "size".',
    ),
    cfg.StrOpt(
        'log_rotation_type',
        default='none',
        choices=[
            ('interval', 'Rotate logs at predefined time intervals.'),
            ('size', 'Rotate logs once they reach a predefined size.'),
            ('none', 'Do not rotate log files.'),
        ],
        ignore_case=True,
        help='Log rotation type.',
    ),
]

log_opts = [
    cfg.StrOpt(
        'logging_context_format_string',
        default='%(asctime)s.%(msecs)03d %(process)d %(levelname)s '
        '%(name)s [%(global_request_id)s %(request_id)s '
        '%(user_identity)s] %(instance)s%(message)s',
        help='Format string to use for log messages with context. '
        'Used by oslo_log.formatters.ContextFormatter',
    ),
    cfg.StrOpt(
        'logging_default_format_string',
        default='%(asctime)s.%(msecs)03d %(process)d %(levelname)s '
        '%(name)s [-] %(instance)s%(message)s',
        help='Format string to use for log messages when context is '
        'undefined. '
        'Used by oslo_log.formatters.ContextFormatter',
    ),
    cfg.StrOpt(
        'logging_debug_format_suffix',
        default='%(funcName)s %(pathname)s:%(lineno)d',
        help='Additional data to append to log message when logging '
        'level for the message is DEBUG. '
        'Used by oslo_log.formatters.ContextFormatter',
    ),
    cfg.StrOpt(
        'logging_exception_prefix',
        default='%(asctime)s.%(msecs)03d %(process)d ERROR %(name)s '
        '%(instance)s',
        help='Prefix each line of exception output with this format. '
        'Used by oslo_log.formatters.ContextFormatter',
    ),
    cfg.StrOpt(
        'logging_user_identity_format',
        default='%(user)s %(project)s %(domain)s %(system_scope)s '
        '%(user_domain)s %(project_domain)s',
        help='Defines the format string for %(user_identity)s that '
        'is used in logging_context_format_string. '
        'Used by oslo_log.formatters.ContextFormatter',
    ),
    cfg.ListOpt(
        'default_log_levels',
        default=DEFAULT_LOG_LEVELS,
        help='List of package logging levels in logger=LEVEL pairs. '
        + _IGNORE_MESSAGE,
    ),
    cfg.BoolOpt(
        'publish_errors',
        default=False,
        help='Enables or disables publication of error events.',
    ),
    # NOTE(mikal): there are two options here because sometimes we are handed
    # a full instance (and could include more information), and other times we
    # are just handed a UUID for the instance.
    cfg.StrOpt(
        'instance_format',
        default='[instance: %(uuid)s] ',
        help='The format for an instance that is passed with the log message.',
    ),
    cfg.StrOpt(
        'instance_uuid_format',
        default='[instance: %(uuid)s] ',
        help='The format for an instance UUID that is passed with the '
        'log message.',
    ),
    cfg.IntOpt(
        'rate_limit_interval',
        default=0,
        help='Interval, number of seconds, of log rate limiting.',
    ),
    cfg.IntOpt(
        'rate_limit_burst',
        default=0,
        help='Maximum number of logged messages per rate_limit_interval.',
    ),
    cfg.StrOpt(
        'rate_limit_except_level',
        default='CRITICAL',
        choices=['CRITICAL', 'ERROR', 'INFO', 'WARNING', 'DEBUG', ''],
        help='Log level name used by rate limiting. Logs with level '
        'greater or equal to rate_limit_except_level are not '
        'filtered. An empty string means that all levels are '
        'filtered.',
    ),
]


def list_opts() -> list[tuple[str | None, list[cfg.Opt]]]:
    """Returns a list of oslo.config options available in the library.

    The returned list includes all oslo.config options which may be registered
    at runtime by the library.

    Each element of the list is a tuple. The first element is the name of the
    group under which the list of elements in the second element will be
    registered. A group name of None corresponds to the [DEFAULT] group in
    config files.

    The purpose of this is to allow tools like the Oslo sample config file
    generator (oslo-config-generator) to discover the options exposed to users
    by this library.

    :returns: a list of (group_name, opts) tuples
    """
    return [
        (
            None,
            [
                *common_cli_opts,
                *logging_cli_opts,
                *generic_log_opts,
                *log_opts,
                *versionutils.deprecated_opts,
            ],
        )
    ]
