Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(189)

Side by Side Diff: client/utils/logging_utils.py

Issue 1233303003: Migrate and merge logging_utils from swarming_bot into client/utils. (Closed) Base URL: git@github.com:luci/luci-py.git@master
Patch Set: Created 5 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « client/trace_inputs.py ('k') | client/utils/logging_utils_test.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # Copyright 2014 The Swarming Authors. All rights reserved. 1 # Copyright 2015 The Swarming Authors. All rights reserved.
2 # Use of this source code is governed by the Apache v2.0 license that can be 2 # Use of this source code is governed under the Apache License, Version 2.0 that
3 # found in the LICENSE file. 3 # can be found in the LICENSE file.
4 4
5 """Utility relating to logging. 5 """Utility relating to logging."""
6 6
7 TODO(maruel): Merge buffering and output related code from client/utils/tools.py 7 import argparse
8 in a single file.
9 """
10
11 import codecs 8 import codecs
12 import logging 9 import logging
13 import logging.handlers 10 import logging.handlers
11 import optparse
14 import os 12 import os
15 import sys 13 import sys
16 import tempfile 14 import tempfile
17 import time 15 import time
18 16
19 17
20 # This works around file locking issue on Windows specifically in the case of 18 # This works around file locking issue on Windows specifically in the case of
21 # long lived child processes. 19 # long lived child processes.
22 # 20 #
23 # Python opens files with inheritable handle and without file sharing by 21 # Python opens files with inheritable handle and without file sharing by
(...skipping 27 matching lines...) Expand all
51 return os.fdopen(new_fd, self.mode) 49 return os.fdopen(new_fd, self.mode)
52 return codecs.open(self.baseFilename, self.mode, self.encoding) 50 return codecs.open(self.baseFilename, self.mode, self.encoding)
53 51
54 52
55 else: # Not Windows. 53 else: # Not Windows.
56 54
57 55
58 NoInheritRotatingFileHandler = logging.handlers.RotatingFileHandler 56 NoInheritRotatingFileHandler = logging.handlers.RotatingFileHandler
59 57
60 58
59 # Levels used for logging.
60 LEVELS = [logging.ERROR, logging.INFO, logging.DEBUG]
61
62
61 class CaptureLogs(object): 63 class CaptureLogs(object):
62 """Captures all the logs in a context.""" 64 """Captures all the logs in a context."""
63 def __init__(self, prefix, root=None): 65 def __init__(self, prefix, root=None):
64 handle, self._path = tempfile.mkstemp(prefix=prefix, suffix='.log') 66 handle, self._path = tempfile.mkstemp(prefix=prefix, suffix='.log')
65 os.close(handle) 67 os.close(handle)
66 self._handler = logging.FileHandler(self._path, 'w') 68 self._handler = logging.FileHandler(self._path, 'w')
67 self._handler.setLevel(logging.DEBUG) 69 self._handler.setLevel(logging.DEBUG)
68 formatter = UTCFormatter( 70 formatter = UTCFormatter(
69 '%(process)d %(asctime)s: %(levelname)-5s %(message)s') 71 '%(process)d %(asctime)s: %(levelname)-5s %(message)s')
70 self._handler.setFormatter(formatter) 72 self._handler.setFormatter(formatter)
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after
127 if getattr(log, 'stream', None) is sys.stderr: 129 if getattr(log, 'stream', None) is sys.stderr:
128 return log 130 return log
129 131
130 132
131 def prepare_logging(filename, root=None): 133 def prepare_logging(filename, root=None):
132 """Prepare logging for scripts. 134 """Prepare logging for scripts.
133 135
134 Makes it log in UTC all the time. Prepare a rotating file based log. 136 Makes it log in UTC all the time. Prepare a rotating file based log.
135 """ 137 """
136 assert not find_stderr(root) 138 assert not find_stderr(root)
139 # Other options are:
140 # '%(levelname)5s %(relativeCreated)6d %(module)15s(%(lineno)3d): '
141 # '%(message)s'
142 # or:
143 # '%(asctime)s %(levelname)-8s %(module)15s(%(lineno)3d): %(message)s'))
137 formatter = UTCFormatter( 144 formatter = UTCFormatter(
138 '%(process)d %(asctime)s: %(levelname)-5s %(message)s') 145 '%(process)d %(asctime)s: %(levelname)-5s %(message)s')
Vadim Sh. 2015/07/15 18:46:38 consider also https://chromium.googlesource.com/in
139 146
140 # It is a requirement that the root logger is set to DEBUG, so the messages 147 # It is a requirement that the root logger is set to DEBUG, so the messages
141 # are not lost. It defaults to WARNING otherwise. 148 # are not lost. It defaults to WARNING otherwise.
142 logger = root or logging.getLogger() 149 logger = root or logging.getLogger()
143 logger.setLevel(logging.DEBUG) 150 logger.setLevel(logging.DEBUG)
144 151
145 stderr = logging.StreamHandler() 152 stderr = logging.StreamHandler()
146 stderr.setFormatter(formatter) 153 stderr.setFormatter(formatter)
147 # Default to ERROR. 154 # Default to ERROR.
148 stderr.setLevel(logging.ERROR) 155 stderr.setLevel(logging.ERROR)
149 logger.addHandler(stderr) 156 logger.addHandler(stderr)
150 157
151 # Setup up logging to a constant file so we can debug issues where 158 # Setup up logging to a constant file so we can debug issues where
152 # the results aren't properly sent to the result URL. 159 # the results aren't properly sent to the result URL.
153 if filename: 160 if filename:
154 try: 161 try:
155 rotating_file = NoInheritRotatingFileHandler( 162 rotating_file = NoInheritRotatingFileHandler(
156 filename, maxBytes=10 * 1024 * 1024, backupCount=5) 163 filename, maxBytes=10 * 1024 * 1024, backupCount=5,
164 encoding='utf-8')
157 rotating_file.setLevel(logging.DEBUG) 165 rotating_file.setLevel(logging.DEBUG)
158 rotating_file.setFormatter(formatter) 166 rotating_file.setFormatter(formatter)
159 logger.addHandler(rotating_file) 167 logger.addHandler(rotating_file)
160 except Exception: 168 except Exception:
161 # May happen on cygwin. Do not crash. 169 # May happen on cygwin. Do not crash.
162 logging.exception('Failed to open %s', filename) 170 logging.exception('Failed to open %s', filename)
163 171
164 172
165 def set_console_level(level, root=None): 173 def set_console_level(level, root=None):
166 """Reset the console (stderr) logging level.""" 174 """Reset the console (stderr) logging level."""
167 handler = find_stderr(root) 175 handler = find_stderr(root)
168 handler.setLevel(level) 176 handler.setLevel(level)
177
178
179 class OptionParserWithLogging(optparse.OptionParser):
180 """Adds --verbose option."""
181
182 # Set to True to enable --log-file options.
183 enable_log_file = True
184
185 # Set in unit tests.
186 logger_root = None
187
188 def __init__(self, verbose=0, log_file=None, **kwargs):
189 kwargs.setdefault('description', sys.modules['__main__'].__doc__)
190 optparse.OptionParser.__init__(self, **kwargs)
191 self.group_logging = optparse.OptionGroup(self, 'Logging')
192 self.group_logging.add_option(
193 '-v', '--verbose',
194 action='count',
195 default=verbose,
196 help='Use multiple times to increase verbosity')
197 if self.enable_log_file:
198 self.group_logging.add_option(
199 '-l', '--log-file',
200 default=log_file,
201 help='The name of the file to store rotating log details')
202 self.group_logging.add_option(
203 '--no-log', action='store_const', const='', dest='log_file',
204 help='Disable log file')
205
206 def parse_args(self, *args, **kwargs):
207 # Make sure this group is always the last one.
208 self.add_option_group(self.group_logging)
209
210 options, args = optparse.OptionParser.parse_args(self, *args, **kwargs)
211 prepare_logging(self.enable_log_file and options.log_file, self.logger_root)
212 set_console_level(
213 LEVELS[min(len(LEVELS) - 1, options.verbose)], self.logger_root)
214 return options, args
215
216
217 class ArgumentParserWithLogging(argparse.ArgumentParser):
218 """Adds --verbose option."""
219
220 # Set to True to enable --log-file options.
221 enable_log_file = True
222
223 def __init__(self, verbose=0, log_file=None, **kwargs):
224 kwargs.setdefault('description', sys.modules['__main__'].__doc__)
225 kwargs.setdefault('conflict_handler', 'resolve')
226 self.__verbose = verbose
227 self.__log_file = log_file
228 super(ArgumentParserWithLogging, self).__init__(**kwargs)
229
230 def _add_logging_group(self):
231 group = self.add_argument_group('Logging')
232 group.add_argument(
233 '-v', '--verbose',
234 action='count',
235 default=self.__verbose,
236 help='Use multiple times to increase verbosity')
237 if self.enable_log_file:
238 group.add_argument(
239 '-l', '--log-file',
240 default=self.__log_file,
241 help='The name of the file to store rotating log details')
242 group.add_argument(
243 '--no-log', action='store_const', const='', dest='log_file',
244 help='Disable log file')
245
246 def parse_args(self, *args, **kwargs):
247 # Make sure this group is always the last one.
248 self._add_logging_group()
249
250 args = super(ArgumentParserWithLogging, self).parse_args(*args, **kwargs)
251 prepare_logging(self.enable_log_file and args.log_file, self.logger_root)
252 set_console_level(
253 LEVELS[min(len(LEVELS) - 1, args.verbose)], self.logger_root)
254 return args
OLDNEW
« no previous file with comments | « client/trace_inputs.py ('k') | client/utils/logging_utils_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698