Coverage for eminus/logger.py: 95.56%

45 statements  

« prev     ^ index     » next       coverage.py v7.8.2, created at 2025-06-02 10:16 +0000

1# SPDX-FileCopyrightText: 2022 The eminus developers 

2# SPDX-License-Identifier: Apache-2.0 

3"""Logger initialization and configuration.""" 

4 

5import logging 

6import sys 

7 

8 

9class CustomLogger(logging.Logger): 

10 """Custom logger for the usage outside of classes. 

11 

12 This is just a basic logger but with an added verbose property. 

13 

14 Args: 

15 name: Logger name. 

16 """ 

17 

18 def __init__(self, name): 

19 """Initialize the CustomLogger object.""" 

20 super().__init__(name) 

21 

22 @property 

23 def verbose(self): 

24 """Verbosity level.""" 

25 return self._verbose 

26 

27 @verbose.setter 

28 def verbose(self, level): 

29 self._verbose = get_level(level) 

30 self.setLevel(self._verbose) 

31 

32 

33class CustomFormatter(logging.Formatter): 

34 """Custom logger formatter.""" 

35 

36 def format(self, record): 

37 """Use different formatting for different logging levels. 

38 

39 Args: 

40 record: LogRecord object. 

41 

42 Returns: 

43 Formatted log text. 

44 """ 

45 if record.levelno >= logging.WARNING: 

46 # Print the level name for errors and warnings 

47 self._style._fmt = "%(levelname)s: %(msg)s" 

48 else: 

49 # But not for info and debug messages 

50 self._style._fmt = "%(msg)s" 

51 return super().format(record) 

52 

53 

54# The following code is not guarded by a function because it has to be run once the logger is called 

55# to set up the basic logger configuration 

56 

57# Create a base logger that can be used outside of classes 

58logging.setLoggerClass(CustomLogger) 

59#: Global logging object. 

60log = logging.getLogger("eminus") 

61 

62# Basic logger setup 

63__formatter = CustomFormatter() 

64__handler = logging.StreamHandler(sys.stdout) 

65__handler.setFormatter(__formatter) 

66logging.root.addHandler(__handler) 

67 

68 

69def create_logger(obj): 

70 """Create a logger unique to an object. 

71 

72 Args: 

73 obj: Instance of a class. 

74 

75 Returns: 

76 Logger object. 

77 """ 

78 # Use the ID of objects to create a unique logger 

79 # Without this setting the verbosity in one instance would affect other instances 

80 local_log = logging.getLogger(str(id(obj))) 

81 local_log.verbose = log.verbose 

82 return local_log 

83 

84 

85def get_level(verbose): 

86 """Validate logging levels. 

87 

88 Args: 

89 verbose: Level of output. 

90 

91 Returns: 

92 Logging level. 

93 """ 

94 log_levels = { 

95 0: "CRITICAL", 

96 1: "ERROR", 

97 2: "WARNING", 

98 3: "INFO", 

99 4: "DEBUG", 

100 } 

101 # Use the global logging level for None 

102 if verbose is None: 

103 level = log.verbose 

104 # Fall back to DEBUG if the level is not available 

105 elif isinstance(verbose, int): 

106 level = log_levels.get(verbose, "DEBUG") 

107 else: 

108 level = verbose 

109 level = level.upper() 

110 if level not in log_levels.values(): 

111 msg = f"{level} is no recognized logging level." 

112 raise ValueError(msg) 

113 return level 

114 

115 

116def name(newname): 

117 """Add a name to functions without evaluating them for better logging. 

118 

119 Args: 

120 newname: Function name. 

121 

122 Returns: 

123 Decorator. 

124 """ 

125 

126 def decorator(f): 

127 f.__name__ = newname 

128 return f 

129 

130 return decorator