Index: client/utils/logging_utils.py |
diff --git a/appengine/swarming/swarming_bot/logging_utils.py b/client/utils/logging_utils.py |
similarity index 57% |
rename from appengine/swarming/swarming_bot/logging_utils.py |
rename to client/utils/logging_utils.py |
index efddb9c99c8da8360341115449ff09bc87aa0568..02420c7ffd563e7fd8fdd05c749c27b0cf271bff 100644 |
--- a/appengine/swarming/swarming_bot/logging_utils.py |
+++ b/client/utils/logging_utils.py |
@@ -1,16 +1,14 @@ |
-# Copyright 2014 The Swarming Authors. All rights reserved. |
-# Use of this source code is governed by the Apache v2.0 license that can be |
-# found in the LICENSE file. |
+# Copyright 2015 The Swarming Authors. All rights reserved. |
+# Use of this source code is governed under the Apache License, Version 2.0 that |
+# can be found in the LICENSE file. |
-"""Utility relating to logging. |
- |
-TODO(maruel): Merge buffering and output related code from client/utils/tools.py |
-in a single file. |
-""" |
+"""Utility relating to logging.""" |
+import argparse |
import codecs |
import logging |
import logging.handlers |
+import optparse |
import os |
import sys |
import tempfile |
@@ -58,6 +56,10 @@ else: # Not Windows. |
NoInheritRotatingFileHandler = logging.handlers.RotatingFileHandler |
+# Levels used for logging. |
+LEVELS = [logging.ERROR, logging.INFO, logging.DEBUG] |
+ |
+ |
class CaptureLogs(object): |
"""Captures all the logs in a context.""" |
def __init__(self, prefix, root=None): |
@@ -121,6 +123,18 @@ class UTCFormatter(logging.Formatter): |
return "%s.%03d" % (t, record.msecs) |
+class Filter(logging.Filter): |
+ """Adds fields used by the infra-specific formatter. |
+ |
+ Fields added: |
+ - 'severity': one-letter indicator of log level (first letter of levelname). |
+ """ |
+ |
+ def filter(self, record): |
+ record.severity = record.levelname[0] |
+ return True |
+ |
+ |
def find_stderr(root=None): |
"""Returns the logging.handler streaming to stderr, if any.""" |
for log in (root or logging.getLogger()).handlers: |
@@ -134,8 +148,7 @@ def prepare_logging(filename, root=None): |
Makes it log in UTC all the time. Prepare a rotating file based log. |
""" |
assert not find_stderr(root) |
- formatter = UTCFormatter( |
- '%(process)d %(asctime)s: %(levelname)-5s %(message)s') |
+ formatter = UTCFormatter('%(process)d %(asctime)s %(severity)s: %(message)s') |
# It is a requirement that the root logger is set to DEBUG, so the messages |
# are not lost. It defaults to WARNING otherwise. |
@@ -144,6 +157,7 @@ def prepare_logging(filename, root=None): |
stderr = logging.StreamHandler() |
stderr.setFormatter(formatter) |
+ stderr.addFilter(Filter()) |
# Default to ERROR. |
stderr.setLevel(logging.ERROR) |
logger.addHandler(stderr) |
@@ -153,9 +167,11 @@ def prepare_logging(filename, root=None): |
if filename: |
try: |
rotating_file = NoInheritRotatingFileHandler( |
- filename, maxBytes=10 * 1024 * 1024, backupCount=5) |
+ filename, maxBytes=10 * 1024 * 1024, backupCount=5, |
+ encoding='utf-8') |
rotating_file.setLevel(logging.DEBUG) |
rotating_file.setFormatter(formatter) |
+ rotating_file.addFilter(Filter()) |
logger.addHandler(rotating_file) |
except Exception: |
# May happen on cygwin. Do not crash. |
@@ -166,3 +182,81 @@ def set_console_level(level, root=None): |
"""Reset the console (stderr) logging level.""" |
handler = find_stderr(root) |
handler.setLevel(level) |
+ |
+ |
+class OptionParserWithLogging(optparse.OptionParser): |
+ """Adds --verbose option.""" |
+ |
+ # Set to True to enable --log-file options. |
+ enable_log_file = True |
+ |
+ # Set in unit tests. |
+ logger_root = None |
+ |
+ def __init__(self, verbose=0, log_file=None, **kwargs): |
+ kwargs.setdefault('description', sys.modules['__main__'].__doc__) |
+ optparse.OptionParser.__init__(self, **kwargs) |
+ self.group_logging = optparse.OptionGroup(self, 'Logging') |
+ self.group_logging.add_option( |
+ '-v', '--verbose', |
+ action='count', |
+ default=verbose, |
+ help='Use multiple times to increase verbosity') |
+ if self.enable_log_file: |
+ self.group_logging.add_option( |
+ '-l', '--log-file', |
+ default=log_file, |
+ help='The name of the file to store rotating log details') |
+ self.group_logging.add_option( |
+ '--no-log', action='store_const', const='', dest='log_file', |
+ help='Disable log file') |
+ |
+ def parse_args(self, *args, **kwargs): |
+ # Make sure this group is always the last one. |
+ self.add_option_group(self.group_logging) |
+ |
+ options, args = optparse.OptionParser.parse_args(self, *args, **kwargs) |
+ prepare_logging(self.enable_log_file and options.log_file, self.logger_root) |
+ set_console_level( |
+ LEVELS[min(len(LEVELS) - 1, options.verbose)], self.logger_root) |
+ return options, args |
+ |
+ |
+class ArgumentParserWithLogging(argparse.ArgumentParser): |
+ """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__) |
+ kwargs.setdefault('conflict_handler', 'resolve') |
+ self.__verbose = verbose |
+ self.__log_file = log_file |
+ super(ArgumentParserWithLogging, self).__init__(**kwargs) |
+ |
+ def _add_logging_group(self): |
+ group = self.add_argument_group('Logging') |
+ group.add_argument( |
+ '-v', '--verbose', |
+ action='count', |
+ default=self.__verbose, |
+ help='Use multiple times to increase verbosity') |
+ if self.enable_log_file: |
+ group.add_argument( |
+ '-l', '--log-file', |
+ default=self.__log_file, |
+ help='The name of the file to store rotating log details') |
+ group.add_argument( |
+ '--no-log', action='store_const', const='', dest='log_file', |
+ help='Disable log file') |
+ |
+ def parse_args(self, *args, **kwargs): |
+ # Make sure this group is always the last one. |
+ self._add_logging_group() |
+ |
+ args = super(ArgumentParserWithLogging, self).parse_args(*args, **kwargs) |
+ prepare_logging(self.enable_log_file and args.log_file, self.logger_root) |
+ set_console_level( |
+ LEVELS[min(len(LEVELS) - 1, args.verbose)], self.logger_root) |
+ return args |