edq.core.log

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

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