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
« prev ^ index » next coverage.py v7.8.0, created at 2025-09-24 18:08 +0000
1"""Logger port for structured logging operations."""
3from abc import ABC, abstractmethod
6class Logger(ABC):
7 """Abstract base class for logging operations.
9 Defines the contract for logging operations throughout the system following
10 Python's stdlib logging patterns. This abstract base class enables:
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
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
24 All methods follow Python's stdlib logging signature patterns, supporting
25 both simple string messages and formatted messages with positional/keyword arguments.
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
46 """
48 @abstractmethod
49 def debug(self, msg: object, *args: object, **kwargs: object) -> None:
50 """Log detailed diagnostic information for troubleshooting.
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.
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)
61 Examples:
62 >>> logger.debug('Processing node %s', node_id)
63 >>> logger.debug('Validation result: %s', result, extra={'node_id': node_id})
65 """
66 msg = 'Subclasses must implement the debug() method' # pragma: no cover
67 raise NotImplementedError(msg) # pragma: no cover
69 @abstractmethod
70 def info(self, msg: object, *args: object, **kwargs: object) -> None:
71 """Log general operational information.
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.
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)
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})
87 """
88 msg = 'Subclasses must implement the info() method' # pragma: no cover
89 raise NotImplementedError(msg) # pragma: no cover
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.
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.
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)
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'})
109 """
110 msg = 'Subclasses must implement the warning() method' # pragma: no cover
111 raise NotImplementedError(msg) # pragma: no cover
113 @abstractmethod
114 def error(self, msg: object, *args: object, **kwargs: object) -> None:
115 """Log error conditions that prevent operations from completing.
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.
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)
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)})
131 """
132 msg = 'Subclasses must implement the error() method' # pragma: no cover
133 raise NotImplementedError(msg) # pragma: no cover
135 @abstractmethod
136 def exception(self, msg: object, *args: object, **kwargs: object) -> None:
137 """Log exception information with error level and traceback.
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.
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)
148 Examples:
149 >>> try:
150 ... risky_operation()
151 ... except Exception:
152 ... logger.exception('Operation failed')
154 """
155 msg = 'Subclasses must implement the exception() method' # pragma: no cover
156 raise NotImplementedError(msg) # pragma: no cover