Refactor logging setup to be more modular

This commit is contained in:
Anders Ingemann 2014-07-09 22:20:20 +02:00
parent 9d8821235f
commit 7fe9c1ba36
2 changed files with 77 additions and 54 deletions

View file

@ -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
"""

View file

@ -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