proteusPy.logger_config
This module provides utility functions for configuring and managing loggers within the proteusPy package. The functions are used within the package to convey logging information at a fine-grained level. The functions are completely independent of the application and can be used in any Python project.
Author: Eric G. Suchanek, PhD Last updated 2025-02-19 23:43:45 -egs-
1""" 2This module provides utility functions for configuring and managing loggers 3within the proteusPy package. The functions are used within the package to 4convey logging information at a fine-grained level. The functions are completely 5independent of the application and can be used in any Python project. 6 7Author: Eric G. Suchanek, PhD 8Last updated 2025-02-19 23:43:45 -egs- 9""" 10 11import logging 12from pathlib import Path 13 14DEFAULT_LOG_LEVEL = logging.WARNING 15 16 17def set_logging_level_for_all_handlers(log_level: int): 18 """ 19 Sets the logging level for all handlers of all loggers in the proteusPy package. 20 21 :param log_level: The logging level to set. 22 :type log_level: int 23 """ 24 # Get the root logger 25 root_logger = logging.getLogger() 26 27 # Set the level for the root logger 28 root_logger.setLevel(log_level) 29 30 # Iterate through all loggers 31 for logger_name in logging.Logger.manager.loggerDict: 32 _logger = logging.getLogger(logger_name) 33 34 # Set the level for the logger itself 35 _logger.setLevel(log_level) 36 37 # Iterate through all handlers of the logger 38 for handler in _logger.handlers: 39 handler.setLevel(log_level) 40 41 42def disable_stream_handlers_for_namespace(namespace: str): 43 """ 44 Disables all stream handlers for all loggers under the specified namespace. 45 46 :param namespace: The namespace whose stream handlers should be disabled. 47 :type namespace: str 48 """ 49 # First check the specific logger for the namespace 50 logger = logging.getLogger(namespace) 51 for handler in logger.handlers[ 52 : 53 ]: # Create a copy of the list to safely modify during iteration 54 if isinstance(handler, logging.StreamHandler): 55 logger.removeHandler(handler) 56 57 # Then check all loggers in the manager's dictionary that start with the namespace 58 for logger_name in logging.Logger.manager.loggerDict: 59 if logger_name.startswith(namespace): 60 _logger = logging.getLogger(logger_name) 61 for handler in _logger.handlers[ 62 : 63 ]: # Create a copy of the list to safely modify during iteration 64 if isinstance(handler, logging.StreamHandler): 65 _logger.removeHandler(handler) 66 67 68def configure_master_logger( 69 log_file: str, 70 file_path: str = "~/logs", 71 log_level: int = logging.ERROR, 72 disabled: bool = False, 73) -> None: 74 """ 75 Configures the root logger to write to a specified log file. 76 77 :param log_file: Name of the log file. 78 :type log_file: str 79 :param file_path: Path to the directory where log files will be stored. Defaults to '~/logs'. 80 :type file_path: str 81 :param log_level: The logging level to set. Defaults to logging.ERROR. 82 :type log_level: int 83 :param disabled: If True, the logger will be disabled. Defaults to False. 84 :type disabled: bool 85 """ 86 # Expand user path 87 file_path = Path(file_path).expanduser() 88 89 # Ensure the directory exists 90 file_path.mkdir(parents=True, exist_ok=True) 91 92 # Full path to the log file 93 full_log_file_path = file_path / log_file 94 95 root_logger = logging.getLogger() 96 97 # Set the root logger level to DEBUG to capture all messages 98 root_logger.setLevel(log_level) 99 100 # Remove all existing handlers 101 for handler in root_logger.handlers[:]: 102 root_logger.removeHandler(handler) 103 104 # Create a new FileHandler 105 handler = logging.FileHandler(full_log_file_path, mode="w") 106 107 formatter = logging.Formatter( 108 "proteusPy: %(levelname)s %(asctime)s - %(name)s.%(funcName)s - %(message)s" 109 ) 110 handler.setFormatter(formatter) 111 112 root_logger.addHandler(handler) 113 for handler in root_logger.handlers: 114 handler.setLevel(log_level) 115 116 if disabled: 117 root_logger.disabled = True 118 else: 119 root_logger.disabled = False # Enable the root logger 120 121 return 122 123 124def create_logger( 125 name: str, 126 log_level: int = logging.INFO, 127) -> logging.Logger: 128 """ 129 Returns a logger with the specified name, configured to use both StreamHandler 130 and RotatingFileHandler with the predefined formats. 131 132 :param name: The name of the logger. 133 :type name: str 134 :param log_level: The logging level, defaults to logging.INFO 135 :type log_level: int, optional 136 :return: Configured logger instance. 137 :rtype: logging.Logger 138 """ 139 logger = logging.getLogger(name) 140 logger.setLevel(log_level) 141 142 # Clear all existing handlers 143 if logger.hasHandlers(): 144 logger.handlers.clear() 145 146 # Define formatter 147 formatter = logging.Formatter( 148 "proteusPy: %(levelname)s %(asctime)s - %(name)s.%(funcName)s - %(message)s" 149 ) 150 151 # StreamHandler setup 152 stream_handler = logging.StreamHandler() 153 stream_handler.setLevel(log_level) 154 stream_handler.setFormatter(formatter) 155 logger.addHandler(stream_handler) 156 157 # Allows log messages to propagate to the root logger 158 logger.propagate = True 159 160 return logger 161 162 163def set_logger_level(name, level): 164 """ 165 Sets the logging level for the logger with the specified name. 166 167 :param name: The name of the logger. 168 :type name: str 169 :param level: The logging level to set. Must be one of ["WARNING", "ERROR", "INFO", "DEBUG"]. 170 :type level: str 171 :raises ValueError: If the provided level is not one of the allowed values. 172 """ 173 level_dict = { 174 "WARNING": logging.WARNING, 175 "ERROR": logging.ERROR, 176 "INFO": logging.INFO, 177 "DEBUG": logging.DEBUG, 178 } 179 180 if level not in level_dict: 181 raise ValueError( 182 ( 183 f"--> set_logger_level(): Invalid logging level: {level}." 184 f"Must be one of ['WARNING', 'ERROR', 'INFO', 'DEBUG']" 185 ) 186 ) 187 188 _logger = logging.getLogger(name) 189 _logger.setLevel(level_dict[level]) 190 191 for handler in _logger.handlers: 192 handler.setLevel(level_dict[level]) 193 194 195def toggle_stream_handler(name, enable): 196 """ 197 Enables or disables the StreamHandler for the logger with the specified name. 198 199 :param name: The name of the logger. 200 :type name: str 201 :param enable: If True, enables the StreamHandler; if False, disables it. 202 :type enable: bool 203 """ 204 logger = logging.getLogger(name) 205 stream_handler = None 206 207 # Find the StreamHandler if it exists 208 for handler in logger.handlers: 209 if isinstance(handler, logging.StreamHandler): 210 stream_handler = handler 211 break 212 213 if enable: 214 if stream_handler is None: 215 # Define formatter 216 formatter = logging.Formatter( 217 "stream proteusPy: %(levelname)-7s %(asctime)s - %(name)s.%(funcName)s - %(message)s" 218 ) 219 # Create and add a new StreamHandler 220 stream_handler = logging.StreamHandler() 221 stream_handler.setLevel(logger.level) 222 stream_handler.setFormatter(formatter) 223 logger.addHandler(stream_handler) 224 else: 225 if stream_handler is not None: 226 # Remove the existing StreamHandler 227 logger.removeHandler(stream_handler) 228 229 230def list_all_loggers(): 231 """ 232 Lists all loggers that have been created in the application. 233 234 :return: List of logger names. 235 :rtype: list 236 """ 237 logger_dict = logging.Logger.manager.loggerDict 238 loggers = [ 239 name 240 for name, logger in logger_dict.items() 241 if isinstance(logger, logging.Logger) 242 ] 243 return loggers 244 245 246def list_handlers(name): 247 """ 248 Lists the handlers for the logger with the specified name. 249 250 :param name: The name of the logger. 251 :type name: str 252 :return: List of handler types and their configurations. 253 :rtype: list 254 """ 255 logger = logging.getLogger(name) 256 handlers_info = [] 257 258 for handler in logger.handlers: 259 handler_type = type(handler).__name__ 260 handler_info = { 261 "type": handler_type, 262 "level": logging.getLevelName(handler.level), 263 "formatter": handler.formatter._fmt if handler.formatter else None, 264 } 265 handlers_info.append(handler_info) 266 267 return handlers_info 268 269 270def set_logger_level_for_module(pkg_name, level=""): 271 """ 272 Set the logging level for all loggers within a specified package. 273 274 This function iterates through all registered loggers and sets the logging 275 level for those that belong to the specified package. 276 277 :param pkg_name: The name of the package for which to set the logging level. 278 :type pkg_name: str 279 :param level: The logging level to set (e.g., 'DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'). 280 If not specified, the logging level will not be changed. 281 :type level: str, optional 282 :return: A list of logger names that were found and had their levels set. 283 :rtype: list 284 """ 285 logger_dict = logging.Logger.manager.loggerDict 286 registered_loggers = [ 287 name 288 for name, logger in logger_dict.items() 289 if isinstance(logger, logging.Logger) and name.startswith(pkg_name) 290 ] 291 for logger_name in registered_loggers: 292 logger = logging.getLogger(logger_name) 293 if level: 294 logger.setLevel(level) 295 296 return registered_loggers 297 298 299if __name__ == "__main__": 300 import doctest 301 302 doctest.testmod() 303 304# end of file
18def set_logging_level_for_all_handlers(log_level: int): 19 """ 20 Sets the logging level for all handlers of all loggers in the proteusPy package. 21 22 :param log_level: The logging level to set. 23 :type log_level: int 24 """ 25 # Get the root logger 26 root_logger = logging.getLogger() 27 28 # Set the level for the root logger 29 root_logger.setLevel(log_level) 30 31 # Iterate through all loggers 32 for logger_name in logging.Logger.manager.loggerDict: 33 _logger = logging.getLogger(logger_name) 34 35 # Set the level for the logger itself 36 _logger.setLevel(log_level) 37 38 # Iterate through all handlers of the logger 39 for handler in _logger.handlers: 40 handler.setLevel(log_level)
Sets the logging level for all handlers of all loggers in the proteusPy package.
Parameters
- log_level: The logging level to set.
43def disable_stream_handlers_for_namespace(namespace: str): 44 """ 45 Disables all stream handlers for all loggers under the specified namespace. 46 47 :param namespace: The namespace whose stream handlers should be disabled. 48 :type namespace: str 49 """ 50 # First check the specific logger for the namespace 51 logger = logging.getLogger(namespace) 52 for handler in logger.handlers[ 53 : 54 ]: # Create a copy of the list to safely modify during iteration 55 if isinstance(handler, logging.StreamHandler): 56 logger.removeHandler(handler) 57 58 # Then check all loggers in the manager's dictionary that start with the namespace 59 for logger_name in logging.Logger.manager.loggerDict: 60 if logger_name.startswith(namespace): 61 _logger = logging.getLogger(logger_name) 62 for handler in _logger.handlers[ 63 : 64 ]: # Create a copy of the list to safely modify during iteration 65 if isinstance(handler, logging.StreamHandler): 66 _logger.removeHandler(handler)
Disables all stream handlers for all loggers under the specified namespace.
Parameters
- namespace: The namespace whose stream handlers should be disabled.
69def configure_master_logger( 70 log_file: str, 71 file_path: str = "~/logs", 72 log_level: int = logging.ERROR, 73 disabled: bool = False, 74) -> None: 75 """ 76 Configures the root logger to write to a specified log file. 77 78 :param log_file: Name of the log file. 79 :type log_file: str 80 :param file_path: Path to the directory where log files will be stored. Defaults to '~/logs'. 81 :type file_path: str 82 :param log_level: The logging level to set. Defaults to logging.ERROR. 83 :type log_level: int 84 :param disabled: If True, the logger will be disabled. Defaults to False. 85 :type disabled: bool 86 """ 87 # Expand user path 88 file_path = Path(file_path).expanduser() 89 90 # Ensure the directory exists 91 file_path.mkdir(parents=True, exist_ok=True) 92 93 # Full path to the log file 94 full_log_file_path = file_path / log_file 95 96 root_logger = logging.getLogger() 97 98 # Set the root logger level to DEBUG to capture all messages 99 root_logger.setLevel(log_level) 100 101 # Remove all existing handlers 102 for handler in root_logger.handlers[:]: 103 root_logger.removeHandler(handler) 104 105 # Create a new FileHandler 106 handler = logging.FileHandler(full_log_file_path, mode="w") 107 108 formatter = logging.Formatter( 109 "proteusPy: %(levelname)s %(asctime)s - %(name)s.%(funcName)s - %(message)s" 110 ) 111 handler.setFormatter(formatter) 112 113 root_logger.addHandler(handler) 114 for handler in root_logger.handlers: 115 handler.setLevel(log_level) 116 117 if disabled: 118 root_logger.disabled = True 119 else: 120 root_logger.disabled = False # Enable the root logger 121 122 return
Configures the root logger to write to a specified log file.
Parameters
- log_file: Name of the log file.
- file_path: Path to the directory where log files will be stored. Defaults to '~/logs'.
- log_level: The logging level to set. Defaults to logging.ERROR.
- disabled: If True, the logger will be disabled. Defaults to False.
125def create_logger( 126 name: str, 127 log_level: int = logging.INFO, 128) -> logging.Logger: 129 """ 130 Returns a logger with the specified name, configured to use both StreamHandler 131 and RotatingFileHandler with the predefined formats. 132 133 :param name: The name of the logger. 134 :type name: str 135 :param log_level: The logging level, defaults to logging.INFO 136 :type log_level: int, optional 137 :return: Configured logger instance. 138 :rtype: logging.Logger 139 """ 140 logger = logging.getLogger(name) 141 logger.setLevel(log_level) 142 143 # Clear all existing handlers 144 if logger.hasHandlers(): 145 logger.handlers.clear() 146 147 # Define formatter 148 formatter = logging.Formatter( 149 "proteusPy: %(levelname)s %(asctime)s - %(name)s.%(funcName)s - %(message)s" 150 ) 151 152 # StreamHandler setup 153 stream_handler = logging.StreamHandler() 154 stream_handler.setLevel(log_level) 155 stream_handler.setFormatter(formatter) 156 logger.addHandler(stream_handler) 157 158 # Allows log messages to propagate to the root logger 159 logger.propagate = True 160 161 return logger
Returns a logger with the specified name, configured to use both StreamHandler and RotatingFileHandler with the predefined formats.
Parameters
- name: The name of the logger.
- log_level: The logging level, defaults to logging.INFO
Returns
Configured logger instance.
164def set_logger_level(name, level): 165 """ 166 Sets the logging level for the logger with the specified name. 167 168 :param name: The name of the logger. 169 :type name: str 170 :param level: The logging level to set. Must be one of ["WARNING", "ERROR", "INFO", "DEBUG"]. 171 :type level: str 172 :raises ValueError: If the provided level is not one of the allowed values. 173 """ 174 level_dict = { 175 "WARNING": logging.WARNING, 176 "ERROR": logging.ERROR, 177 "INFO": logging.INFO, 178 "DEBUG": logging.DEBUG, 179 } 180 181 if level not in level_dict: 182 raise ValueError( 183 ( 184 f"--> set_logger_level(): Invalid logging level: {level}." 185 f"Must be one of ['WARNING', 'ERROR', 'INFO', 'DEBUG']" 186 ) 187 ) 188 189 _logger = logging.getLogger(name) 190 _logger.setLevel(level_dict[level]) 191 192 for handler in _logger.handlers: 193 handler.setLevel(level_dict[level])
Sets the logging level for the logger with the specified name.
Parameters
- name: The name of the logger.
- level: The logging level to set. Must be one of ["WARNING", "ERROR", "INFO", "DEBUG"].
Raises
- ValueError: If the provided level is not one of the allowed values.
196def toggle_stream_handler(name, enable): 197 """ 198 Enables or disables the StreamHandler for the logger with the specified name. 199 200 :param name: The name of the logger. 201 :type name: str 202 :param enable: If True, enables the StreamHandler; if False, disables it. 203 :type enable: bool 204 """ 205 logger = logging.getLogger(name) 206 stream_handler = None 207 208 # Find the StreamHandler if it exists 209 for handler in logger.handlers: 210 if isinstance(handler, logging.StreamHandler): 211 stream_handler = handler 212 break 213 214 if enable: 215 if stream_handler is None: 216 # Define formatter 217 formatter = logging.Formatter( 218 "stream proteusPy: %(levelname)-7s %(asctime)s - %(name)s.%(funcName)s - %(message)s" 219 ) 220 # Create and add a new StreamHandler 221 stream_handler = logging.StreamHandler() 222 stream_handler.setLevel(logger.level) 223 stream_handler.setFormatter(formatter) 224 logger.addHandler(stream_handler) 225 else: 226 if stream_handler is not None: 227 # Remove the existing StreamHandler 228 logger.removeHandler(stream_handler)
Enables or disables the StreamHandler for the logger with the specified name.
Parameters
- name: The name of the logger.
- enable: If True, enables the StreamHandler; if False, disables it.
231def list_all_loggers(): 232 """ 233 Lists all loggers that have been created in the application. 234 235 :return: List of logger names. 236 :rtype: list 237 """ 238 logger_dict = logging.Logger.manager.loggerDict 239 loggers = [ 240 name 241 for name, logger in logger_dict.items() 242 if isinstance(logger, logging.Logger) 243 ] 244 return loggers
Lists all loggers that have been created in the application.
Returns
List of logger names.
247def list_handlers(name): 248 """ 249 Lists the handlers for the logger with the specified name. 250 251 :param name: The name of the logger. 252 :type name: str 253 :return: List of handler types and their configurations. 254 :rtype: list 255 """ 256 logger = logging.getLogger(name) 257 handlers_info = [] 258 259 for handler in logger.handlers: 260 handler_type = type(handler).__name__ 261 handler_info = { 262 "type": handler_type, 263 "level": logging.getLevelName(handler.level), 264 "formatter": handler.formatter._fmt if handler.formatter else None, 265 } 266 handlers_info.append(handler_info) 267 268 return handlers_info
Lists the handlers for the logger with the specified name.
Parameters
- name: The name of the logger.
Returns
List of handler types and their configurations.
271def set_logger_level_for_module(pkg_name, level=""): 272 """ 273 Set the logging level for all loggers within a specified package. 274 275 This function iterates through all registered loggers and sets the logging 276 level for those that belong to the specified package. 277 278 :param pkg_name: The name of the package for which to set the logging level. 279 :type pkg_name: str 280 :param level: The logging level to set (e.g., 'DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'). 281 If not specified, the logging level will not be changed. 282 :type level: str, optional 283 :return: A list of logger names that were found and had their levels set. 284 :rtype: list 285 """ 286 logger_dict = logging.Logger.manager.loggerDict 287 registered_loggers = [ 288 name 289 for name, logger in logger_dict.items() 290 if isinstance(logger, logging.Logger) and name.startswith(pkg_name) 291 ] 292 for logger_name in registered_loggers: 293 logger = logging.getLogger(logger_name) 294 if level: 295 logger.setLevel(level) 296 297 return registered_loggers
Set the logging level for all loggers within a specified package.
This function iterates through all registered loggers and sets the logging level for those that belong to the specified package.
Parameters
- pkg_name: The name of the package for which to set the logging level.
- level: The logging level to set (e.g., 'DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'). If not specified, the logging level will not be changed.
Returns
A list of logger names that were found and had their levels set.