diff --git a/documents/references.md b/documents/references.md index 0b6fc289032f444c79fa46646922ba4374ea608c..b2fa18f40e0262724e4ca73ad7b9b9c7b9948d01 100644 --- a/documents/references.md +++ b/documents/references.md @@ -13,3 +13,6 @@ Various references used in project. ### Django Logging (Pretty thorough docs of logging "settings dictionaries" in Python) <https://docs.djangoproject.com/en/dev/topics/logging/> + +### Adding a New Logging Level +<https://stackoverflow.com/a/35804945> diff --git a/logging.py b/logging.py index 2b606d97aa3d41a966803fa485d7011b6efd1b49..ec9545f2bc2fd47014c800dac8e6c30480a2ec76 100644 --- a/logging.py +++ b/logging.py @@ -93,11 +93,19 @@ def get_logging_settings(): 'backupCount': logging_backup_count, 'formatter': 'standard', }, + 'test_level': { + 'level': 'TESTING', + 'class': logging_class, + 'filename': os.path.join(logging_directory, 'error.log'), + 'maxBytes': logging_max_bytes, + 'backupCount': logging_backup_count, + 'formatter': 'standard', + } }, 'loggers': { # All basic logging. '': { - 'handlers': ['console', 'file_debug', 'file_info', 'file_warn', 'file_error'], + 'handlers': ['console', 'file_debug', 'file_info', 'file_warn', 'file_error', 'test_level'], 'level': 'NOTSET', 'propagate': False, } @@ -116,8 +124,46 @@ def init_logging(caller): print('Creating logging folders.') os.makedirs(logging_directory) + # 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) return logging.getLogger(caller) + + +def add_logging_level(level_name, level_num, method_name=None): + """ + Logic to add a new logging level to logger. + + Logic from https://stackoverflow.com/a/35804945 + :param level_name: The name of new log level to create. + :param level_num: The numerical value of new log level to create. + :param method_name: The name of invoke method for new log level. Defaults to lowercase of level_name. + """ + # Get method name if not provided. + if not method_name: + 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)) + + # 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) + + # Set logger attributes for new level. + logging.addLevelName(level_num, level_name) + setattr(logging, level_name, level_num) + setattr(logging.getLoggerClass(), method_name, log_for_level) + setattr(logging, method_name, log_to_root) diff --git a/tests/main.py b/tests/main.py index eccfe3af0303c856b86c0e6dda18d8f1c212a954..83886fccd35025e11a0139171bac24281ea0e23f 100644 --- a/tests/main.py +++ b/tests/main.py @@ -18,6 +18,7 @@ def main(): logger.info('Test INFO logging statement.') logger.warning('Test WARNING logging statement.') logger.error('Test ERROR logging statement.') + logger.testing('Test TEST logging statement.') if __name__ == '__main__': diff --git a/tests/src/logging.py b/tests/src/logging.py index 2b606d97aa3d41a966803fa485d7011b6efd1b49..5a1b35bd1e908b796b8426e6862b2cc2f36a38b1 100644 --- a/tests/src/logging.py +++ b/tests/src/logging.py @@ -93,11 +93,19 @@ def get_logging_settings(): 'backupCount': logging_backup_count, 'formatter': 'standard', }, + 'test_level': { + 'level': 'TESTING', + 'class': logging_class, + 'filename': os.path.join(logging_directory, 'test.log'), + 'maxBytes': logging_max_bytes, + 'backupCount': logging_backup_count, + 'formatter': 'standard', + }, }, 'loggers': { # All basic logging. '': { - 'handlers': ['console', 'file_debug', 'file_info', 'file_warn', 'file_error'], + 'handlers': ['console', 'file_debug', 'file_info', 'file_warn', 'file_error', 'test_level'], 'level': 'NOTSET', 'propagate': False, } @@ -116,8 +124,46 @@ def init_logging(caller): print('Creating logging folders.') os.makedirs(logging_directory) + # 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) return logging.getLogger(caller) + + +def add_logging_level(level_name, level_num, method_name=None): + """ + Logic to add a new logging level to logger. + + Logic from https://stackoverflow.com/a/35804945 + :param level_name: The name of new log level to create. + :param level_num: The numerical value of new log level to create. + :param method_name: The name of invoke method for new log level. Defaults to lowercase of level_name. + """ + # Get method name if not provided. + if not method_name: + 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)) + + # 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) + + # Set logger attributes for new level. + logging.addLevelName(level_num, level_name) + setattr(logging, level_name, level_num) + setattr(logging.getLoggerClass(), method_name, log_for_level) + setattr(logging, method_name, log_to_root)