edq.core.log

  1import argparse
  2import logging
  3import typing
  4
  5DEFAULT_LOGGING_LEVEL: str = logging.getLevelName(logging.INFO)
  6DEFAULT_LOGGING_FORMAT: str = '%(asctime)s [%(levelname)-8s] - %(filename)s:%(lineno)s -- %(message)s'
  7
  8LEVELS: typing.List[str] = [
  9    'TRACE',
 10    logging.getLevelName(logging.DEBUG),
 11    logging.getLevelName(logging.INFO),
 12    logging.getLevelName(logging.WARNING),
 13    logging.getLevelName(logging.ERROR),
 14    logging.getLevelName(logging.CRITICAL),
 15]
 16
 17def init(level: str = DEFAULT_LOGGING_LEVEL, log_format: str = DEFAULT_LOGGING_FORMAT,
 18        warn_loggers: typing.Union[typing.List[str], None] = None,
 19        **kwargs: typing.Any) -> None:
 20    """
 21    Initialize or re-initialize the logging infrastructure.
 22    The list of warning loggers is a list of identifiers for loggers (usually third-party) to move up to warning on init.
 23    """
 24
 25    # Add trace.
 26    _add_logging_level('TRACE', logging.DEBUG - 5)
 27
 28    logging.basicConfig(level = level, format = log_format, force = True)
 29
 30    if (warn_loggers is not None):
 31        for warn_logger in warn_loggers:
 32            logging.getLogger(warn_logger).setLevel(logging.WARNING)
 33
 34    logging.trace("Logging initialized with level '%s'.", level)  # type: ignore[attr-defined]
 35
 36def set_cli_args(parser: argparse.ArgumentParser, extra_state: typing.Dict[str, typing.Any]) -> None:
 37    """
 38    Set common CLI arguments.
 39    This is a sibling to init_from_args(), as the arguments set here can be interpreted there.
 40    """
 41
 42    parser.add_argument('--log-level', dest = 'log_level',
 43            action = 'store', type = str, default = logging.getLevelName(logging.INFO),
 44            choices = LEVELS,
 45            help = 'Set the logging level (default: %(default)s).')
 46
 47    parser.add_argument('--quiet', dest = 'quiet',
 48            action = 'store_true', default = False,
 49            help = 'Set the logging level to warning (overrides --log-level) (default: %(default)s).')
 50
 51    parser.add_argument('--debug', dest = 'debug',
 52            action = 'store_true', default = False,
 53            help = 'Set the logging level to debug (overrides --log-level and --quiet) (default: %(default)s).')
 54
 55def init_from_args(
 56        parser: argparse.ArgumentParser,
 57        args: argparse.Namespace,
 58        extra_state: typing.Dict[str, typing.Any]) -> None:
 59    """
 60    Take in args from a parser that was passed to set_cli_args(),
 61    and call init() with the appropriate arguments.
 62    """
 63
 64    level = args.log_level
 65
 66    if (args.quiet):
 67        level = logging.getLevelName(logging.WARNING)
 68
 69    if (args.debug):
 70        level = logging.getLevelName(logging.DEBUG)
 71
 72    init(level)
 73
 74def _add_logging_level(level_name: str, level_number: int, method_name: typing.Union[str, None] = None) -> None:
 75    """
 76    Add a new logging level.
 77
 78    See https://stackoverflow.com/questions/2183233/how-to-add-a-custom-loglevel-to-pythons-logging-facility/35804945#35804945 .
 79    """
 80
 81    if (method_name is None):
 82        method_name = level_name.lower()
 83
 84    # Level has already been defined.
 85    if hasattr(logging, level_name):
 86        return
 87
 88    def log_for_level(self: typing.Any, message: str, *args: typing.Any, **kwargs: typing.Any) -> None:
 89        if self.isEnabledFor(level_number):
 90            self._log(level_number, message, args, **kwargs)
 91
 92    def log_to_root(message: str, *args: typing.Any, **kwargs: typing.Any) -> None:
 93        logging.log(level_number, message, *args, **kwargs)
 94
 95    logging.addLevelName(level_number, level_name)
 96    setattr(logging, level_name, level_number)
 97    setattr(logging.getLoggerClass(), method_name, log_for_level)
 98    setattr(logging, method_name, log_to_root)
 99
100# Load the default logging when this module is loaded.
101init()
DEFAULT_LOGGING_LEVEL: str = 'INFO'
DEFAULT_LOGGING_FORMAT: str = '%(asctime)s [%(levelname)-8s] - %(filename)s:%(lineno)s -- %(message)s'
LEVELS: List[str] = ['TRACE', 'DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL']
def init( level: str = 'INFO', log_format: str = '%(asctime)s [%(levelname)-8s] - %(filename)s:%(lineno)s -- %(message)s', warn_loggers: Optional[List[str]] = None, **kwargs: Any) -> None:
18def init(level: str = DEFAULT_LOGGING_LEVEL, log_format: str = DEFAULT_LOGGING_FORMAT,
19        warn_loggers: typing.Union[typing.List[str], None] = None,
20        **kwargs: typing.Any) -> None:
21    """
22    Initialize or re-initialize the logging infrastructure.
23    The list of warning loggers is a list of identifiers for loggers (usually third-party) to move up to warning on init.
24    """
25
26    # Add trace.
27    _add_logging_level('TRACE', logging.DEBUG - 5)
28
29    logging.basicConfig(level = level, format = log_format, force = True)
30
31    if (warn_loggers is not None):
32        for warn_logger in warn_loggers:
33            logging.getLogger(warn_logger).setLevel(logging.WARNING)
34
35    logging.trace("Logging initialized with level '%s'.", level)  # type: ignore[attr-defined]

Initialize or re-initialize the logging infrastructure. The list of warning loggers is a list of identifiers for loggers (usually third-party) to move up to warning on init.

def set_cli_args(parser: argparse.ArgumentParser, extra_state: Dict[str, Any]) -> None:
37def set_cli_args(parser: argparse.ArgumentParser, extra_state: typing.Dict[str, typing.Any]) -> None:
38    """
39    Set common CLI arguments.
40    This is a sibling to init_from_args(), as the arguments set here can be interpreted there.
41    """
42
43    parser.add_argument('--log-level', dest = 'log_level',
44            action = 'store', type = str, default = logging.getLevelName(logging.INFO),
45            choices = LEVELS,
46            help = 'Set the logging level (default: %(default)s).')
47
48    parser.add_argument('--quiet', dest = 'quiet',
49            action = 'store_true', default = False,
50            help = 'Set the logging level to warning (overrides --log-level) (default: %(default)s).')
51
52    parser.add_argument('--debug', dest = 'debug',
53            action = 'store_true', default = False,
54            help = 'Set the logging level to debug (overrides --log-level and --quiet) (default: %(default)s).')

Set common CLI arguments. This is a sibling to init_from_args(), as the arguments set here can be interpreted there.

def init_from_args( parser: argparse.ArgumentParser, args: argparse.Namespace, extra_state: Dict[str, Any]) -> None:
56def init_from_args(
57        parser: argparse.ArgumentParser,
58        args: argparse.Namespace,
59        extra_state: typing.Dict[str, typing.Any]) -> None:
60    """
61    Take in args from a parser that was passed to set_cli_args(),
62    and call init() with the appropriate arguments.
63    """
64
65    level = args.log_level
66
67    if (args.quiet):
68        level = logging.getLevelName(logging.WARNING)
69
70    if (args.debug):
71        level = logging.getLevelName(logging.DEBUG)
72
73    init(level)

Take in args from a parser that was passed to set_cli_args(), and call init() with the appropriate arguments.