diff --git a/logging.py b/logging.py index ec9545f2bc2fd47014c800dac8e6c30480a2ec76..bc82d0f8cb0dffa1f6b2bad0a29b445e5b021fcb 100644 --- a/logging.py +++ b/logging.py @@ -7,12 +7,14 @@ Version 1.0 # System Imports. -import logging.config, os +import logging.config, pathlib, sys # Logging Variables. -project_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -logging_directory = os.path.join(project_dir, 'src/logs') +this = sys.modules[__name__] +this.settings = None +project_dir = pathlib.Path().absolute() +logging_directory = project_dir.joinpath('src/logs') logging_class = 'logging.handlers.RotatingFileHandler' logging_max_bytes = 1024 * 1024 * 10 # Max log file size of 10 MB. logging_backup_count = 10 # Keep 10 log files before overwriting. @@ -61,7 +63,7 @@ def get_logging_settings(): 'file_debug': { 'level': 'DEBUG', 'class': logging_class, - 'filename': os.path.join(logging_directory, 'debug.log'), + 'filename': logging_directory.joinpath('debug.log'), 'maxBytes': logging_max_bytes, 'backupCount': logging_backup_count, 'formatter': 'standard', @@ -70,7 +72,7 @@ def get_logging_settings(): 'file_info': { 'level': 'INFO', 'class': logging_class, - 'filename': os.path.join(logging_directory, 'info.log'), + 'filename': logging_directory.joinpath('info.log'), 'maxBytes': logging_max_bytes, 'backupCount': logging_backup_count, 'formatter': 'standard', @@ -79,7 +81,7 @@ def get_logging_settings(): 'file_warn': { 'level': 'WARNING', 'class': logging_class, - 'filename': os.path.join(logging_directory, 'warn.log'), + 'filename': logging_directory.joinpath('warn.log'), 'maxBytes': logging_max_bytes, 'backupCount': logging_backup_count, 'formatter': 'standard', @@ -88,7 +90,7 @@ def get_logging_settings(): 'file_error': { 'level': 'ERROR', 'class': logging_class, - 'filename': os.path.join(logging_directory, 'error.log'), + 'filename': logging_directory.joinpath('error.log'), 'maxBytes': logging_max_bytes, 'backupCount': logging_backup_count, 'formatter': 'standard', @@ -96,11 +98,11 @@ def get_logging_settings(): 'test_level': { 'level': 'TESTING', 'class': logging_class, - 'filename': os.path.join(logging_directory, 'error.log'), + 'filename': logging_directory.joinpath('test.log'), 'maxBytes': logging_max_bytes, 'backupCount': logging_backup_count, 'formatter': 'standard', - } + }, }, 'loggers': { # All basic logging. @@ -117,19 +119,24 @@ def init_logging(caller): """ Initializes and returns an instance of the logger. :param caller: __name__ attribute of calling file. + :param initialize_settings: Boolean defined at top of file. :return: Instance of logger, associated with calling file's __name__. """ - # Create logging folder if does not exist. - if not os.path.exists(logging_directory): - print('Creating logging folders.') - os.makedirs(logging_directory) + if this.settings is None: + # Create logging folder if does not exist. + if not logging_directory.is_dir(): + print('Creating logging folders.') + logging_directory.mkdir() - # Add new logging levels. - add_logging_level('testing', 25) + # Add new logging levels. + add_logging_level('TESTING', 25) - # Load dictionary of settings into logger. - logger_settings = get_logging_settings() - logging.config.dictConfig(logger_settings) + # Load dictionary of settings into logger. + this.settings = get_logging_settings() + logging.config.dictConfig(this.settings) + + # Clear setup variable to prevent calling again on each file that imports logging. + initialize_settings = False return logging.getLogger(caller) @@ -148,17 +155,22 @@ def add_logging_level(level_name, level_num, method_name=None): method_name = level_name.lower() # Check if values have already been defined in logger. Prevents accidental overriding. - if hasattr(logging, level_name): - raise AttributeError('{} already defined in logging module.'.format(level_name)) - if hasattr(logging, method_name): - raise AttributeError('{} already defined in logging module.'.format(method_name)) - if hasattr(logging.getLoggerClass(), method_name): - raise AttributeError('{} already defined in logger class.'.format(method_name)) + if (hasattr(logging, level_name) and + hasattr(logging, method_name) and + hasattr(logging.getLoggerClass(), method_name)): + # Log level already set with same values. Skip setting. + return None + elif (hasattr(logging, level_name) or + hasattr(logging, method_name) or + hasattr(logging.getLoggerClass(), method_name)): + # Log level partially defined with some values. Raise error. + raise AttributeError('{0} already defined in logging module, but values do not match.'.format(level_name)) # Methods to enable logging at new level. def log_for_level(self, message, *args, **kwargs): if self.isEnabledFor(level_num): self._log(level_num, message, args, **kwargs) + def log_to_root(message, *args, **kwargs): logging.log(level_num, message, *args, **kwargs) diff --git a/tests/src/logging.py b/tests/src/logging.py index 5a1b35bd1e908b796b8426e6862b2cc2f36a38b1..bc82d0f8cb0dffa1f6b2bad0a29b445e5b021fcb 100644 --- a/tests/src/logging.py +++ b/tests/src/logging.py @@ -7,12 +7,14 @@ Version 1.0 # System Imports. -import logging.config, os +import logging.config, pathlib, sys # Logging Variables. -project_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -logging_directory = os.path.join(project_dir, 'src/logs') +this = sys.modules[__name__] +this.settings = None +project_dir = pathlib.Path().absolute() +logging_directory = project_dir.joinpath('src/logs') logging_class = 'logging.handlers.RotatingFileHandler' logging_max_bytes = 1024 * 1024 * 10 # Max log file size of 10 MB. logging_backup_count = 10 # Keep 10 log files before overwriting. @@ -61,7 +63,7 @@ def get_logging_settings(): 'file_debug': { 'level': 'DEBUG', 'class': logging_class, - 'filename': os.path.join(logging_directory, 'debug.log'), + 'filename': logging_directory.joinpath('debug.log'), 'maxBytes': logging_max_bytes, 'backupCount': logging_backup_count, 'formatter': 'standard', @@ -70,7 +72,7 @@ def get_logging_settings(): 'file_info': { 'level': 'INFO', 'class': logging_class, - 'filename': os.path.join(logging_directory, 'info.log'), + 'filename': logging_directory.joinpath('info.log'), 'maxBytes': logging_max_bytes, 'backupCount': logging_backup_count, 'formatter': 'standard', @@ -79,7 +81,7 @@ def get_logging_settings(): 'file_warn': { 'level': 'WARNING', 'class': logging_class, - 'filename': os.path.join(logging_directory, 'warn.log'), + 'filename': logging_directory.joinpath('warn.log'), 'maxBytes': logging_max_bytes, 'backupCount': logging_backup_count, 'formatter': 'standard', @@ -88,7 +90,7 @@ def get_logging_settings(): 'file_error': { 'level': 'ERROR', 'class': logging_class, - 'filename': os.path.join(logging_directory, 'error.log'), + 'filename': logging_directory.joinpath('error.log'), 'maxBytes': logging_max_bytes, 'backupCount': logging_backup_count, 'formatter': 'standard', @@ -96,7 +98,7 @@ def get_logging_settings(): 'test_level': { 'level': 'TESTING', 'class': logging_class, - 'filename': os.path.join(logging_directory, 'test.log'), + 'filename': logging_directory.joinpath('test.log'), 'maxBytes': logging_max_bytes, 'backupCount': logging_backup_count, 'formatter': 'standard', @@ -117,19 +119,24 @@ def init_logging(caller): """ Initializes and returns an instance of the logger. :param caller: __name__ attribute of calling file. + :param initialize_settings: Boolean defined at top of file. :return: Instance of logger, associated with calling file's __name__. """ - # Create logging folder if does not exist. - if not os.path.exists(logging_directory): - print('Creating logging folders.') - os.makedirs(logging_directory) + if this.settings is None: + # Create logging folder if does not exist. + if not logging_directory.is_dir(): + print('Creating logging folders.') + logging_directory.mkdir() - # Add new logging levels. - add_logging_level('TESTING', 25) + # Add new logging levels. + add_logging_level('TESTING', 25) - # Load dictionary of settings into logger. - logger_settings = get_logging_settings() - logging.config.dictConfig(logger_settings) + # Load dictionary of settings into logger. + this.settings = get_logging_settings() + logging.config.dictConfig(this.settings) + + # Clear setup variable to prevent calling again on each file that imports logging. + initialize_settings = False return logging.getLogger(caller) @@ -148,17 +155,22 @@ def add_logging_level(level_name, level_num, method_name=None): method_name = level_name.lower() # Check if values have already been defined in logger. Prevents accidental overriding. - if hasattr(logging, level_name): - raise AttributeError('{} already defined in logging module.'.format(level_name)) - if hasattr(logging, method_name): - raise AttributeError('{} already defined in logging module.'.format(method_name)) - if hasattr(logging.getLoggerClass(), method_name): - raise AttributeError('{} already defined in logger class.'.format(method_name)) + if (hasattr(logging, level_name) and + hasattr(logging, method_name) and + hasattr(logging.getLoggerClass(), method_name)): + # Log level already set with same values. Skip setting. + return None + elif (hasattr(logging, level_name) or + hasattr(logging, method_name) or + hasattr(logging.getLoggerClass(), method_name)): + # Log level partially defined with some values. Raise error. + raise AttributeError('{0} already defined in logging module, but values do not match.'.format(level_name)) # Methods to enable logging at new level. def log_for_level(self, message, *args, **kwargs): if self.isEnabledFor(level_num): self._log(level_num, message, args, **kwargs) + def log_to_root(message, *args, **kwargs): logging.log(level_num, message, *args, **kwargs)