OLD | NEW |
1 # Copyright 2015 The Chromium Authors. All rights reserved. | 1 # Copyright 2015 The Chromium Authors. All rights reserved. |
2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 # pylint: disable=unused-argument | 5 # pylint: disable=unused-argument |
6 | 6 |
7 import collections | 7 import collections |
8 import itertools | 8 import itertools |
9 import logging | 9 import logging |
10 import subprocess | 10 import subprocess |
11 import tempfile | 11 import tempfile |
12 import time | 12 import time |
13 import re | 13 import re |
14 | 14 |
15 from pylib.device import adb_wrapper | 15 from pylib.device import adb_wrapper |
16 from pylib.device import decorators | 16 from pylib.device import decorators |
17 from pylib.device import device_errors | 17 from pylib.device import device_errors |
18 | 18 |
19 | 19 |
20 class LogcatMonitor(object): | 20 class LogcatMonitor(object): |
21 | 21 |
22 # Format: <DATE> <TIME> <PID> <TID> <LEVEL> <COMPONENT>: <MESSAGE> | 22 _THREADTIME_RE_FORMAT = ( |
23 _THREADTIME_RE_FORMAT = r'\S* +\S* +(%s) +(%s) +(%s) +(%s): +(%s)$' | 23 r'(?P<date>\S*) +(?P<time>\S*) +(?P<proc_id>%s) +(?P<thread_id>%s) +' |
| 24 r'(?P<log_level>%s) +(?P<component>%s) *: +(?P<message>%s)$') |
24 | 25 |
25 def __init__(self, adb, clear=True, filter_specs=None): | 26 def __init__(self, adb, clear=True, filter_specs=None): |
26 """Create a LogcatMonitor instance. | 27 """Create a LogcatMonitor instance. |
27 | 28 |
28 Args: | 29 Args: |
29 adb: An instance of adb_wrapper.AdbWrapper. | 30 adb: An instance of adb_wrapper.AdbWrapper. |
30 clear: If True, clear the logcat when monitoring starts. | 31 clear: If True, clear the logcat when monitoring starts. |
31 filter_specs: An optional list of '<tag>[:priority]' strings. | 32 filter_specs: An optional list of '<tag>[:priority]' strings. |
32 """ | 33 """ |
33 if isinstance(adb, adb_wrapper.AdbWrapper): | 34 if isinstance(adb, adb_wrapper.AdbWrapper): |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
90 """Finds all lines in the logcat that match the provided constraints. | 91 """Finds all lines in the logcat that match the provided constraints. |
91 | 92 |
92 Args: | 93 Args: |
93 message_regex: The regular expression that the <message> section must | 94 message_regex: The regular expression that the <message> section must |
94 match. | 95 match. |
95 proc_id: The process ID to match. If None, matches any process ID. | 96 proc_id: The process ID to match. If None, matches any process ID. |
96 thread_id: The thread ID to match. If None, matches any thread ID. | 97 thread_id: The thread ID to match. If None, matches any thread ID. |
97 log_level: The log level to match. If None, matches any log level. | 98 log_level: The log level to match. If None, matches any log level. |
98 component: The component to match. If None, matches any component. | 99 component: The component to match. If None, matches any component. |
99 | 100 |
100 Returns: | 101 Yields: |
101 An iterable containing objects with five attributes: | 102 A match object for each matching line in the logcat. The match object |
102 |proc_id|: the process ID | 103 will always contain, in addition to groups defined in |message_regex|, |
103 |thread_id|: the thread ID | 104 the following named groups: 'date', 'time', 'proc_id', 'thread_id', |
104 |log_level|: the log level | 105 'log_level', 'component', and 'message'. |
105 |component|: the component | |
106 |message|: the logcat message | |
107 """ | 106 """ |
108 LogcatLine = collections.namedtuple( | |
109 'LogcatLine', | |
110 ['proc_id', 'thread_id', 'log_level', 'component', 'message']) | |
111 | |
112 if proc_id is None: | 107 if proc_id is None: |
113 proc_id = r'\d+' | 108 proc_id = r'\d+' |
114 if thread_id is None: | 109 if thread_id is None: |
115 thread_id = r'\d+' | 110 thread_id = r'\d+' |
116 if log_level is None: | 111 if log_level is None: |
117 log_level = r'[VDIWEF]' | 112 log_level = r'[VDIWEF]' |
118 if component is None: | 113 if component is None: |
119 component = r'[^\s:]+' | 114 component = r'[^\s:]+' |
120 threadtime_re = re.compile( | 115 threadtime_re = re.compile( |
121 type(self)._THREADTIME_RE_FORMAT % ( | 116 type(self)._THREADTIME_RE_FORMAT % ( |
122 proc_id, thread_id, log_level, component, message_regex)) | 117 proc_id, thread_id, log_level, component, message_regex)) |
123 | 118 |
124 regexed_lines = ( | 119 for line in self._adb.Logcat(dump=True, logcat_format='threadtime'): |
125 re.match(threadtime_re, l) | 120 m = re.match(threadtime_re, line) |
126 for l in self._adb.Logcat(dump=True, logcat_format='threadtime')) | 121 if m: |
127 only_matches = (m for m in regexed_lines if m) | 122 yield m |
128 return (LogcatLine(*m.group(1, 2, 3, 4, 5)) for m in only_matches) | |
129 | 123 |
130 def Start(self): | 124 def Start(self): |
131 """Starts the logcat monitor. | 125 """Starts the logcat monitor. |
132 | 126 |
133 Clears the logcat if |clear| was set in |__init__|. | 127 Clears the logcat if |clear| was set in |__init__|. |
134 """ | 128 """ |
135 if self._clear: | 129 if self._clear: |
136 self._adb.Logcat(clear=True) | 130 self._adb.Logcat(clear=True) |
137 | 131 |
138 def __enter__(self): | 132 def __enter__(self): |
139 """Starts the logcat monitor.""" | 133 """Starts the logcat monitor.""" |
140 self.Start() | 134 self.Start() |
141 return self | 135 return self |
142 | 136 |
143 def __exit__(self, exc_type, exc_val, exc_tb): | 137 def __exit__(self, exc_type, exc_val, exc_tb): |
144 """Stops the logcat monitor.""" | 138 """Stops the logcat monitor.""" |
145 pass | 139 pass |
OLD | NEW |