blob: 2885954faca8a2040e4ec9f04fc5779f45ba774d [file] [log] [blame]
# Copyright 2013 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Various utility functions and classes not specific to any single area."""
import logging
import logging.handlers
import optparse
import os
import sys
import time
import traceback
class OptionParserWithLogging(optparse.OptionParser):
"""Adds --verbose option."""
# Set to True to enable --log-file options.
enable_log_file = True
def __init__(self, verbose=0, log_file=None, **kwargs):
kwargs.setdefault('description', sys.modules['__main__'].__doc__)
optparse.OptionParser.__init__(self, **kwargs)
self.add_option(
'-v', '--verbose',
action='count',
default=verbose,
help='Use multiple times to increase verbosity')
if self.enable_log_file:
self.add_option(
'-l', '--log-file',
default=log_file,
help='The name of the file to store rotating log details')
self.add_option(
'--no-log', action='store_const', const='', dest='log_file',
help='Disable log file')
def parse_args(self, *args, **kwargs):
options, args = optparse.OptionParser.parse_args(self, *args, **kwargs)
levels = [logging.ERROR, logging.INFO, logging.DEBUG]
level = levels[min(len(levels) - 1, options.verbose)]
logging_console = logging.StreamHandler()
logging_console.setFormatter(logging.Formatter(
'%(levelname)5s %(module)15s(%(lineno)3d): %(message)s'))
logging_console.setLevel(level)
logging.getLogger().setLevel(level)
logging.getLogger().addHandler(logging_console)
if self.enable_log_file and options.log_file:
# This is necessary otherwise attached handler will miss the messages.
logging.getLogger().setLevel(logging.DEBUG)
logging_rotating_file = logging.handlers.RotatingFileHandler(
options.log_file,
maxBytes=10 * 1024 * 1024,
backupCount=5,
encoding='utf-8')
# log files are always at DEBUG level.
logging_rotating_file.setLevel(logging.DEBUG)
logging_rotating_file.setFormatter(logging.Formatter(
'%(asctime)s %(levelname)-8s %(module)15s(%(lineno)3d): %(message)s'))
logging.getLogger().addHandler(logging_rotating_file)
return options, args
class Profiler(object):
"""Context manager that records time spend inside its body."""
def __init__(self, name):
self.name = name
self.start_time = None
def __enter__(self):
self.start_time = time.time()
return self
def __exit__(self, _exc_type, _exec_value, _traceback):
time_taken = time.time() - self.start_time
logging.info('Profiling: Section %s took %3.3f seconds',
self.name, time_taken)
class Unbuffered(object):
"""Disable buffering on a file object."""
def __init__(self, stream):
self.stream = stream
def write(self, data):
self.stream.write(data)
if '\n' in data:
self.stream.flush()
def __getattr__(self, attr):
return getattr(self.stream, attr)
def disable_buffering():
"""Makes this process and child processes stdout unbuffered."""
if not os.environ.get('PYTHONUNBUFFERED'):
# Since sys.stdout is a C++ object, it's impossible to do
# sys.stdout.write = lambda...
sys.stdout = Unbuffered(sys.stdout)
os.environ['PYTHONUNBUFFERED'] = 'x'
def fix_python_path(cmd):
"""Returns the fixed command line to call the right python executable."""
out = cmd[:]
if out[0] == 'python':
out[0] = sys.executable
elif out[0].endswith('.py'):
out.insert(0, sys.executable)
return out
def report_error(error):
"""Prints a error to stderr, wrapping it into header and footer.
That way errors can be reliably extracted from logs. It's indented to be used
only for non recoverable unexpected errors. Is should NOT be used for input
validation, command line argument errors, etc.
Arguments:
error: error message string (possibly multiple lines) or an instance of
Exception subclass. In the later case a traceback will also be
reported. It's assumed that |report_error| is called in an except
block where |error| was caught.
"""
print >> sys.stderr, '[------ Swarming Error ------]'
print >> sys.stderr, str(error)
if isinstance(error, Exception):
print >> sys.stderr, traceback.format_exc(),
print >> sys.stderr, '[----------------------------]'