OLD | NEW |
---|---|
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # | 2 # |
3 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 3 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
4 # Use of this source code is governed by a BSD-style license that can be | 4 # Use of this source code is governed by a BSD-style license that can be |
5 # found in the LICENSE file. | 5 # found in the LICENSE file. |
6 | 6 |
7 """Shutdown adb_logcat_monitor and print accumulated logs. | 7 """Shutdown adb_logcat_monitor and print accumulated logs. |
8 | 8 |
9 To test, call './adb_logcat_printer.py <base_dir>' where | 9 To test, call './adb_logcat_printer.py <base_dir>' where |
10 <base_dir> contains 'adb logcat -v threadtime' files named as | 10 <base_dir> contains 'adb logcat -v threadtime' files named as |
11 logcat_<deviceID>_<sequenceNum> | 11 logcat_<deviceID>_<sequenceNum> |
12 | 12 |
13 The script will print the files to out, and will combine multiple | 13 The script will print the files to out, and will combine multiple |
14 logcats from a single device if there is overlap. | 14 logcats from a single device if there is overlap. |
15 | 15 |
16 Additionally, if a <base_dir>/LOGCAT_MONITOR_PID exists, the script | 16 Additionally, if a <base_dir>/LOGCAT_MONITOR_PID exists, the script |
17 will attempt to terminate the contained PID by sending a SIGINT and | 17 will attempt to terminate the contained PID by sending a SIGINT and |
18 monitoring for the deletion of the aforementioned file. | 18 monitoring for the deletion of the aforementioned file. |
19 """ | 19 """ |
20 | 20 |
21 import cStringIO | 21 import cStringIO |
22 import logging | 22 import logging |
23 import optparse | |
23 import os | 24 import os |
24 import re | 25 import re |
25 import signal | 26 import signal |
26 import sys | 27 import sys |
27 import time | 28 import time |
28 | 29 |
29 | 30 |
30 # Set this to debug for more verbose output | 31 # Set this to debug for more verbose output |
31 LOG_LEVEL = logging.INFO | 32 LOG_LEVEL = logging.INFO |
32 | 33 |
(...skipping 12 matching lines...) Expand all Loading... | |
45 for cur_file, cur_file_lines in list_of_lists: | 46 for cur_file, cur_file_lines in list_of_lists: |
46 # Ignore files with just the logcat header | 47 # Ignore files with just the logcat header |
47 if len(cur_file_lines) < 2: | 48 if len(cur_file_lines) < 2: |
48 continue | 49 continue |
49 common_index = 0 | 50 common_index = 0 |
50 # Skip this step if list just has empty string | 51 # Skip this step if list just has empty string |
51 if len(cur_device_log) > 1: | 52 if len(cur_device_log) > 1: |
52 try: | 53 try: |
53 line = cur_device_log[-1] | 54 line = cur_device_log[-1] |
54 # Used to make sure we only splice on a timestamped line | 55 # Used to make sure we only splice on a timestamped line |
55 if re.match('^\d{2}-\d{2} \d{2}:\d{2}:\d{2}.\d{3} ', line): | 56 if re.match(r'^\d{2}-\d{2} \d{2}:\d{2}:\d{2}.\d{3} ', line): |
56 common_index = cur_file_lines.index(line) | 57 common_index = cur_file_lines.index(line) |
57 else: | 58 else: |
58 logger.warning('splice error - no timestamp in "%s"?', line.strip()) | 59 logger.warning('splice error - no timestamp in "%s"?', line.strip()) |
59 except ValueError: | 60 except ValueError: |
60 # The last line was valid but wasn't found in the next file | 61 # The last line was valid but wasn't found in the next file |
61 cur_device_log += ['***** POSSIBLE INCOMPLETE LOGCAT *****'] | 62 cur_device_log += ['***** POSSIBLE INCOMPLETE LOGCAT *****'] |
62 logger.info('Unable to splice %s. Incomplete logcat?', cur_file) | 63 logger.info('Unable to splice %s. Incomplete logcat?', cur_file) |
63 | 64 |
64 cur_device_log += ['*'*30 + ' %s' % cur_file] | 65 cur_device_log += ['*'*30 + ' %s' % cur_file] |
65 cur_device_log.extend(cur_file_lines[common_index:]) | 66 cur_device_log.extend(cur_file_lines[common_index:]) |
66 | 67 |
67 return cur_device_log | 68 return cur_device_log |
68 | 69 |
69 | 70 |
70 def FindLogFiles(base_dir): | 71 def FindLogFiles(base_dir): |
71 """Search a directory for logcat files. | 72 """Search a directory for logcat files. |
72 | 73 |
73 Args: | 74 Args: |
74 base_dir: directory to search | 75 base_dir: directory to search |
75 | 76 |
76 Returns: | 77 Returns: |
77 Mapping of device_id to a sorted list of file paths for a given device | 78 Mapping of device_id to a sorted list of file paths for a given device |
78 """ | 79 """ |
79 logcat_filter = re.compile('^logcat_(\w+)_(\d+)$') | 80 logcat_filter = re.compile(r'^logcat_(\w+)_(\d+)$') |
80 # list of tuples (<device_id>, <seq num>, <full file path>) | 81 # list of tuples (<device_id>, <seq num>, <full file path>) |
81 filtered_list = [] | 82 filtered_list = [] |
82 for cur_file in os.listdir(base_dir): | 83 for cur_file in os.listdir(base_dir): |
83 matcher = logcat_filter.match(cur_file) | 84 matcher = logcat_filter.match(cur_file) |
84 if matcher: | 85 if matcher: |
85 filtered_list += [(matcher.group(1), int(matcher.group(2)), | 86 filtered_list += [(matcher.group(1), int(matcher.group(2)), |
86 os.path.join(base_dir, cur_file))] | 87 os.path.join(base_dir, cur_file))] |
87 filtered_list.sort() | 88 filtered_list.sort() |
88 file_map = {} | 89 file_map = {} |
89 for device_id, _, cur_file in filtered_list: | 90 for device_id, _, cur_file in filtered_list: |
90 if not device_id in file_map: | 91 if device_id not in file_map: |
91 file_map[device_id] = [] | 92 file_map[device_id] = [] |
92 | 93 |
93 file_map[device_id] += [cur_file] | 94 file_map[device_id] += [cur_file] |
94 return file_map | 95 return file_map |
95 | 96 |
96 | 97 |
97 def GetDeviceLogs(log_filenames, logger): | 98 def GetDeviceLogs(log_filenames, logger): |
98 """Read log files, combine and format. | 99 """Read log files, combine and format. |
99 | 100 |
100 Args: | 101 Args: |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
142 logger.info('Waiting for logcat process to terminate.') | 143 logger.info('Waiting for logcat process to terminate.') |
143 i += 1 | 144 i += 1 |
144 if i >= 10: | 145 if i >= 10: |
145 logger.warning('Monitor pid did not terminate. Continuing anyway.') | 146 logger.warning('Monitor pid did not terminate. Continuing anyway.') |
146 return | 147 return |
147 | 148 |
148 except (ValueError, IOError, OSError): | 149 except (ValueError, IOError, OSError): |
149 logger.exception('Error signaling logcat monitor - continuing') | 150 logger.exception('Error signaling logcat monitor - continuing') |
150 | 151 |
151 | 152 |
152 def main(base_dir, output_file): | 153 def main(argv): |
154 parser = optparse.OptionParser(usage='Usage: %prog [options] <log dir>') | |
bulach
2013/11/14 19:50:04
nit: while at it, it seems a bit confusing to have
Isaac (away)
2013/11/21 11:11:34
--output-path is optional, while the extra arg is
| |
155 parser.add_option('--output-path', | |
156 help='Output file path (if unspecified, prints to stdout)') | |
157 options, args = parser.parse_args(argv) | |
158 if len(args) != 1: | |
159 parser.error('Wrong number of unparsed args') | |
160 base_dir = args[0] | |
161 if options.output_path: | |
162 output_file = open(options.output_path, 'w') | |
163 else: | |
164 output_file = sys.stdout | |
165 | |
153 log_stringio = cStringIO.StringIO() | 166 log_stringio = cStringIO.StringIO() |
154 logger = logging.getLogger('LogcatPrinter') | 167 logger = logging.getLogger('LogcatPrinter') |
155 logger.setLevel(LOG_LEVEL) | 168 logger.setLevel(LOG_LEVEL) |
156 sh = logging.StreamHandler(log_stringio) | 169 sh = logging.StreamHandler(log_stringio) |
157 sh.setFormatter(logging.Formatter('%(asctime)-2s %(levelname)-8s' | 170 sh.setFormatter(logging.Formatter('%(asctime)-2s %(levelname)-8s' |
158 ' %(message)s')) | 171 ' %(message)s')) |
159 logger.addHandler(sh) | 172 logger.addHandler(sh) |
160 | 173 |
161 try: | 174 try: |
162 # Wait at least 5 seconds after base_dir is created before printing. | 175 # Wait at least 5 seconds after base_dir is created before printing. |
(...skipping 26 matching lines...) Expand all Loading... | |
189 output_file.write(f.read()) | 202 output_file.write(f.read()) |
190 except: | 203 except: |
191 logger.exception('Unexpected exception') | 204 logger.exception('Unexpected exception') |
192 | 205 |
193 logger.info('Done.') | 206 logger.info('Done.') |
194 sh.flush() | 207 sh.flush() |
195 output_file.write('\nLogcat Printer Event Log\n') | 208 output_file.write('\nLogcat Printer Event Log\n') |
196 output_file.write(log_stringio.getvalue()) | 209 output_file.write(log_stringio.getvalue()) |
197 | 210 |
198 if __name__ == '__main__': | 211 if __name__ == '__main__': |
199 if len(sys.argv) == 1: | 212 sys.exit(main(sys.argv[1:])) |
200 print 'Usage: %s <base_dir>' % sys.argv[0] | |
201 sys.exit(1) | |
202 sys.exit(main(sys.argv[1], sys.stdout)) | |
OLD | NEW |