import logging as _logging from syslog import LOG_ALERT, LOG_CRIT, LOG_ERR, LOG_WARNING, LOG_INFO, LOG_DEBUG from typing import Union LevelT = Union[str, int] def check_level(level: LevelT) -> int: """ Custom implementation of the logging module's _checkLevel(...) function. Returns the numeric representation of a log level. Args: level: Either a string such as 'DEBUG' or 'WARNING' or an integer. Returns: If an integer is passed, it is returned unchanged; if a string is passed, the corresponding numeric log level is returned. Raises: TypeError if something other than a string or an integer is passed. ValueError if the string has no corresponding level in the logging module. """ if isinstance(level, int): return level if str(level) != level: raise TypeError(f"Level not an integer or a valid string: {level}") output = getattr(_logging, level) if output is None: raise ValueError(f"Unknown level: {level}") return output def py_to_sys_lvl(level_num: int) -> int: """ Maps a (numeric) log level as defined in Python stdlib logging module to the syslog module's log levels. The output number corresponds to a syslog `PRI` without the enclosing angle brackets. Even though there are more levels available to syslog, the `EMERG` (num. 0) and `NOTICE` (num. 5) levels are omitted here, i.e. it goes straight from `INFO` (num. 6) to `WARNING` (num. 4) because there is no equivalent in the Python logging module to `NOTICE`, and `EMERG` is unnecessary because no Python script should be able to cause such severe problems. """ if level_num <= _logging.DEBUG: return LOG_DEBUG if level_num <= _logging.INFO: return LOG_INFO if level_num <= _logging.WARNING: return LOG_WARNING if level_num <= _logging.ERROR: return LOG_ERR if level_num <= _logging.CRITICAL: return LOG_CRIT return LOG_ALERT