diff --git a/src/corelibs/logging_handling/log.py b/src/corelibs/logging_handling/log.py index 19f95c4..29d36a4 100644 --- a/src/corelibs/logging_handling/log.py +++ b/src/corelibs/logging_handling/log.py @@ -9,6 +9,7 @@ import logging.handlers import logging from pathlib import Path from typing import Mapping, TextIO, TypedDict, Any, TYPE_CHECKING, cast +from corelibs.logging_handling.logging_level_handling.logging_level import LoggingLevel from corelibs.string_handling.text_colors import Colors if TYPE_CHECKING: @@ -72,8 +73,6 @@ class Log: logger setup """ - # exception level - EXCEPTION: int = 60 # spacer lenght characters and the character SPACER_CHAR: str = '=' SPACER_LENGTH: int = 32 @@ -97,7 +96,7 @@ class Log: log_settings: dict[str, 'str | bool | None | Queue[str]'] | LogSettings | None = None, ): # add new level for EXCEPTION - logging.addLevelName(Log.EXCEPTION, 'EXCEPTION') + logging.addLevelName(LoggingLevel.EXCEPTION.value, 'EXCEPTION') # parse the logging settings self.log_settings = self.__parse_log_settings(log_settings) # if path, set log name with .log @@ -322,7 +321,7 @@ class Log: *args (object): arguments for msg extra: Mapping[str, object] | None: extra arguments for the formatting if needed """ - self.logger.log(Log.EXCEPTION, msg, *args, exc_info=True, extra=extra) + self.logger.log(LoggingLevel.EXCEPTION.value, msg, *args, exc_info=True, extra=extra) @staticmethod def validate_log_level(log_level: Any) -> bool: @@ -335,11 +334,27 @@ class Log: Returns: bool: _description_ """ - if isinstance(log_level, int): - return any(getattr(logging, attr) == log_level for attr in dir(logging)) - elif isinstance(log_level, str): - return isinstance(getattr(logging, log_level.upper(), None), int) - else: + try: + _ = LoggingLevel.from_any(log_level).value + return True + except ValueError: return False + @staticmethod + def get_log_level_int(log_level: Any) -> int: + """ + Return log level as INT + If invalid return set level in default log level + + Arguments: + log_level {Any} -- _description_ + + Returns: + int -- _description_ + """ + try: + return LoggingLevel.from_any(log_level).value + except ValueError: + return LoggingLevel.from_string(Log.DEFAULT_LOG_LEVEL).value + # __END__ diff --git a/src/corelibs/logging_handling/logging_level_handling/__init__.py b/src/corelibs/logging_handling/logging_level_handling/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/corelibs/logging_handling/logging_level_handling/logging_level.py b/src/corelibs/logging_handling/logging_level_handling/logging_level.py new file mode 100644 index 0000000..9090273 --- /dev/null +++ b/src/corelibs/logging_handling/logging_level_handling/logging_level.py @@ -0,0 +1,75 @@ +""" +All logging levels +""" + +import logging +from typing import Any +from enum import Enum + + +class LoggingLevel(Enum): + """ + Log class levels + """ + NOTSET = logging.NOTSET # 0 + DEBUG = logging.DEBUG # 10 + INFO = logging.INFO # 20 + WARNING = logging.WARNING # 30 + ERROR = logging.ERROR # 40 + CRITICAL = logging.CRITICAL # 50 + EXCEPTION = 60 # 60 (manualy set) + # Alternative names + WARN = logging.WARN # 30 (alias for WARNING) + FATAL = logging.FATAL # 50 (alias for CRITICAL) + + # Optional: Add string representation for better readability + @classmethod + def from_string(cls, level_str: str): + """Convert string to LogLevel enum""" + try: + return cls[level_str.upper()] + except KeyError as e: + raise ValueError(f"Invalid log level: {level_str}") from e + + @classmethod + def from_int(cls, level_int: int): + """Convert integer to LogLevel enum""" + try: + return cls(level_int) + except ValueError as e: + raise ValueError(f"Invalid log level: {level_int}") from e + + @classmethod + def from_any(cls, level_any: Any): + """Convert either str or int""" + if isinstance(level_any, int): + return cls.from_int(level_any) + return cls.from_string(level_any) + + def to_logging_level(self): + """Convert to logging module level""" + return self.value + + def to_lower_case(self): + """return loser case""" + return self.name.lower() + + def __str__(self): + return self.name + + def includes(self, level: 'LoggingLevel'): + """ + if given level is included in set level + eg: INFO set, ERROR is included in INFO because INFO level would print ERROR + """ + return self.value <= level.value + + def is_higher_than(self, level: 'LoggingLevel'): + """if given value is higher than set""" + return self.value > level.value + + def is_lower_than(self, level: 'LoggingLevel'): + """if given value is lower than set""" + return self.value > level.value + +# __END__ diff --git a/test-run/logging_handling/log.py b/test-run/logging_handling/log.py index e3e1708..caa0817 100644 --- a/test-run/logging_handling/log.py +++ b/test-run/logging_handling/log.py @@ -6,6 +6,7 @@ Log logging_handling.log testing from pathlib import Path # this is for testing only from corelibs.logging_handling.log import Log +from corelibs.logging_handling.logging_level_handling.logging_level import LoggingLevel def main(): @@ -30,10 +31,33 @@ def main(): log.logger.critical('[NORMAL] Critical test: %s', log.logger.name) log.exception('[NORMAL] Exception test: %s', log.logger.name) - if not Log.validate_log_level('WRONG'): - print("Invalid level") - if Log.validate_log_level('WARNING'): - print("Valid level") + bad_level = 'WRONG' + if not Log.validate_log_level(bad_level): + print(f"Invalid level: {bad_level}") + good_level = 'WARNING' + if Log.validate_log_level(good_level): + print(f"Valid level: {good_level}") + + print(f"ERROR is to_logging_level(): {LoggingLevel.ERROR.to_logging_level()}") + print(f"ERROR is to_lower_case(): {LoggingLevel.ERROR.to_lower_case()}") + print(f"ERROR is: {LoggingLevel.ERROR}") + print(f"ERROR is value: {LoggingLevel.ERROR.value}") + print(f"ERROR is name: {LoggingLevel.ERROR.name}") + print(f"ERROR is from_string(lower): {LoggingLevel.from_string('ERROR')}") + print(f"ERROR is from_string(upper): {LoggingLevel.from_string('ERROR')}") + print(f"ERROR is from_int: {LoggingLevel.from_int(40)}") + print(f"ERROR is from_any(text lower): {LoggingLevel.from_any('ERROR')}") + print(f"ERROR is from_any(text upper): {LoggingLevel.from_any('ERROR')}") + print(f"ERROR is from_any(int): {LoggingLevel.from_any(40)}") + print(f"INFO <= ERROR: {LoggingLevel.INFO.includes(LoggingLevel.ERROR)}") + print(f"INFO > ERROR: {LoggingLevel.INFO.is_higher_than(LoggingLevel.ERROR)}") + print(f"INFO < ERROR: {LoggingLevel.INFO.is_lower_than(LoggingLevel.ERROR)}") + print(f"INFO < ERROR: {LoggingLevel.INFO.is_lower_than(LoggingLevel.ERROR)}") + + try: + print(f"INVALID is A: {LoggingLevel.from_string('INVALID')}") + except ValueError as e: + print(f"* ERROR: {e}") if __name__ == "__main__":