From 7022c4370cae8070e4632a423b78298782f3cabb Mon Sep 17 00:00:00 2001 From: Francesco Di Natale Date: Tue, 14 Apr 2020 11:28:38 -0700 Subject: [PATCH] Bugfix for logging that didn't appear in submodules (#247) * Improved logging setup. * Transition to a LoggerUtil class. * Addition of docstring to LoggerUtility + cleanup. --- maestrowf/maestro.py | 63 ++++++++++------------------------------ maestrowf/utils.py | 68 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 48 deletions(-) diff --git a/maestrowf/maestro.py b/maestrowf/maestro.py index 901ef450c..d82ddb875 100644 --- a/maestrowf/maestro.py +++ b/maestrowf/maestro.py @@ -29,7 +29,6 @@ """A script for launching a YAML study specification.""" from argparse import ArgumentParser, ArgumentError, RawTextHelpFormatter -import inspect import logging import os import shutil @@ -44,13 +43,13 @@ from maestrowf.datastructures.core import Study from maestrowf.datastructures.environment import Variable from maestrowf.utils import \ - create_parentdir, create_dictionary, make_safe_path, \ + create_parentdir, create_dictionary, LoggerUtility, make_safe_path, \ start_process # Program Globals -ROOTLOGGER = logging.getLogger(inspect.getmodule(__name__)) LOGGER = logging.getLogger(__name__) +LOG_UTIL = LoggerUtility(LOGGER) # Configuration globals LFORMAT = "%(asctime)s - %(name)s:%(funcName)s:%(lineno)s - " \ @@ -168,8 +167,10 @@ def run_study(args): output_path = make_safe_path(out_dir, *[out_name]) environment.add(Variable("OUTPUT_PATH", output_path)) - # Now that we know outpath, set up logging. - setup_logging(args, output_path, spec.name.replace(" ", "_").lower()) + # Set up file logging + create_parentdir(os.path.join(output_path, "logs")) + log_path = os.path.join(output_path, "logs", "{}.log".format(spec.name)) + LOG_UTIL.add_file_handler(log_path, LFORMAT, args.debug_lvl) # Check for pargs without the matching pgen if args.pargs and not args.pgen: @@ -389,49 +390,6 @@ def setup_argparser(): return parser -def setup_logging(args, path, name): - """ - Set up logging based on the ArgumentParser. - - :param args: A Namespace object created by a parsed ArgumentParser. - :param path: A default path to be used if a log path is not specified by - user command line arguments. - :param name: The name of the log file. - """ - # If the user has specified a path, use that. - if args.logpath: - logpath = args.logpath - # Otherwise, we should just output to the OUTPUT_PATH. - else: - logpath = make_safe_path(path, *["logs"]) - - loglevel = args.debug_lvl * 10 - - # Create the FileHandler and add it to the logger. - create_parentdir(logpath) - formatter = logging.Formatter(LFORMAT) - ROOTLOGGER.setLevel(loglevel) - - log_path = make_safe_path(logpath, *["{}.log".format(name)]) - fh = logging.FileHandler(log_path) - fh.setLevel(loglevel) - fh.setFormatter(formatter) - ROOTLOGGER.addHandler(fh) - - if args.logstdout: - # Add the StreamHandler - sh = logging.StreamHandler() - sh.setLevel(loglevel) - sh.setFormatter(formatter) - ROOTLOGGER.addHandler(sh) - - # Print the level of logging. - LOGGER.info("INFO Logging Level -- Enabled") - LOGGER.warning("WARNING Logging Level -- Enabled") - LOGGER.critical("CRITICAL Logging Level -- Enabled") - LOGGER.debug("DEBUG Logging Level -- Enabled") - - def main(): """ Execute the main program's functionality. @@ -444,6 +402,15 @@ def main(): parser = setup_argparser() args = parser.parse_args() + # If we have requested to log stdout, set it up to be logged. + if args.logstdout: + LOG_UTIL.configure(LFORMAT, args.debug_lvl) + + LOGGER.info("INFO Logging Level -- Enabled") + LOGGER.warning("WARNING Logging Level -- Enabled") + LOGGER.critical("CRITICAL Logging Level -- Enabled") + LOGGER.debug("DEBUG Logging Level -- Enabled") + rc = args.func(args) sys.exit(rc) diff --git a/maestrowf/utils.py b/maestrowf/utils.py index d7404faec..bafdccaeb 100644 --- a/maestrowf/utils.py +++ b/maestrowf/utils.py @@ -258,3 +258,71 @@ def create_dictionary(list_keyvalues, token=":"): raise ValueError(msg) return _dict + + +class LoggerUtility: + """Utility class for setting up logging consistently.""" + + def __init__(self, logger): + """ + Initialize a new LoggerUtility class instance. + + :param logger: An instance of a logger to configure. + """ + self._logger = logger + + def configure(self, log_format, log_lvl=2): + """ + Configures the general logging facility. + + :param log_format: String containing the desired logging format. + :param log_lvl: Integer level (1-5) to set the logger to. + """ + logging.basicConfig(level=self.map_level(log_lvl), format=log_format) + + def add_stream_handler(self, log_format, log_lvl=2): + """ + Add a stream handler to logging. + + :param log_format: String containing the desired logging format. + :param log_lvl: Integer level (1-5) to set the logger to. + """ + # Create the FileHandler and add it to the logger. + sh = logging.StreamHandler() + sh.setLevel(self.map_level(log_lvl)) + sh.setFormatter(logging.Formatter(log_format)) + self._logger.addHandler(sh) + + def add_file_handler(self, log_path, log_format, log_lvl=2): + """ + Add a file handler to logging. + + :param log_path: String containing the file path to store logging. + :param log_format: String containing the desired logging format. + :param log_lvl: Integer level (1-5) to set the logger to. + """ + # Create the FileHandler and add it to the logger. + formatter = logging.Formatter(log_format) + + fh = logging.FileHandler(log_path) + fh.setLevel(self.map_level(log_lvl)) + fh.setFormatter(formatter) + self._logger.addHandler(fh) + + @staticmethod + def map_level(log_lvl): + """ + Map level 1-5 to their respective logging enumerations. + + :param log_lvl: Integer level (1-5) representing logging verbosity. + """ + if log_lvl == 1: + return logging.DEBUG + elif log_lvl == 2: + return logging.INFO + elif log_lvl == 3: + return logging.WARNING + elif log_lvl == 4: + return logging.ERROR + else: + return logging.CRITICAL