| OLD | NEW |
| 1 # Copyright 2013 The Swarming Authors. All rights reserved. | 1 # Copyright 2013 The Swarming Authors. All rights reserved. |
| 2 # Use of this source code is governed under the Apache License, Version 2.0 that | 2 # Use of this source code is governed under the Apache License, Version 2.0 that |
| 3 # can be found in the LICENSE file. | 3 # can be found in the LICENSE file. |
| 4 | 4 |
| 5 """Various utility functions and classes not specific to any single area.""" | 5 """Various utility functions and classes not specific to any single area.""" |
| 6 | 6 |
| 7 import argparse | |
| 8 import atexit | 7 import atexit |
| 9 import cStringIO | 8 import cStringIO |
| 10 import functools | 9 import functools |
| 11 import json | 10 import json |
| 12 import logging | 11 import logging |
| 13 import logging.handlers | |
| 14 import optparse | |
| 15 import os | 12 import os |
| 16 import re | 13 import re |
| 17 import sys | 14 import sys |
| 18 import threading | 15 import threading |
| 19 import time | 16 import time |
| 20 | 17 |
| 21 import utils | 18 import utils |
| 22 from utils import zip_package | 19 from utils import zip_package |
| 23 | 20 |
| 24 | 21 |
| 25 # Path to (possibly extracted from zip) cacert.pem bundle file. | 22 # Path to (possibly extracted from zip) cacert.pem bundle file. |
| 26 # See get_cacerts_bundle(). | 23 # See get_cacerts_bundle(). |
| 27 _ca_certs = None | 24 _ca_certs = None |
| 28 _ca_certs_lock = threading.Lock() | 25 _ca_certs_lock = threading.Lock() |
| 29 | 26 |
| 30 | 27 |
| 31 # @cached decorators registered by report_cache_stats_at_exit. | 28 # @cached decorators registered by report_cache_stats_at_exit. |
| 32 _caches = [] | 29 _caches = [] |
| 33 _caches_lock = threading.Lock() | 30 _caches_lock = threading.Lock() |
| 34 | 31 |
| 35 | 32 |
| 36 class OptionParserWithLogging(optparse.OptionParser): | |
| 37 """Adds --verbose option.""" | |
| 38 | |
| 39 # Set to True to enable --log-file options. | |
| 40 enable_log_file = True | |
| 41 | |
| 42 def __init__(self, verbose=0, log_file=None, **kwargs): | |
| 43 kwargs.setdefault('description', sys.modules['__main__'].__doc__) | |
| 44 optparse.OptionParser.__init__(self, **kwargs) | |
| 45 self.group_logging = optparse.OptionGroup(self, 'Logging') | |
| 46 self.group_logging.add_option( | |
| 47 '-v', '--verbose', | |
| 48 action='count', | |
| 49 default=verbose, | |
| 50 help='Use multiple times to increase verbosity') | |
| 51 if self.enable_log_file: | |
| 52 self.group_logging.add_option( | |
| 53 '-l', '--log-file', | |
| 54 default=log_file, | |
| 55 help='The name of the file to store rotating log details') | |
| 56 self.group_logging.add_option( | |
| 57 '--no-log', action='store_const', const='', dest='log_file', | |
| 58 help='Disable log file') | |
| 59 | |
| 60 def parse_args(self, *args, **kwargs): | |
| 61 # Make sure this group is always the last one. | |
| 62 self.add_option_group(self.group_logging) | |
| 63 | |
| 64 options, args = optparse.OptionParser.parse_args(self, *args, **kwargs) | |
| 65 levels = [logging.ERROR, logging.INFO, logging.DEBUG] | |
| 66 level = levels[min(len(levels) - 1, options.verbose)] | |
| 67 | |
| 68 logging_console = logging.StreamHandler() | |
| 69 logging_console.setFormatter(logging.Formatter( | |
| 70 '%(levelname)5s %(relativeCreated)6d %(module)15s(%(lineno)3d): ' | |
| 71 '%(message)s')) | |
| 72 logging_console.setLevel(level) | |
| 73 logging.getLogger().setLevel(level) | |
| 74 logging.getLogger().addHandler(logging_console) | |
| 75 | |
| 76 if self.enable_log_file and options.log_file: | |
| 77 # This is necessary otherwise attached handler will miss the messages. | |
| 78 logging.getLogger().setLevel(logging.DEBUG) | |
| 79 | |
| 80 logging_rotating_file = logging.handlers.RotatingFileHandler( | |
| 81 options.log_file, | |
| 82 maxBytes=10 * 1024 * 1024, | |
| 83 backupCount=5, | |
| 84 encoding='utf-8') | |
| 85 # log files are always at DEBUG level. | |
| 86 logging_rotating_file.setLevel(logging.DEBUG) | |
| 87 logging_rotating_file.setFormatter(logging.Formatter( | |
| 88 '%(asctime)s %(levelname)-8s %(module)15s(%(lineno)3d): %(message)s')) | |
| 89 logging.getLogger().addHandler(logging_rotating_file) | |
| 90 | |
| 91 return options, args | |
| 92 | |
| 93 | |
| 94 class ArgumentParserWithLogging(argparse.ArgumentParser): | |
| 95 """Adds --verbose option.""" | |
| 96 | |
| 97 # Set to True to enable --log-file options. | |
| 98 enable_log_file = True | |
| 99 | |
| 100 def __init__(self, verbose=0, log_file=None, **kwargs): | |
| 101 kwargs.setdefault('description', sys.modules['__main__'].__doc__) | |
| 102 kwargs.setdefault('conflict_handler', 'resolve') | |
| 103 self.__verbose = verbose | |
| 104 self.__log_file = log_file | |
| 105 super(ArgumentParserWithLogging, self).__init__(**kwargs) | |
| 106 | |
| 107 def _add_logging_group(self): | |
| 108 group = self.add_argument_group('Logging') | |
| 109 group.add_argument( | |
| 110 '-v', '--verbose', | |
| 111 action='count', | |
| 112 default=self.__verbose, | |
| 113 help='Use multiple times to increase verbosity') | |
| 114 if self.enable_log_file: | |
| 115 group.add_argument( | |
| 116 '-l', '--log-file', | |
| 117 default=self.__log_file, | |
| 118 help='The name of the file to store rotating log details') | |
| 119 group.add_argument( | |
| 120 '--no-log', action='store_const', const='', dest='log_file', | |
| 121 help='Disable log file') | |
| 122 | |
| 123 def parse_args(self, *args, **kwargs): | |
| 124 # Make sure this group is always the last one. | |
| 125 self._add_logging_group() | |
| 126 | |
| 127 args = super(ArgumentParserWithLogging, self).parse_args(*args, **kwargs) | |
| 128 levels = [logging.ERROR, logging.INFO, logging.DEBUG] | |
| 129 level = levels[min(len(levels) - 1, args.verbose)] | |
| 130 | |
| 131 logging_console = logging.StreamHandler() | |
| 132 logging_console.setFormatter(logging.Formatter( | |
| 133 '%(levelname)5s %(relativeCreated)6d %(module)15s(%(lineno)3d): ' | |
| 134 '%(message)s')) | |
| 135 logging_console.setLevel(level) | |
| 136 logging.getLogger().setLevel(level) | |
| 137 logging.getLogger().addHandler(logging_console) | |
| 138 | |
| 139 if self.enable_log_file and args.log_file: | |
| 140 # This is necessary otherwise attached handler will miss the messages. | |
| 141 logging.getLogger().setLevel(logging.DEBUG) | |
| 142 | |
| 143 logging_rotating_file = logging.handlers.RotatingFileHandler( | |
| 144 args.log_file, | |
| 145 maxBytes=10 * 1024 * 1024, | |
| 146 backupCount=5, | |
| 147 encoding='utf-8') | |
| 148 # log files are always at DEBUG level. | |
| 149 logging_rotating_file.setLevel(logging.DEBUG) | |
| 150 logging_rotating_file.setFormatter(logging.Formatter( | |
| 151 '%(asctime)s %(levelname)-8s %(module)15s(%(lineno)3d): %(message)s')) | |
| 152 logging.getLogger().addHandler(logging_rotating_file) | |
| 153 | |
| 154 return args | |
| 155 | |
| 156 | |
| 157 class Profiler(object): | 33 class Profiler(object): |
| 158 """Context manager that records time spend inside its body.""" | 34 """Context manager that records time spend inside its body.""" |
| 159 def __init__(self, name): | 35 def __init__(self, name): |
| 160 self.name = name | 36 self.name = name |
| 161 self.start_time = None | 37 self.start_time = None |
| 162 | 38 |
| 163 def __enter__(self): | 39 def __enter__(self): |
| 164 self.start_time = time.time() | 40 self.start_time = time.time() |
| 165 return self | 41 return self |
| 166 | 42 |
| (...skipping 266 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 433 a zip archive, we need to extract the file first. | 309 a zip archive, we need to extract the file first. |
| 434 """ | 310 """ |
| 435 global _ca_certs | 311 global _ca_certs |
| 436 with _ca_certs_lock: | 312 with _ca_certs_lock: |
| 437 if _ca_certs is not None and os.path.exists(_ca_certs): | 313 if _ca_certs is not None and os.path.exists(_ca_certs): |
| 438 return _ca_certs | 314 return _ca_certs |
| 439 # Some rogue process clears /tmp and causes cacert.pem to disappear. Extract | 315 # Some rogue process clears /tmp and causes cacert.pem to disappear. Extract |
| 440 # to current directory instead. We use our own bundled copy of cacert.pem. | 316 # to current directory instead. We use our own bundled copy of cacert.pem. |
| 441 _ca_certs = zip_package.extract_resource(utils, 'cacert.pem', temp_dir='.') | 317 _ca_certs = zip_package.extract_resource(utils, 'cacert.pem', temp_dir='.') |
| 442 return _ca_certs | 318 return _ca_certs |
| OLD | NEW |