Coverage for src/prosemark/ports/logger.py: 100%

12 statements  

« prev     ^ index     » next       coverage.py v7.8.0, created at 2025-09-24 18:08 +0000

1"""Logger port for structured logging operations.""" 

2 

3from abc import ABC, abstractmethod 

4 

5 

6class Logger(ABC): 

7 """Abstract base class for logging operations. 

8 

9 Defines the contract for logging operations throughout the system following 

10 Python's stdlib logging patterns. This abstract base class enables: 

11 

12 * Consistent logging across all application layers with multiple log levels 

13 * Testable logging behavior through dependency injection and mocking 

14 * Support for different logging targets (console, file, structured logging services) 

15 * Hexagonal architecture compliance by isolating logging concerns 

16 * Observability for debugging, monitoring, and audit trails 

17 

18 The Logger supports four standard log levels with flexible message formatting: 

19 - debug: Detailed diagnostic information for troubleshooting 

20 - info: General operational information and successful operations 

21 - warning: Important events that don't prevent operation but need attention 

22 - error: Error conditions that prevent operations from completing 

23 

24 All methods follow Python's stdlib logging signature patterns, supporting 

25 both simple string messages and formatted messages with positional/keyword arguments. 

26 

27 Examples: 

28 >>> class TestLogger(Logger): 

29 ... def debug(self, msg: object, *args: object, **kwargs: object) -> None: 

30 ... print(f'[DEBUG] {msg}', *args) 

31 ... 

32 ... def info(self, msg: object, *args: object, **kwargs: object) -> None: 

33 ... print(f'[INFO] {msg}', *args) 

34 ... 

35 ... def warning(self, msg: object, *args: object, **kwargs: object) -> None: 

36 ... print(f'[WARNING] {msg}', *args) 

37 ... 

38 ... def error(self, msg: object, *args: object, **kwargs: object) -> None: 

39 ... print(f'[ERROR] {msg}', *args) 

40 >>> logger = TestLogger() 

41 >>> logger.info('Simple message') 

42 [INFO] Simple message 

43 >>> logger.info('Formatted %s with %d args', 'message', 2) 

44 [INFO] Formatted message with 2 args 

45 

46 """ 

47 

48 @abstractmethod 

49 def debug(self, msg: object, *args: object, **kwargs: object) -> None: 

50 """Log detailed diagnostic information for troubleshooting. 

51 

52 Use for verbose diagnostic information that is only of interest when 

53 diagnosing problems. Typically disabled in production environments 

54 to avoid performance impact and log volume. 

55 

56 Args: 

57 msg: The log message or format string 

58 *args: Positional arguments for string formatting 

59 **kwargs: Keyword arguments (implementation-specific, e.g., 'extra' for context) 

60 

61 Examples: 

62 >>> logger.debug('Processing node %s', node_id) 

63 >>> logger.debug('Validation result: %s', result, extra={'node_id': node_id}) 

64 

65 """ 

66 msg = 'Subclasses must implement the debug() method' # pragma: no cover 

67 raise NotImplementedError(msg) # pragma: no cover 

68 

69 @abstractmethod 

70 def info(self, msg: object, *args: object, **kwargs: object) -> None: 

71 """Log general operational information. 

72 

73 Use for tracking normal application flow, successful operations, 

74 and important state changes. This is the standard level for 

75 operational logging and user-facing status updates. 

76 

77 Args: 

78 msg: The log message or format string 

79 *args: Positional arguments for string formatting 

80 **kwargs: Keyword arguments (implementation-specific, e.g., 'extra' for context) 

81 

82 Examples: 

83 >>> logger.info('Created node %s', node_id) 

84 >>> logger.info('Project initialized at %s', project_path) 

85 >>> logger.info('Node added to binder', extra={'node_id': node_id, 'parent_id': parent_id}) 

86 

87 """ 

88 msg = 'Subclasses must implement the info() method' # pragma: no cover 

89 raise NotImplementedError(msg) # pragma: no cover 

90 

91 @abstractmethod 

92 def warning(self, msg: object, *args: object, **kwargs: object) -> None: 

93 """Log warning messages for important events that don't prevent operation. 

94 

95 Use for conditions that are unexpected but don't prevent the operation 

96 from completing successfully. These events often indicate potential 

97 problems or degraded functionality that should be investigated. 

98 

99 Args: 

100 msg: The log message or format string 

101 *args: Positional arguments for string formatting 

102 **kwargs: Keyword arguments (implementation-specific, e.g., 'extra' for context) 

103 

104 Examples: 

105 >>> logger.warning('Node %s not found in binder, adding anyway', node_id) 

106 >>> logger.warning('Large binder detected: %d items', item_count) 

107 >>> logger.warning('Deprecated feature used', extra={'feature': 'old_api'}) 

108 

109 """ 

110 msg = 'Subclasses must implement the warning() method' # pragma: no cover 

111 raise NotImplementedError(msg) # pragma: no cover 

112 

113 @abstractmethod 

114 def error(self, msg: object, *args: object, **kwargs: object) -> None: 

115 """Log error conditions that prevent operations from completing. 

116 

117 Use for error conditions, exceptions, and failures that prevent 

118 the requested operation from completing successfully. These indicate 

119 problems that require immediate attention or user intervention. 

120 

121 Args: 

122 msg: The log message or format string 

123 *args: Positional arguments for string formatting 

124 **kwargs: Keyword arguments (implementation-specific, e.g., 'extra' for context) 

125 

126 Examples: 

127 >>> logger.error('Failed to create node files: %s', error_msg) 

128 >>> logger.error('Binder integrity violation: duplicate node %s', node_id) 

129 >>> logger.error('Node creation failed', extra={'node_id': node_id, 'error': str(exc)}) 

130 

131 """ 

132 msg = 'Subclasses must implement the error() method' # pragma: no cover 

133 raise NotImplementedError(msg) # pragma: no cover 

134 

135 @abstractmethod 

136 def exception(self, msg: object, *args: object, **kwargs: object) -> None: 

137 """Log exception information with error level and traceback. 

138 

139 Use for logging exceptions with their full traceback information. 

140 This is typically used in exception handlers to capture both the 

141 error message and the complete stack trace for debugging. 

142 

143 Args: 

144 msg: The log message or format string 

145 *args: Positional arguments for string formatting 

146 **kwargs: Keyword arguments (implementation-specific, e.g., 'extra' for context) 

147 

148 Examples: 

149 >>> try: 

150 ... risky_operation() 

151 ... except Exception: 

152 ... logger.exception('Operation failed') 

153 

154 """ 

155 msg = 'Subclasses must implement the exception() method' # pragma: no cover 

156 raise NotImplementedError(msg) # pragma: no cover