diff --git a/bootstrapvz/base/log.py b/bootstrapvz/base/log.py index 83ff97f..5699dcc 100644 --- a/bootstrapvz/base/log.py +++ b/bootstrapvz/base/log.py @@ -4,6 +4,48 @@ both to a file and to the console. import logging +def get_console_handler(debug): + """Returns a log handler for the console + The handler color codes the different log levels + + :params bool debug: Whether to set the log level to DEBUG (otherwise INFO) + :return: The console logging handler + """ + # Create a console log handler + import sys + console_handler = logging.StreamHandler(sys.stderr) + # We want to colorize the output to the console, so we add a formatter + console_handler.setFormatter(ConsoleFormatter()) + # Set the log level depending on the debug argument + if debug: + console_handler.setLevel(logging.DEBUG) + else: + console_handler.setLevel(logging.INFO) + return console_handler + + +def get_file_handler(path, debug): + """Returns a log handler for the given path + If the parent directory of the logpath does not exist it will be created + The handler outputs relative timestamps (to when it was created) + + :params str path: The full path to the logfile + :params bool debug: Whether to set the log level to DEBUG (otherwise INFO) + :return: The file logging handler + """ + import os.path + if not os.path.exists(os.path.dirname(path)): + os.makedirs(os.path.dirname(path)) + # Create the log handler + file_handler = logging.FileHandler(path) + # Absolute timestamps are rather useless when bootstrapping, it's much more interesting + # to see how long things take, so we log in a relative format instead + file_handler.setFormatter(FileFormatter('[%(relativeCreated)s] %(levelname)s: %(message)s')) + # The file log handler always logs everything + file_handler.setLevel(logging.DEBUG) + return file_handler + + def get_log_filename(manifest_path): """Returns the path to a logfile given a manifest The logfile name is constructed from the current timestamp and the basename of the manifest @@ -22,40 +64,6 @@ def get_log_filename(manifest_path): return filename -def setup_logger(logfile=None, debug=False): - """Sets up the python logger to log to both a file and the console - - :param str logfile: Path to a logfile - :param bool debug: Whether to log debug output to the console - """ - root = logging.getLogger() - # Make sure all logging statements are processed by our handlers, they decide the log level - root.setLevel(logging.NOTSET) - - # Only enable logging to file if a destination was supplied - if logfile is not None: - # Create a file log handler - file_handler = logging.FileHandler(logfile) - # Absolute timestamps are rather useless when bootstrapping, it's much more interesting - # to see how long things take, so we log in a relative format instead - file_handler.setFormatter(FileFormatter('[%(relativeCreated)s] %(levelname)s: %(message)s')) - # The file log handler always logs everything - file_handler.setLevel(logging.DEBUG) - root.addHandler(file_handler) - - # Create a console log handler - import sys - console_handler = logging.StreamHandler(sys.stderr) - # We want to colorize the output to the console, so we add a formatter - console_handler.setFormatter(ConsoleFormatter()) - # Set the log level depending on the debug argument - if debug: - console_handler.setLevel(logging.DEBUG) - else: - console_handler.setLevel(logging.INFO) - root.addHandler(console_handler) - - class ConsoleFormatter(logging.Formatter): """Formats log statements for the console """ diff --git a/bootstrapvz/base/main.py b/bootstrapvz/base/main.py index ad64db6..80a4c4d 100644 --- a/bootstrapvz/base/main.py +++ b/bootstrapvz/base/main.py @@ -17,20 +17,14 @@ def main(): if os.geteuid() != 0 and not opts['--dry-run']: raise Exception('This program requires root privileges.') - import log - # Log to file unless --log is a single dash - if opts['--log'] != '-': - # Setup logging - if not os.path.exists(opts['--log']): - os.makedirs(opts['--log']) - log_filename = log.get_log_filename(opts['MANIFEST']) - logfile = os.path.join(opts['--log'], log_filename) - else: - logfile = None - log.setup_logger(logfile=logfile, debug=opts['--debug']) + # Set up logging + setup_loggers(opts) # Everything has been set up, begin the bootstrapping process - run(opts) + run(opts['MANIFEST'], + debug=opts['--debug'], + pause_on_error=opts['--pause-on-error'], + dry_run=opts['--dry-run']) def get_opts(): @@ -49,18 +43,39 @@ Options: --debug Print debugging information -h, --help show this help """ - opts = docopt(usage) - return opts + return docopt(usage) -def run(opts): +def setup_loggers(opts): + """Sets up the file and console loggers + + :params dict opts: Dictionary of options from the commandline + """ + import logging + root = logging.getLogger() + root.setLevel(logging.NOTSET) + + import log + # Log to file unless --log is a single dash + if opts['--log'] != '-': + import os.path + log_filename = log.get_log_filename(opts['MANIFEST']) + logpath = os.path.join(opts['--log'], log_filename) + file_handler = log.get_file_handler(path=logpath, debug=True) + root.addHandler(file_handler) + + console_handler = log.get_console_handler(debug=opts['--debug']) + root.addHandler(console_handler) + + +def run(manifest_path, debug=False, pause_on_error=False, dry_run=False): """Runs the bootstrapping process :params dict opts: Dictionary of options from the commandline """ # Load the manifest from manifest import Manifest - manifest = Manifest(opts['MANIFEST']) + manifest = Manifest(manifest_path) # Get the tasklist from tasklist import load_tasks @@ -71,18 +86,18 @@ def run(opts): # Create the bootstrap information object that'll be used throughout the bootstrapping process from bootstrapinfo import BootstrapInformation - bootstrap_info = BootstrapInformation(manifest=manifest, debug=opts['--debug']) + bootstrap_info = BootstrapInformation(manifest=manifest, debug=debug) try: # Run all the tasks the tasklist has gathered - tasklist.run(info=bootstrap_info, dry_run=opts['--dry-run']) + tasklist.run(info=bootstrap_info, dry_run=dry_run) # We're done! :-) log.info('Successfully completed bootstrapping') return bootstrap_info except (Exception, KeyboardInterrupt) as e: # When an error occurs, log it and begin rollback log.exception(e) - if opts['--pause-on-error']: + if pause_on_error: # The --pause-on-error is useful when the user wants to inspect the volume before rollback raw_input('Press Enter to commence rollback') log.error('Rolling back') @@ -106,6 +121,6 @@ def run(opts): rollback_tasklist = TaskList(rollback_tasks) # Run the rollback tasklist - rollback_tasklist.run(info=bootstrap_info, dry_run=opts['--dry-run']) + rollback_tasklist.run(info=bootstrap_info, dry_run=dry_run) log.info('Successfully completed rollback') raise e