Commit da0030e7 authored by Trevor Cappallo's avatar Trevor Cappallo
Browse files

add options, change names

parent d6103181
......@@ -13,6 +13,7 @@ import atexit
import datetime
import StringIO
import inspect
import json
import OperationalMail
......@@ -30,55 +31,51 @@ def __exit_handler(logger, addresses, critical_addresses):
logger.email_log(addresses)
#class MultiLineWrapper(logging.StreamHandler):
# def __init__(self, stream_handler):
# super(logging.StreamHandler, self).__init__(stream_handler)
# self.stream_handler = stream_handler
#
# def emit(self, record):
# print "-------", record
# super(MultiLineWrapper, self).emit(record)
#for line in record.split('\n'):
# self.stream_handler.emit(line)
# def flush(self):
# self.stream_handler.flush()
class OperationalLogger:
"""Main class for Operational Logging."""
def __init__(self, log_file='log.txt', log_level=logging.DEBUG,
log_format=None, date_format=None, from_address=None, logger_name='', console_level=logging.DEBUG):
"""Initialize logging handlers, formats, etc."""
self.__file_name = log_file if logger_name is None else logger_name
# logger = OperationalLogger(
# log_filename=os.path.join(log_directory, log_filename), logger_name=logger_name,
# from_address=from_address, notify=notify, log_level=log_level, console_level=console_level,
# log_format_file=log_format_file, date_format_file=date_format_file,
# log_format_console=log_format_console, date_format_console=date_format_console
# )
def __init__(self, log_filename, logger_name, from_address, notify, log_level, console_level,
log_format_file, date_format_file, log_format_console, date_format_console, **kwargs):
"""Initialize logging handlers, formats, etc.
Should not be called directly; use setup().
"""
self.__server_name = socket.getfqdn()
self.__full_buffer = StringIO.StringIO()
self.__debug_buffer = StringIO.StringIO()
self.__error_buffer = StringIO.StringIO()
self.__critical_buffer = StringIO.StringIO()
if log_format is None:
log_format = '[%(asctime)s][%(levelname)5s][p%(process)d][%(filename)s:%(lineno)d] %(message)s'
self.__log_format = log_format
if date_format is None:
date_format = '%Y/%m/%d %H:%M:%S'
self.__date_format = date_format
if log_format_file is None:
log_format_file = '[%(asctime)s][%(levelname)5s][p%(process)d][%(filename)s:%(lineno)d] %(message)s'
if log_format_console is None:
log_format_console = '%(asctime)s (%(levelname)5s) [%(filename)s:%(lineno)d] %(message)s'
if date_format_file is None:
date_format_file = '%Y-%m-%d %H:%M:%S'
if date_format_console is None:
date_format_console = '%H:%M:%S'
if from_address is None:
from_address = 'no-reply@aer.com'
self.__from_address = from_address
logging.basicConfig(filename=log_file, format=self.__log_format, datefmt=self.__date_format, level=log_level)
formatter = logging.Formatter(fmt=self.__log_format, datefmt='%m/%d %H:%M:%S')
logging.basicConfig(filename=log_filename, format=log_format_file, datefmt=date_format_file, level=logging.DEBUG)
formatter = logging.Formatter(fmt=log_format_console, datefmt=date_format_console)
formatter_sub = logging.Formatter(
fmt='[%(asctime)s][%(levelname)5s][p%(process)d][%(parentname)s:%(parentline)d] %(message)s',
#fmt='%(asctime)s [%(parentname)s:%(parentline)d] :: %(message)s',
fmt='%(asctime)s |- %(message)s',
datefmt='%H:%M:%S'
)
sub_handler = logging.handlers.WatchedFileHandler(log_file)
sub_handler = logging.handlers.WatchedFileHandler(log_filename)
sub_handler.setLevel(logging.INFO)
sub_handler.setFormatter(formatter_sub)
logging.getLogger(logger_name + '_sub').addHandler(sub_handler)
logging.getLogger(logger_name + '_sub').propagate = False
logging.getLogger('_sub').addHandler(sub_handler)
logging.getLogger('_sub').propagate = False
full_trigger = logging.StreamHandler(self.__full_buffer)
full_trigger = logging.StreamHandler(self.__debug_buffer)
full_trigger.setLevel(logging.DEBUG)
full_trigger.setFormatter(formatter)
error_trigger = logging.StreamHandler(self.__error_buffer)
......@@ -97,15 +94,15 @@ class OperationalLogger:
console = logging.StreamHandler(sys.stdout)
console.setLevel(console_level)
console.setFormatter(logging.Formatter(
fmt='[%(asctime)s][%(levelname)5s][%(filename)s:%(lineno)d] %(message)s',
datefmt='%m/%d %H:%M:%S'
fmt=log_format_console,
datefmt=date_format_console,
))
logging.getLogger(logger_name).addHandler(console)
sub_handler_console = logging.StreamHandler(sys.stdout)
sub_handler_console.setLevel(logging.INFO)
sub_handler_console.setFormatter(formatter_sub)
logging.getLogger(logger_name + '_sub').addHandler(sub_handler_console)
logging.getLogger('_sub').addHandler(sub_handler_console)
def has_errors(self):
"""Return whether any ERROR-level log statements in buffer."""
......@@ -115,14 +112,14 @@ class OperationalLogger:
"""Return whether any CRITICAL-level log statements in buffer."""
return len(self.__critical_buffer.getvalue()) > 0
def email_log(self, addresses):
def email_log(self, addresses, from_address):
"""Send email to addresses with current buffered errors."""
print "sending email to", addresses
message = "<br />\n".join([OperationalLogger.__colorize(l) for l in self.__error_buffer.getvalue().split('\n')])
message = "<br />\n".join([__class__.__colorize(line) for line in self.__error_buffer.getvalue().split('\n')])
OperationalMail.send_status(subject='Errors detected {}:{}'.format(self.__server_name,
os.path.basename(sys.argv[0])),
message=message, to_list=addresses, from_address=self.__from_address,
text_attachment=self.__full_buffer.getvalue())
message=message, to_list=addresses, from_address=from_address,
text_attachment=self.__debug_buffer.getvalue())
@staticmethod
def __colorize(line):
......@@ -143,37 +140,60 @@ class OperationalLogger:
##########################################################################################
def default_logging(log_file_name=None, log_root_dir=None, addresses=[], critical_addresses=[], from_address=None, **kwargs):
"""Set an intelligent,context-dependent default logging scheme for the current script.
log_root_dir, log_file_name: log will be created as log_root_dir/log/YYYY-MM-DD/LOG_FILE_NAME.log
log_file_name defaults to script name
addresses, critical_address: email addresses to be notified on respective level errors
def setup(config_file=None, logger_name=None, log_filename=None, log_root_dir=None,
from_address=None, notify=None, log_level=None, console_level=None,
log_format_file=None, date_format_file=None, log_format_console=None, date_format_console=None):
"""Configure logger and begin logging.
config_file: filename to load for logging config as JSON using these keywords, overwritten by args
logger_name: name to pass to logger
log_root_dir, log_filename: log will be created as log_root_dir/log/YYYY-MM-DD/LOG_FILENAME.log
log_root_dir defaults to '.'
log_filename defaults to script name
from_address: email 'From' address for notifications
notify: dict containing 'LOG_LEVEL':['list', 'of', 'addresses']
log_level: cutoff level for overall/file logging
console_level: cutoff level for stdout
log_format_file, date_format_file: formats to use for file logger
log_format_console, date_format_console: formats to use for stdout logger
"""
if not log_file_name:
log_file_name = os.path.basename(sys.argv[0])
if not log_filename:
log_filename = os.path.basename(sys.argv[0]).split('.')[0] + '.log'
if not log_root_dir:
log_root_dir = os.getcwd()
log_directory = os.path.join(log_root_dir, 'log', datetime.datetime.utcnow().strftime('%Y-%m-%d'))
if not os.path.exists(log_directory):
try:
os.makedirs(log_directory, 0755)
except Exception as err:
# Potential race condition. Warn, but do not error
logging.warning('Unable to create log directory {}:{}'.format(log_directory, str(err)))
logger = OperationalLogger(os.path.join(log_directory, '{}.log'.format(log_file_name)),
logging.DEBUG, from_address=from_address, **kwargs)
except OSError:
logging.warn('Unable to create log directory %s', log_directory)
if notify is None:
notify = {}
if logger_name is None:
logger_name = ''
if log_level is None:
log_level = logging.DEBUG
if console_level is None:
console_level = logging.INFO
if config_file:
with open(config_file, 'r') as f:
config = json.load(f)
for key, val in config.items():
if key in vars() and vars()[key] is None:
vars()[key] = val
logger = OperationalLogger(
log_filename=os.path.join(log_directory, log_filename), logger_name=logger_name,
from_address=from_address, notify=notify, log_level=log_level, console_level=console_level,
log_format_file=log_format_file, date_format_file=date_format_file,
log_format_console=log_format_console, date_format_console=date_format_console
)
logging.info('Logging output from: {}'.format(' '.join(sys.argv)))
logging.info('Log writing to: %s', os.path.join(log_directory, log_file_name + '.log'))
logging.info('Log writing to: %s', os.path.join(log_directory, log_filename))
logging.info('Process id {}, parent process id {}'.format(os.getpid(), os.getppid()))
logging.info('-' * 60)
if addresses: # and not sys.stdout.isatty():
atexit.register(__exit_handler, logger, addresses, critical_addresses)
return
if notify:
atexit.register(__exit_handler, logger, notify)
def long_log(logging_level=logging.INFO, message=''):
......@@ -183,7 +203,7 @@ def long_log(logging_level=logging.INFO, message=''):
"""
message_list = message.split("\n")
for msg in message_list:
logging.log(logging_level, ' :: ' + msg)
logging.log(logging_level, ' :: ' + msg)
def long_critical(message):
......@@ -211,31 +231,11 @@ def long_debug(message):
long_log(logging.DEBUG, message)
def logged_subprocess(arg_list):
def log_shell(arg_list, **kwargs):
"""Pass arg_list to a subprocess call and log all output."""
logging.info('Child process of [p{}]'.format(os.getppid()))
logging.info('Attempting {}'.format(' '.join(arg_list)))
logging.info('Starting child process of ppid={}'.format(os.getppid()))
logging.info('Executing: {}'.format(' '.join(arg_list)))
try:
results = subprocess.check_output(arg_list, stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as cpe:
logging.error('Call {} failed with exit code {}'.format(' '.join(arg_list), str(cpe.returncode)))
long_error(cpe.output)
except Exception as err:
logging.error('Unknown exception resulting from {}:{}'.format(' '.join(arg_list), str(err)))
else:
logging.info('Successfully completed {}'.format(' '.join(arg_list)))
long_info(results)
return 0
logging.warning('Failure on {}'.format(' '.join(arg_list)))
return -1
def logged_sub(arg_list, logging_level=logging.INFO, **kwargs):
"""Pass arg_list to a subprocess call and log all output."""
# TODO: respect logging_level
logging.info('Child process of [p{}]'.format(os.getppid()))
logging.info('Attempting {}'.format(' '.join(arg_list)))
try:
#results = ''
my_name = os.path.basename(inspect.getfile(inspect.currentframe()))
for f in inspect.stack():
if my_name != os.path.basename(f[1]):
......@@ -246,30 +246,30 @@ def logged_sub(arg_list, logging_level=logging.INFO, **kwargs):
for line in iter(proc.stdout.readline, b''):
if not line:
break
logging.getLogger('_sub').info(" :: " + line.rstrip(), extra={'parentname':parent_name, 'parentline':line_num})
#results += line
logging.getLogger('_sub').info(line.rstrip(), extra={'parentname':parent_name, 'parentline':line_num})
return_code = proc.wait()
if return_code:
logging.warning('{} failed with exit code {}'.format(' '.join(arg_list), return_code))
return return_code
except OSError as err:
logging.error('Error resulting from {}:{}'.format(' '.join(arg_list), str(err)))
logging.error('Error while executing %s: %s', ' '.join(arg_list), str(err))
return err
else:
#long_info(results)
logging.info('Successfully completed {}'.format(' '.join(arg_list)))
logging.info('Successfully finished: %s', ' '.join(arg_list))
return 0
def stack_trace(logging_level=logging.ERROR):
def stack_trace(log_level=logging.ERROR):
"""Log a stack trace at the given level."""
import traceback
tb = traceback.format_exc()
long_log(logging_level, tb)
long_log(log_level, tb)
if __name__ == '__main__':
#default_logging(addresses=['tcappallo@veriskclimate.com'], from_address='tcappallo@veriskclimate.com')
#default_logging(addresses=['msze@veriskclimate.com', 'msze@aer.com'], from_address='tcappallo@veriskclimate.com')
default_logging()
setup()
test = """Blah
foo
bar
......@@ -285,9 +285,9 @@ if __name__ == '__main__':
#c = ['find', '.', '-name', '*.py']
c = ['diff', 'a', 'b']
#logged_subprocess(c)
logged_sub(c)
log_shell(c)
try:
raise Exception("I AM ERROR.")
except Exception as err:
logging.debug(str(err))
stack_trace(logging.ERROR)
logging.debug(err)
stack_trace()
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment