Coverage for eminus/logger.py: 95.65%

46 statements  

« prev     ^ index     » next       coverage.py v7.6.4, created at 2024-11-08 12:59 +0000

1# SPDX-FileCopyrightText: 2021 The eminus developers 

2# SPDX-License-Identifier: Apache-2.0 

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

4 

5import logging 

6import numbers 

7import sys 

8 

9 

10class CustomLogger(logging.Logger): 

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

12 

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

14 

15 Args: 

16 name: Logger name. 

17 """ 

18 

19 def __init__(self, name): 

20 """Initialize the CustomLogger object.""" 

21 super().__init__(name) 

22 

23 @property 

24 def verbose(self): 

25 """Verbosity level.""" 

26 return self._verbose 

27 

28 @verbose.setter 

29 def verbose(self, level): 

30 self._verbose = get_level(level) 

31 self.setLevel(self._verbose) 

32 

33 

34class CustomFormatter(logging.Formatter): 

35 """Custom logger formatter.""" 

36 

37 def format(self, record): 

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

39 

40 Args: 

41 record: LogRecord object. 

42 

43 Returns: 

44 Formatted log text. 

45 """ 

46 if record.levelno >= logging.WARNING: 

47 # Print the level name for errors and warnings 

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

49 else: 

50 # But not for info and debug messages 

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

52 return super().format(record) 

53 

54 

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

56# to set up the basic logger configuration 

57 

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

59logging.setLoggerClass(CustomLogger) 

60#: Global logging object. 

61log = logging.getLogger("eminus") 

62 

63# Basic logger setup 

64__formatter = CustomFormatter() 

65__handler = logging.StreamHandler(sys.stdout) 

66__handler.setFormatter(__formatter) 

67logging.root.addHandler(__handler) 

68 

69 

70def create_logger(obj): 

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

72 

73 Args: 

74 obj: Instance of a class. 

75 

76 Returns: 

77 Logger object. 

78 """ 

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

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

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

82 local_log.verbose = log.verbose 

83 return local_log 

84 

85 

86def get_level(verbose): 

87 """Validate logging levels. 

88 

89 Args: 

90 verbose: Level of output. 

91 

92 Returns: 

93 Logging level. 

94 """ 

95 log_levels = { 

96 0: "CRITICAL", 

97 1: "ERROR", 

98 2: "WARNING", 

99 3: "INFO", 

100 4: "DEBUG", 

101 } 

102 # Use the global logging level for None 

103 if verbose is None: 

104 level = log.verbose 

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

106 elif isinstance(verbose, numbers.Number): 

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

108 else: 

109 level = verbose 

110 level = level.upper() 

111 if level not in log_levels.values(): 

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

113 raise ValueError(msg) 

114 return level 

115 

116 

117def name(newname): 

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

119 

120 Args: 

121 newname: Function name. 

122 

123 Returns: 

124 Decorator. 

125 """ 

126 

127 def decorator(f): 

128 f.__name__ = newname 

129 return f 

130 

131 return decorator