From 424c91945ae157dc748a345c6c4ee141f8f7d024 Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Thu, 10 Jul 2025 13:43:35 +0900 Subject: [PATCH] Update log and add Log Enum for Logging levels, add log level as in from Log Add all logging levels as Enum class type (same as for the PHP CoreLibs Logging) Add a new method to get the log level as int with fallback via the LoggingLevel Enum --- src/corelibs/logging_handling/log.py | 33 +++++--- .../logging_level_handling/__init__.py | 0 .../logging_level_handling/logging_level.py | 75 +++++++++++++++++++ test-run/logging_handling/log.py | 32 +++++++- 4 files changed, 127 insertions(+), 13 deletions(-) create mode 100644 src/corelibs/logging_handling/logging_level_handling/__init__.py create mode 100644 src/corelibs/logging_handling/logging_level_handling/logging_level.py 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__":