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 update: 2025-04-25 19:10:55
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 update: 2025-04-25 19:10:55 9""" 10 11import logging 12from pathlib import Path 13 14from rich.logging import RichHandler 15 16DEFAULT_LOG_LEVEL = logging.WARNING 17 18 19def set_logging_level_for_all_handlers(log_level: int): 20 """ 21 Sets the logging level for all handlers of all loggers in the proteusPy package. 22 23 :param log_level: The logging level to set. 24 :type log_level: int 25 """ 26 root_logger = logging.getLogger() 27 root_logger.setLevel(log_level) 28 29 for logger_name in logging.Logger.manager.loggerDict: 30 _logger = logging.getLogger(logger_name) 31 _logger.setLevel(log_level) 32 for handler in _logger.handlers: 33 handler.setLevel(log_level) 34 35 36def disable_stream_handlers_for_namespace(namespace: str): 37 """ 38 Disables all stream handlers for all loggers under the specified namespace. 39 40 :param namespace: The namespace whose stream handlers should be disabled. 41 :type namespace: str 42 """ 43 logger = logging.getLogger(namespace) 44 for handler in logger.handlers[:]: 45 if isinstance(handler, logging.StreamHandler): 46 logger.removeHandler(handler) 47 48 for logger_name in logging.Logger.manager.loggerDict: 49 if logger_name.startswith(namespace): 50 _logger = logging.getLogger(logger_name) 51 for handler in _logger.handlers[:]: 52 if isinstance(handler, logging.StreamHandler): 53 _logger.removeHandler(handler) 54 55 56def configure_master_logger( 57 log_file: str, 58 file_path: str = "~/logs", 59 log_level: int = logging.ERROR, 60 disabled: bool = False, 61) -> None: 62 """ 63 Configures the root logger to write to a specified log file. 64 65 :param log_file: Name of the log file. 66 :type log_file: str 67 :param file_path: Path to the directory where log files will be stored. Defaults to '~/logs'. 68 :type file_path: str 69 :param log_level: The logging level to set. Defaults to logging.ERROR. 70 :type log_level: int 71 :param disabled: If True, the logger will be disabled. Defaults to False. 72 :type disabled: bool 73 """ 74 file_path = Path(file_path).expanduser() 75 file_path.mkdir(parents=True, exist_ok=True) 76 full_log_file_path = file_path / log_file 77 78 root_logger = logging.getLogger() 79 root_logger.setLevel(log_level) 80 81 # Remove all existing handlers 82 root_logger.handlers.clear() 83 84 # Add FileHandler 85 handler = logging.FileHandler(full_log_file_path, mode="w") 86 formatter = logging.Formatter( 87 "proteusPy: %(levelname)s %(asctime)s - %(name)s.%(funcName)s - %(message)s" 88 ) 89 handler.setFormatter(formatter) 90 handler.setLevel(log_level) 91 root_logger.addHandler(handler) 92 93 root_logger.disabled = disabled 94 95 96def create_logger( 97 name: str, 98 log_level: int = logging.INFO, 99 propagate: bool = False, # Default to False to avoid duplicates 100) -> logging.Logger: 101 """ 102 Returns a logger with the specified name, configured to use a RichHandler for console output. 103 104 :param name: The name of the logger. 105 :type name: str 106 :param log_level: The logging level, defaults to logging.INFO 107 :type log_level: int 108 :param propagate: Whether to propagate messages to parent loggers, defaults to False 109 :type propagate: bool 110 :return: Configured logger instance. 111 :rtype: logging.Logger 112 """ 113 logger = logging.getLogger(name) 114 logger.setLevel(log_level) 115 116 # Clear existing handlers 117 logger.handlers.clear() 118 119 # Add RichHandler for console output 120 rich_handler = RichHandler(rich_tracebacks=True) 121 rich_formatter = logging.Formatter( 122 "proteusPy: %(levelname)s %(asctime)s - %(name)s.%(funcName)s - %(message)s" 123 ) 124 rich_handler.setLevel(log_level) 125 rich_handler.setFormatter(rich_formatter) 126 logger.addHandler(rich_handler) 127 128 # Set propagation 129 logger.propagate = propagate 130 131 return logger 132 133 134def set_logger_level(name, level): 135 """ 136 Sets the logging level for the logger with the specified name. 137 138 :param name: The name of the logger. 139 :type name: str 140 :param level: The logging level to set. Must be one of ["WARNING", "ERROR", "INFO", "DEBUG"]. 141 :type level: str 142 :raises ValueError: If the provided level is not one of the allowed values. 143 """ 144 level_dict = { 145 "WARNING": logging.WARNING, 146 "ERROR": logging.ERROR, 147 "INFO": logging.INFO, 148 "DEBUG": logging.DEBUG, 149 } 150 151 if level not in level_dict: 152 raise ValueError( 153 f"set_logger_level(): Invalid logging level: {level}. " 154 "Must be one of ['WARNING', 'ERROR', 'INFO', 'DEBUG']" 155 ) 156 157 _logger = logging.getLogger(name) 158 _logger.setLevel(level_dict[level]) 159 160 for handler in _logger.handlers: 161 handler.setLevel(level_dict[level]) 162 163 164def toggle_stream_handler(name, enable): 165 """ 166 Enables or disables the StreamHandler for the logger with the specified name. 167 168 :param name: The name of the logger. 169 :type name: str 170 :param enable: If True, enables the StreamHandler; if False, disables it. 171 :type enable: bool 172 """ 173 logger = logging.getLogger(name) 174 stream_handler = None 175 176 for handler in logger.handlers: 177 if isinstance(handler, logging.StreamHandler): 178 stream_handler = handler 179 break 180 181 if enable: 182 if stream_handler is None: 183 formatter = logging.Formatter( 184 "stream proteusPy: %(levelname)-7s %(asctime)s - %(name)s.%(funcName)s - %(message)s" 185 ) 186 stream_handler = logging.StreamHandler() 187 stream_handler.setLevel(logger.level) 188 stream_handler.setFormatter(formatter) 189 logger.addHandler(stream_handler) 190 else: 191 if stream_handler is not None: 192 logger.removeHandler(stream_handler) 193 194 195def list_all_loggers(): 196 """ 197 Lists all loggers that have been created in the application. 198 199 :return: List of logger names. 200 :rtype: list 201 """ 202 logger_dict = logging.Logger.manager.loggerDict 203 loggers = [name for name, logger in logger_dict.items() if isinstance(logger, logging.Logger)] 204 return loggers 205 206 207def list_handlers(name): 208 """ 209 Lists the handlers for the logger with the specified name. 210 211 :param name: The name of the logger. 212 :type name: str 213 :return: List of handler types and their configurations. 214 :rtype: list 215 """ 216 logger = logging.getLogger(name) 217 handlers_info = [] 218 219 for handler in logger.handlers: 220 handler_type = type(handler).__name__ 221 handler_info = { 222 "type": handler_type, 223 "level": logging.getLevelName(handler.level), 224 "formatter": handler.formatter._fmt if handler.formatter else None, 225 } 226 handlers_info.append(handler_info) 227 228 return handlers_info 229 230 231def set_logger_level_for_module(pkg_name, level=""): 232 """ 233 Set the logging level for all loggers within a specified package. 234 235 This function iterates through all registered loggers and sets the logging 236 level for those that belong to the specified package. 237 238 :param pkg_name: The name of the package for which to set the logging level. 239 :type pkg_name: str 240 :param level: The logging level to set (e.g., 'DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'). 241 If not specified, the logging level will not be changed. 242 :type level: str, optional 243 :return: A list of logger names that were found and had their levels set. 244 :rtype: list 245 """ 246 logger_dict = logging.Logger.manager.loggerDict 247 registered_loggers = [ 248 name 249 for name, logger in logger_dict.items() 250 if isinstance(logger, logging.Logger) and name.startswith(pkg_name) 251 ] 252 for logger_name in registered_loggers: 253 logger = logging.getLogger(logger_name) 254 if level: 255 logger.setLevel(level) 256 257 return registered_loggers 258 259 260if __name__ == "__main__": 261 import doctest 262 263 doctest.testmod() 264 265# end of file
20def set_logging_level_for_all_handlers(log_level: int): 21 """ 22 Sets the logging level for all handlers of all loggers in the proteusPy package. 23 24 :param log_level: The logging level to set. 25 :type log_level: int 26 """ 27 root_logger = logging.getLogger() 28 root_logger.setLevel(log_level) 29 30 for logger_name in logging.Logger.manager.loggerDict: 31 _logger = logging.getLogger(logger_name) 32 _logger.setLevel(log_level) 33 for handler in _logger.handlers: 34 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.
37def disable_stream_handlers_for_namespace(namespace: str): 38 """ 39 Disables all stream handlers for all loggers under the specified namespace. 40 41 :param namespace: The namespace whose stream handlers should be disabled. 42 :type namespace: str 43 """ 44 logger = logging.getLogger(namespace) 45 for handler in logger.handlers[:]: 46 if isinstance(handler, logging.StreamHandler): 47 logger.removeHandler(handler) 48 49 for logger_name in logging.Logger.manager.loggerDict: 50 if logger_name.startswith(namespace): 51 _logger = logging.getLogger(logger_name) 52 for handler in _logger.handlers[:]: 53 if isinstance(handler, logging.StreamHandler): 54 _logger.removeHandler(handler)
Disables all stream handlers for all loggers under the specified namespace.
Parameters
- namespace: The namespace whose stream handlers should be disabled.
57def configure_master_logger( 58 log_file: str, 59 file_path: str = "~/logs", 60 log_level: int = logging.ERROR, 61 disabled: bool = False, 62) -> None: 63 """ 64 Configures the root logger to write to a specified log file. 65 66 :param log_file: Name of the log file. 67 :type log_file: str 68 :param file_path: Path to the directory where log files will be stored. Defaults to '~/logs'. 69 :type file_path: str 70 :param log_level: The logging level to set. Defaults to logging.ERROR. 71 :type log_level: int 72 :param disabled: If True, the logger will be disabled. Defaults to False. 73 :type disabled: bool 74 """ 75 file_path = Path(file_path).expanduser() 76 file_path.mkdir(parents=True, exist_ok=True) 77 full_log_file_path = file_path / log_file 78 79 root_logger = logging.getLogger() 80 root_logger.setLevel(log_level) 81 82 # Remove all existing handlers 83 root_logger.handlers.clear() 84 85 # Add FileHandler 86 handler = logging.FileHandler(full_log_file_path, mode="w") 87 formatter = logging.Formatter( 88 "proteusPy: %(levelname)s %(asctime)s - %(name)s.%(funcName)s - %(message)s" 89 ) 90 handler.setFormatter(formatter) 91 handler.setLevel(log_level) 92 root_logger.addHandler(handler) 93 94 root_logger.disabled = disabled
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.
97def create_logger( 98 name: str, 99 log_level: int = logging.INFO, 100 propagate: bool = False, # Default to False to avoid duplicates 101) -> logging.Logger: 102 """ 103 Returns a logger with the specified name, configured to use a RichHandler for console output. 104 105 :param name: The name of the logger. 106 :type name: str 107 :param log_level: The logging level, defaults to logging.INFO 108 :type log_level: int 109 :param propagate: Whether to propagate messages to parent loggers, defaults to False 110 :type propagate: bool 111 :return: Configured logger instance. 112 :rtype: logging.Logger 113 """ 114 logger = logging.getLogger(name) 115 logger.setLevel(log_level) 116 117 # Clear existing handlers 118 logger.handlers.clear() 119 120 # Add RichHandler for console output 121 rich_handler = RichHandler(rich_tracebacks=True) 122 rich_formatter = logging.Formatter( 123 "proteusPy: %(levelname)s %(asctime)s - %(name)s.%(funcName)s - %(message)s" 124 ) 125 rich_handler.setLevel(log_level) 126 rich_handler.setFormatter(rich_formatter) 127 logger.addHandler(rich_handler) 128 129 # Set propagation 130 logger.propagate = propagate 131 132 return logger
Returns a logger with the specified name, configured to use a RichHandler for console output.
Parameters
- name: The name of the logger.
- log_level: The logging level, defaults to logging.INFO
- propagate: Whether to propagate messages to parent loggers, defaults to False
Returns
Configured logger instance.
135def set_logger_level(name, level): 136 """ 137 Sets the logging level for the logger with the specified name. 138 139 :param name: The name of the logger. 140 :type name: str 141 :param level: The logging level to set. Must be one of ["WARNING", "ERROR", "INFO", "DEBUG"]. 142 :type level: str 143 :raises ValueError: If the provided level is not one of the allowed values. 144 """ 145 level_dict = { 146 "WARNING": logging.WARNING, 147 "ERROR": logging.ERROR, 148 "INFO": logging.INFO, 149 "DEBUG": logging.DEBUG, 150 } 151 152 if level not in level_dict: 153 raise ValueError( 154 f"set_logger_level(): Invalid logging level: {level}. " 155 "Must be one of ['WARNING', 'ERROR', 'INFO', 'DEBUG']" 156 ) 157 158 _logger = logging.getLogger(name) 159 _logger.setLevel(level_dict[level]) 160 161 for handler in _logger.handlers: 162 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.
165def toggle_stream_handler(name, enable): 166 """ 167 Enables or disables the StreamHandler for the logger with the specified name. 168 169 :param name: The name of the logger. 170 :type name: str 171 :param enable: If True, enables the StreamHandler; if False, disables it. 172 :type enable: bool 173 """ 174 logger = logging.getLogger(name) 175 stream_handler = None 176 177 for handler in logger.handlers: 178 if isinstance(handler, logging.StreamHandler): 179 stream_handler = handler 180 break 181 182 if enable: 183 if stream_handler is None: 184 formatter = logging.Formatter( 185 "stream proteusPy: %(levelname)-7s %(asctime)s - %(name)s.%(funcName)s - %(message)s" 186 ) 187 stream_handler = logging.StreamHandler() 188 stream_handler.setLevel(logger.level) 189 stream_handler.setFormatter(formatter) 190 logger.addHandler(stream_handler) 191 else: 192 if stream_handler is not None: 193 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.
196def list_all_loggers(): 197 """ 198 Lists all loggers that have been created in the application. 199 200 :return: List of logger names. 201 :rtype: list 202 """ 203 logger_dict = logging.Logger.manager.loggerDict 204 loggers = [name for name, logger in logger_dict.items() if isinstance(logger, logging.Logger)] 205 return loggers
Lists all loggers that have been created in the application.
Returns
List of logger names.
208def list_handlers(name): 209 """ 210 Lists the handlers for the logger with the specified name. 211 212 :param name: The name of the logger. 213 :type name: str 214 :return: List of handler types and their configurations. 215 :rtype: list 216 """ 217 logger = logging.getLogger(name) 218 handlers_info = [] 219 220 for handler in logger.handlers: 221 handler_type = type(handler).__name__ 222 handler_info = { 223 "type": handler_type, 224 "level": logging.getLevelName(handler.level), 225 "formatter": handler.formatter._fmt if handler.formatter else None, 226 } 227 handlers_info.append(handler_info) 228 229 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.
232def set_logger_level_for_module(pkg_name, level=""): 233 """ 234 Set the logging level for all loggers within a specified package. 235 236 This function iterates through all registered loggers and sets the logging 237 level for those that belong to the specified package. 238 239 :param pkg_name: The name of the package for which to set the logging level. 240 :type pkg_name: str 241 :param level: The logging level to set (e.g., 'DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'). 242 If not specified, the logging level will not be changed. 243 :type level: str, optional 244 :return: A list of logger names that were found and had their levels set. 245 :rtype: list 246 """ 247 logger_dict = logging.Logger.manager.loggerDict 248 registered_loggers = [ 249 name 250 for name, logger in logger_dict.items() 251 if isinstance(logger, logging.Logger) and name.startswith(pkg_name) 252 ] 253 for logger_name in registered_loggers: 254 logger = logging.getLogger(logger_name) 255 if level: 256 logger.setLevel(level) 257 258 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.