| #!/usr/bin/python |
| # Copyright 2015 The Chromium OS Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| import re |
| import time |
| |
| class ChaosLogAnalyzer(object): |
| """ Class to analyze the debug logs from a chaos test . """ |
| |
| MESSAGE_LOG_ATTEMPT_START_RE = "Connection attempt %d" |
| NET_LOG_ATTEMPT_START_RE = "%s.*PushProfileInternal finished" |
| NET_LOG_ATTEMPT_END_RE = ".*PopProfileInternal finished" |
| LOG_TIMESTAMP_DATE_RE = "[0-9]{4}-[0-9]{2}-[0-9]{2}" |
| LOG_TIMESTAMP_TIME_RE = "[0-9]{2}:[0-9]{2}:[0-9]{2}" |
| LOG_TIMESTAMP_TIMESTAMP_RE = ( |
| LOG_TIMESTAMP_DATE_RE + "T" + LOG_TIMESTAMP_TIME_RE) |
| LOG_TIMESTAMP_FORMAT = "%Y-%m-%dT%H:%M:%S" |
| LOG_ERROR_RE = ".*ERROR:.*" |
| |
| def __init__(self, message_log, net_log, logger): |
| self._net_log = net_log |
| self._message_log = message_log |
| self._log = logger |
| |
| def _find_line_in_log(self, search_pattern, log_file): |
| search_regex = re.compile(search_pattern) |
| log_file.seek(0) |
| for line in log_file: |
| if search_regex.search(line): |
| return line |
| |
| def _find_line_in_message_log(self, search_pattern): |
| return self._find_line_in_log(search_pattern, self._message_log) |
| |
| def _find_line_in_net_log(self, search_pattern): |
| return self._find_line_in_log(search_pattern, self._net_log) |
| |
| def _extract_timestamp_from_line(self, line): |
| timestamp_re = re.compile(self.LOG_TIMESTAMP_TIMESTAMP_RE) |
| timestamp_string = timestamp_re.search(line).group(0) |
| timestamp = time.strptime(timestamp_string, self.LOG_TIMESTAMP_FORMAT) |
| return timestamp |
| |
| def _extract_attempt_timestamp(self, attempt_num): |
| search_pattern = self.MESSAGE_LOG_ATTEMPT_START_RE % (int(attempt_num)) |
| line = self._find_line_in_message_log(search_pattern) |
| return self._extract_timestamp_from_line(line) |
| |
| def _extract_log_lines(self, log_file, start_pattern=None, end_pattern=None, |
| stop_pattern=None, match_pattern=None): |
| start_re = None |
| stop_re = None |
| end_re = None |
| match_re = None |
| start_copy = False |
| lines = "" |
| if start_pattern: |
| start_re = re.compile(start_pattern) |
| else: |
| # If there is no specific start pattern, start copying from |
| # begining of the file. |
| start_copy = True |
| if stop_pattern: |
| stop_re = re.compile(stop_pattern) |
| if end_pattern: |
| end_re = re.compile(end_pattern) |
| if match_pattern: |
| match_re = re.compile(match_pattern) |
| log_file.seek(0) |
| for line in log_file: |
| if ((start_copy == False) and (start_re and start_re.search(line))): |
| start_copy = True |
| if ((start_copy == True) and (stop_re and stop_re.search(line))): |
| break |
| if ((start_copy == True) and |
| ((not match_re) or (match_re and match_re.search(line)))): |
| lines += line |
| if ((start_copy == True) and (end_re and end_re.search(line))): |
| break |
| return lines |
| |
| def _extract_message_log_lines(self, attempt_num): |
| self._log.log_start_section("Extracted Messages Log") |
| start = self.MESSAGE_LOG_ATTEMPT_START_RE % (int(attempt_num)) |
| stop = self.MESSAGE_LOG_ATTEMPT_START_RE % (int(attempt_num) + 1) |
| lines = self._extract_log_lines( |
| self._message_log, start_pattern=start, stop_pattern=stop) |
| self._log.log_to_output_file(lines) |
| |
| def _extract_net_log_lines(self, timestamp): |
| self._log.log_start_section("Extracted Net Log") |
| start = self.NET_LOG_ATTEMPT_START_RE % \ |
| (time.strftime(self.LOG_TIMESTAMP_FORMAT, timestamp)) |
| end = self.NET_LOG_ATTEMPT_END_RE |
| lines = self._extract_log_lines( |
| self._net_log, start_pattern=start, end_pattern=end) |
| # Let's go back 1 sec and search again |
| if lines == "": |
| timestamp_secs = time.mktime(timestamp) |
| new_timestamp = time.localtime(timestamp_secs - 1) |
| start = self.NET_LOG_ATTEMPT_START_RE % \ |
| (time.strftime(self.LOG_TIMESTAMP_FORMAT, new_timestamp)) |
| lines = self._extract_log_lines( |
| self._net_log, start_pattern=start, end_pattern=end) |
| self._log.log_to_output_file(lines) |
| |
| def analyze(self, attempt_num): |
| """ |
| Extracts the snippet of logs for given attempt from the Chaos log file. |
| |
| @param attempt_num: Attempt number for which the logs are to be |
| extracted. |
| |
| """ |
| timestamp = self._extract_attempt_timestamp(attempt_num) |
| print "Attempt started at: " + time.asctime(timestamp) |
| self._extract_message_log_lines(attempt_num) |
| self._extract_net_log_lines(timestamp) |