mirror of
https://github.com/kevingruesser/bootstrap-vz.git
synced 2025-08-24 15:36:27 +00:00
Refactor logging setup to be more modular
This commit is contained in:
parent
9d8821235f
commit
7fe9c1ba36
2 changed files with 77 additions and 54 deletions
|
@ -4,6 +4,48 @@ both to a file and to the console.
|
||||||
import logging
|
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):
|
def get_log_filename(manifest_path):
|
||||||
"""Returns the path to a logfile given a manifest
|
"""Returns the path to a logfile given a manifest
|
||||||
The logfile name is constructed from the current timestamp and the basename of the 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
|
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):
|
class ConsoleFormatter(logging.Formatter):
|
||||||
"""Formats log statements for the console
|
"""Formats log statements for the console
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -17,20 +17,14 @@ def main():
|
||||||
if os.geteuid() != 0 and not opts['--dry-run']:
|
if os.geteuid() != 0 and not opts['--dry-run']:
|
||||||
raise Exception('This program requires root privileges.')
|
raise Exception('This program requires root privileges.')
|
||||||
|
|
||||||
import log
|
# Set up logging
|
||||||
# Log to file unless --log is a single dash
|
setup_loggers(opts)
|
||||||
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'])
|
|
||||||
|
|
||||||
# Everything has been set up, begin the bootstrapping process
|
# 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():
|
def get_opts():
|
||||||
|
@ -49,18 +43,39 @@ Options:
|
||||||
--debug Print debugging information
|
--debug Print debugging information
|
||||||
-h, --help show this help
|
-h, --help show this help
|
||||||
"""
|
"""
|
||||||
opts = docopt(usage)
|
return docopt(usage)
|
||||||
return opts
|
|
||||||
|
|
||||||
|
|
||||||
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
|
"""Runs the bootstrapping process
|
||||||
|
|
||||||
:params dict opts: Dictionary of options from the commandline
|
:params dict opts: Dictionary of options from the commandline
|
||||||
"""
|
"""
|
||||||
# Load the manifest
|
# Load the manifest
|
||||||
from manifest import Manifest
|
from manifest import Manifest
|
||||||
manifest = Manifest(opts['MANIFEST'])
|
manifest = Manifest(manifest_path)
|
||||||
|
|
||||||
# Get the tasklist
|
# Get the tasklist
|
||||||
from tasklist import load_tasks
|
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
|
# Create the bootstrap information object that'll be used throughout the bootstrapping process
|
||||||
from bootstrapinfo import BootstrapInformation
|
from bootstrapinfo import BootstrapInformation
|
||||||
bootstrap_info = BootstrapInformation(manifest=manifest, debug=opts['--debug'])
|
bootstrap_info = BootstrapInformation(manifest=manifest, debug=debug)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Run all the tasks the tasklist has gathered
|
# 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! :-)
|
# We're done! :-)
|
||||||
log.info('Successfully completed bootstrapping')
|
log.info('Successfully completed bootstrapping')
|
||||||
return bootstrap_info
|
return bootstrap_info
|
||||||
except (Exception, KeyboardInterrupt) as e:
|
except (Exception, KeyboardInterrupt) as e:
|
||||||
# When an error occurs, log it and begin rollback
|
# When an error occurs, log it and begin rollback
|
||||||
log.exception(e)
|
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
|
# The --pause-on-error is useful when the user wants to inspect the volume before rollback
|
||||||
raw_input('Press Enter to commence rollback')
|
raw_input('Press Enter to commence rollback')
|
||||||
log.error('Rolling back')
|
log.error('Rolling back')
|
||||||
|
@ -106,6 +121,6 @@ def run(opts):
|
||||||
rollback_tasklist = TaskList(rollback_tasks)
|
rollback_tasklist = TaskList(rollback_tasks)
|
||||||
|
|
||||||
# Run the rollback tasklist
|
# 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')
|
log.info('Successfully completed rollback')
|
||||||
raise e
|
raise e
|
||||||
|
|
Loading…
Add table
Reference in a new issue