Coverage for /usr/lib/python3.10/site-packages/hyd/backend/util/logger.py: 88%

24 statements  

« prev     ^ index     » next       coverage.py v7.0.3, created at 2023-01-05 15:47 +0000

1""" 

2Module creating a basic logger setup with our preferred configuration style for the hyd package. 

3 

4Author: Jendrik A. Potyka, Fabian A. Preiss. 

5""" 

6import logging 

7from logging import Handler, Logger, LoggerAdapter, StreamHandler, getLogger 

8from typing import TYPE_CHECKING 

9 

10# Necessary until python3.11 (https://github.com/python/typeshed/issues/7855) 

11# in future just use LoggerAdapter[Logger] 

12if TYPE_CHECKING: 

13 _LoggerAdapter = LoggerAdapter[Logger] 

14else: 

15 _LoggerAdapter = LoggerAdapter 

16 

17 

18class HydLogger(_LoggerAdapter): 

19 """ 

20 Custom logger wrapping around the logging.Logger class. 

21 

22 Parameters 

23 ---------- 

24 class_name : str 

25 Instance of the class the logger will be used inside of 

26 level : int 

27 loglevel where `FATAL = 50, ERROR = 40, WARNING = 30, 

28 INFO = 20, DEBUG = 10, NOTSET = 0` 

29 

30 Notes 

31 ----- 

32 It is recommended to declare a separate logger object for every class using logging. 

33 Using the directive ``HydLogger(type(self).__name__, level)`` inside of the ``__init__`` 

34 function and running the ``super().__init__(level)`` from a child of said class provides 

35 a separate logger instance for each class as intended. Given the following definitions: 

36 

37 >>> class Parent: 

38 ... def __init__(self, level: Optional[int] = logging.INFO): 

39 ... self.__log = HydLogger(type(self).__name__, level) 

40 ... 

41 ... @property 

42 ... def log(self): 

43 ... return self.__log 

44 ... 

45 >>> class Child(Parent): 

46 ... def __init__(self, level: Optional[int] = logging.INFO): 

47 ... super().__init__(level) 

48 ... 

49 >>> def main(): 

50 ... parent = Parent() 

51 ... parent.log.info("foo") 

52 ... parent.log.warning("bar") 

53 ... 

54 ... child = Child(logging.WARNING) 

55 ... child.log.warning("foo") 

56 

57 Finally 

58 

59 >>> main() 

60 

61 Generates an output similar to: 

62 

63 :: 

64 

65 [2021-04-20 14:18:48][INFO][Parent][main] foo 

66 [2021-04-20 14:18:48][WARNING][Parent][main] bar 

67 [2021-04-20 14:18:48][WARNING][Child][main] foo 

68 """ 

69 

70 def __init__(self, class_name: str = "Script", level: int = logging.INFO): 

71 

72 self.logger: Logger = getLogger(class_name) 

73 handlers: list[Handler] = self.logger.handlers 

74 self.__console_handler: Handler 

75 

76 if not handlers: 

77 self.logger.setLevel(logging.DEBUG) 

78 

79 log_format = "[%(asctime)s][%(levelname)s][%(name)s][%(funcName)s] %(message)s" 

80 log_formatter = logging.Formatter( 

81 log_format, 

82 "%Y-%m-%d %H:%M:%S", 

83 ) 

84 

85 self.__console_handler = StreamHandler() 

86 self.__console_handler.setFormatter(log_formatter) 

87 self.__console_handler.setLevel(level) 

88 self.logger.addHandler(self.__console_handler) 

89 else: 

90 self.__console_handler = handlers[0] 

91 

92 def setLevel(self, level: int | str = logging.INFO) -> None: 

93 self.__console_handler.setLevel(level) 

94 

95 def process(self, msg, kwargs): 

96 return msg, kwargs