OLD | NEW |
| (Empty) |
1 # Copyright 2013 The Chromium Authors. All rights reserved. | |
2 # Use of this source code is governed by a BSD-style license that can be | |
3 # found in the LICENSE file. | |
4 | |
5 """Various utility functions and classes not specific to any single area.""" | |
6 | |
7 import logging | |
8 import logging.handlers | |
9 import optparse | |
10 import os | |
11 import sys | |
12 import time | |
13 import traceback | |
14 | |
15 | |
16 class OptionParserWithLogging(optparse.OptionParser): | |
17 """Adds --verbose option.""" | |
18 | |
19 # Set to True to enable --log-file options. | |
20 enable_log_file = True | |
21 | |
22 def __init__(self, verbose=0, log_file=None, **kwargs): | |
23 kwargs.setdefault('description', sys.modules['__main__'].__doc__) | |
24 optparse.OptionParser.__init__(self, **kwargs) | |
25 self.add_option( | |
26 '-v', '--verbose', | |
27 action='count', | |
28 default=verbose, | |
29 help='Use multiple times to increase verbosity') | |
30 if self.enable_log_file: | |
31 self.add_option( | |
32 '-l', '--log-file', | |
33 default=log_file, | |
34 help='The name of the file to store rotating log details') | |
35 self.add_option( | |
36 '--no-log', action='store_const', const='', dest='log_file', | |
37 help='Disable log file') | |
38 | |
39 def parse_args(self, *args, **kwargs): | |
40 options, args = optparse.OptionParser.parse_args(self, *args, **kwargs) | |
41 levels = [logging.ERROR, logging.INFO, logging.DEBUG] | |
42 level = levels[min(len(levels) - 1, options.verbose)] | |
43 | |
44 logging_console = logging.StreamHandler() | |
45 logging_console.setFormatter(logging.Formatter( | |
46 '%(levelname)5s %(module)15s(%(lineno)3d): %(message)s')) | |
47 logging_console.setLevel(level) | |
48 logging.getLogger().setLevel(level) | |
49 logging.getLogger().addHandler(logging_console) | |
50 | |
51 if self.enable_log_file and options.log_file: | |
52 # This is necessary otherwise attached handler will miss the messages. | |
53 logging.getLogger().setLevel(logging.DEBUG) | |
54 | |
55 logging_rotating_file = logging.handlers.RotatingFileHandler( | |
56 options.log_file, | |
57 maxBytes=10 * 1024 * 1024, | |
58 backupCount=5, | |
59 encoding='utf-8') | |
60 # log files are always at DEBUG level. | |
61 logging_rotating_file.setLevel(logging.DEBUG) | |
62 logging_rotating_file.setFormatter(logging.Formatter( | |
63 '%(asctime)s %(levelname)-8s %(module)15s(%(lineno)3d): %(message)s')) | |
64 logging.getLogger().addHandler(logging_rotating_file) | |
65 | |
66 return options, args | |
67 | |
68 | |
69 class Profiler(object): | |
70 """Context manager that records time spend inside its body.""" | |
71 def __init__(self, name): | |
72 self.name = name | |
73 self.start_time = None | |
74 | |
75 def __enter__(self): | |
76 self.start_time = time.time() | |
77 return self | |
78 | |
79 def __exit__(self, _exc_type, _exec_value, _traceback): | |
80 time_taken = time.time() - self.start_time | |
81 logging.info('Profiling: Section %s took %3.3f seconds', | |
82 self.name, time_taken) | |
83 | |
84 | |
85 class Unbuffered(object): | |
86 """Disable buffering on a file object.""" | |
87 def __init__(self, stream): | |
88 self.stream = stream | |
89 | |
90 def write(self, data): | |
91 self.stream.write(data) | |
92 if '\n' in data: | |
93 self.stream.flush() | |
94 | |
95 def __getattr__(self, attr): | |
96 return getattr(self.stream, attr) | |
97 | |
98 | |
99 def disable_buffering(): | |
100 """Makes this process and child processes stdout unbuffered.""" | |
101 if not os.environ.get('PYTHONUNBUFFERED'): | |
102 # Since sys.stdout is a C++ object, it's impossible to do | |
103 # sys.stdout.write = lambda... | |
104 sys.stdout = Unbuffered(sys.stdout) | |
105 os.environ['PYTHONUNBUFFERED'] = 'x' | |
106 | |
107 | |
108 def fix_python_path(cmd): | |
109 """Returns the fixed command line to call the right python executable.""" | |
110 out = cmd[:] | |
111 if out[0] == 'python': | |
112 out[0] = sys.executable | |
113 elif out[0].endswith('.py'): | |
114 out.insert(0, sys.executable) | |
115 return out | |
116 | |
117 | |
118 def report_error(error): | |
119 """Prints a error to stderr, wrapping it into header and footer. | |
120 | |
121 That way errors can be reliably extracted from logs. It's indented to be used | |
122 only for non recoverable unexpected errors. Is should NOT be used for input | |
123 validation, command line argument errors, etc. | |
124 | |
125 Arguments: | |
126 error: error message string (possibly multiple lines) or an instance of | |
127 Exception subclass. In the later case a traceback will also be | |
128 reported. It's assumed that |report_error| is called in an except | |
129 block where |error| was caught. | |
130 """ | |
131 print >> sys.stderr, '[------ Swarming Error ------]' | |
132 print >> sys.stderr, str(error) | |
133 if isinstance(error, Exception): | |
134 print >> sys.stderr, traceback.format_exc(), | |
135 print >> sys.stderr, '[----------------------------]' | |
OLD | NEW |