diff --git a/documents/references.md b/documents/references.md new file mode 100644 index 0000000000000000000000000000000000000000..764a3d6145d228254877d7d893bf2f4d24154649 --- /dev/null +++ b/documents/references.md @@ -0,0 +1,17 @@ +# Data Measures > Documents > References.md + + +## Description +All references to external logic. Includes anything from stack overflow links to notes about logic from previous works. + + +## Logging Logic +The contents of `resources/logging.py` (and all associated logic) originally started from me trying to +learn how to log information through Django (the Python Web Framework) using a Dictionary type format. (See +<https://docs.djangoproject.com/en/dev/topics/logging/> for reference). + +In 2017, I started expanding and changing this logic to be usable in Non-Django applications, such as personal projects +and school assignments. After repeatedly importing and updating as needed, the logging.py file has slowly changed into +what it is today. + +Logging files can be found in `resources/logs/`. diff --git a/main.py b/main.py new file mode 100644 index 0000000000000000000000000000000000000000..672e0e4796c0ef8706b2cd683543c87a3137fb1c --- /dev/null +++ b/main.py @@ -0,0 +1,24 @@ +""" +Covers basic statistical measurements in data mining. +""" + +# System Imports. + +# User Imports. +from resources import logging as init_logging + + +# Initialize Logger. +logger = init_logging.get_logger(__name__) + + + +def main(): + logger.info('Starting main()') + + + +if __name__ == '__main__': + logger.info('Starting program.') + main() + logger.info('Terminating program.') diff --git a/resources/logging.py b/resources/logging.py new file mode 100644 index 0000000000000000000000000000000000000000..a0a61457fb18d7b4fee625bb25f7ada99d2ca9d9 --- /dev/null +++ b/resources/logging.py @@ -0,0 +1,143 @@ +""" +Logging initialization. + +Note: Standard log priority is "NOTSET" > "DEBUG" > "INFO" > "WARNING" > "ERROR" > "CRITICAL". +""" + +# System Imports. +import logging.config, os + + +# Variables to help library determine how to run logging. +graph_library_logger = True +first_logging_call = True + + +def get_logger(caller): + """ + Returns an instance of the logger. Always pass the __name__ attribute. + By calling through here, guarantees that logger will always have proper settings loaded. + :param caller: __name__ attribute of caller. + :return: Instance of logger, associated with caller's __name__. + """ + # Initialize logger. We should only have to initialize it once per runtime. + if first_logging_call: + _initialize_logger_settings() + + # Return logger instance, using passed name. + return logging.getLogger(caller) + + +def _initialize_logger_settings(debug=False): + """ + Create log directories (if not found) and initialized logging settings. + :param debug: Boolean to indicate if test log messages should also be displayed after initialization. + """ + # Determine logging path. + project_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + log_dir = os.path.join(project_dir, 'resources/logs') + + # Check if logging path exists. + if not os.path.exists(log_dir): + print('Creating logging folders at "{0}".'.format(log_dir)) + os.makedirs(log_dir) + + # Load dictionary of settings into logger. + logging.config.dictConfig(_create_logging_dict(log_dir)) + + # Now that logging has been initialized once, we don't need to call this function again for the duration of program + # runtime. Set "first_logging_call" variable accordingly. + global first_logging_call + first_logging_call = False + + # Optionally test that logging is working as expected. + if debug: + logger = logging.getLogger(__name__) + logger.info('Logging initialized.') + logger.debug('Logging directory: {0}'.format(log_dir)) + + +def _create_logging_dict(log_directory): + """ + Creates dictionary-styled logging options. + :param log_directory: Directory to use for logging initialization. + :return: Dictionary of logging options/settings. + """ + # Dictionary style logging options. + return { + 'version': 1, + 'formatters': { + # Minimal logging. Only includes message. + 'minimal': { + 'format': '%(message)s', + }, + # Simple logging. Includes message type and actual message. + 'simple': { + 'format': '[%(levelname)s] [%(filename)s %(lineno)d]: %(message)s', + }, + # Basic logging. Includes date, message type, file originated, and actual message. + 'standard': { + 'format': '%(asctime)s [%(levelname)s] [%(filename)s %(lineno)d]: %(message)s', + }, + # Verbose logging. Includes standard plus the process number and thread id. + 'verbose': { + 'format': '%(asctime)s [%(levelname)s] [%(filename)s %(lineno)d] || %(process)d %(thread)d || %(message)s', + }, + }, + 'handlers': { + # Sends log message to the void. May be useful for debugging. + 'null': { + 'class': 'logging.NullHandler', + }, + # To console. + 'console': { + 'level': 'INFO', + 'class': 'logging.StreamHandler', + 'formatter': 'simple', + }, + # Debug Level - To file. + 'file_debug': { + 'level': 'DEBUG', + 'class': 'logging.handlers.RotatingFileHandler', + 'filename': os.path.join(log_directory, 'debug.log'), + 'maxBytes': 1024*1024*10, + 'backupCount': 10, + 'formatter': 'standard', + }, + # Info Level - To file. + 'file_info': { + 'level': 'INFO', + 'class': 'logging.handlers.RotatingFileHandler', + 'filename': os.path.join(log_directory, 'info.log'), + 'maxBytes': 1024*1024*10, + 'backupCount': 10, + 'formatter': 'standard', + }, + # Warn Level - To file. + 'file_warn': { + 'level': 'WARNING', + 'class': 'logging.handlers.RotatingFileHandler', + 'filename': os.path.join(log_directory, 'warn.log'), + 'maxBytes': 1024*1024*10, + 'backupCount': 10, + 'formatter': 'verbose', + }, + # Error Level - To file. + 'file_error': { + 'level': 'ERROR', + 'class': 'logging.handlers.RotatingFileHandler', + 'filename': os.path.join(log_directory, 'error.log'), + 'maxBytes': 1024*1024*10, + 'backupCount': 10, + 'formatter': 'verbose', + }, + }, + 'loggers': { + # All basic logging. + '': { + 'handlers': ['console', 'file_debug', 'file_info', 'file_warn', 'file_error'], + 'level': 'DEBUG', + 'propagate': False, + } + }, + }