1#!/usr/bin/python3 2# Copyright 2015 The Chromium OS Authors. All rights reserved. 3# Use of this source code is governed by a BSD-style license that can be 4# found in the LICENSE file. 5 6from __future__ import print_function 7 8import re 9import time 10 11class ChaosLogAnalyzer(object): 12 """ Class to analyze the debug logs from a chaos test . """ 13 14 MESSAGE_LOG_ATTEMPT_START_RE = "Connection attempt %d" 15 NET_LOG_ATTEMPT_START_RE = "%s.*PushProfileInternal finished" 16 NET_LOG_ATTEMPT_END_RE = ".*PopProfileInternal finished" 17 LOG_TIMESTAMP_DATE_RE = "[0-9]{4}-[0-9]{2}-[0-9]{2}" 18 LOG_TIMESTAMP_TIME_RE = "[0-9]{2}:[0-9]{2}:[0-9]{2}" 19 LOG_TIMESTAMP_TIMESTAMP_RE = ( 20 LOG_TIMESTAMP_DATE_RE + "T" + LOG_TIMESTAMP_TIME_RE) 21 LOG_TIMESTAMP_FORMAT = "%Y-%m-%dT%H:%M:%S" 22 LOG_ERROR_RE = ".*ERROR:.*" 23 24 def __init__(self, message_log, net_log, logger): 25 self._net_log = net_log 26 self._message_log = message_log 27 self._log = logger 28 29 def _find_line_in_log(self, search_pattern, log_file): 30 search_regex = re.compile(search_pattern) 31 log_file.seek(0) 32 for line in log_file: 33 if search_regex.search(line): 34 return line 35 36 def _find_line_in_message_log(self, search_pattern): 37 return self._find_line_in_log(search_pattern, self._message_log) 38 39 def _find_line_in_net_log(self, search_pattern): 40 return self._find_line_in_log(search_pattern, self._net_log) 41 42 def _extract_timestamp_from_line(self, line): 43 timestamp_re = re.compile(self.LOG_TIMESTAMP_TIMESTAMP_RE) 44 timestamp_string = timestamp_re.search(line).group(0) 45 timestamp = time.strptime(timestamp_string, self.LOG_TIMESTAMP_FORMAT) 46 return timestamp 47 48 def _extract_attempt_timestamp(self, attempt_num): 49 search_pattern = self.MESSAGE_LOG_ATTEMPT_START_RE % (int(attempt_num)) 50 line = self._find_line_in_message_log(search_pattern) 51 return self._extract_timestamp_from_line(line) 52 53 def _extract_log_lines(self, log_file, start_pattern=None, end_pattern=None, 54 stop_pattern=None, match_pattern=None): 55 start_re = None 56 stop_re = None 57 end_re = None 58 match_re = None 59 start_copy = False 60 lines = "" 61 if start_pattern: 62 start_re = re.compile(start_pattern) 63 else: 64 # If there is no specific start pattern, start copying from 65 # begining of the file. 66 start_copy = True 67 if stop_pattern: 68 stop_re = re.compile(stop_pattern) 69 if end_pattern: 70 end_re = re.compile(end_pattern) 71 if match_pattern: 72 match_re = re.compile(match_pattern) 73 log_file.seek(0) 74 for line in log_file: 75 if ((start_copy == False) and (start_re and start_re.search(line))): 76 start_copy = True 77 if ((start_copy == True) and (stop_re and stop_re.search(line))): 78 break 79 if ((start_copy == True) and 80 ((not match_re) or (match_re and match_re.search(line)))): 81 lines += line 82 if ((start_copy == True) and (end_re and end_re.search(line))): 83 break 84 return lines 85 86 def _extract_message_log_lines(self, attempt_num): 87 self._log.log_start_section("Extracted Messages Log") 88 start = self.MESSAGE_LOG_ATTEMPT_START_RE % (int(attempt_num)) 89 stop = self.MESSAGE_LOG_ATTEMPT_START_RE % (int(attempt_num) + 1) 90 lines = self._extract_log_lines( 91 self._message_log, start_pattern=start, stop_pattern=stop) 92 self._log.log_to_output_file(lines) 93 94 def _extract_net_log_lines(self, timestamp): 95 self._log.log_start_section("Extracted Net Log") 96 start = self.NET_LOG_ATTEMPT_START_RE % \ 97 (time.strftime(self.LOG_TIMESTAMP_FORMAT, timestamp)) 98 end = self.NET_LOG_ATTEMPT_END_RE 99 lines = self._extract_log_lines( 100 self._net_log, start_pattern=start, end_pattern=end) 101 # Let's go back 1 sec and search again 102 if lines == "": 103 timestamp_secs = time.mktime(timestamp) 104 new_timestamp = time.localtime(timestamp_secs - 1) 105 start = self.NET_LOG_ATTEMPT_START_RE % \ 106 (time.strftime(self.LOG_TIMESTAMP_FORMAT, new_timestamp)) 107 lines = self._extract_log_lines( 108 self._net_log, start_pattern=start, end_pattern=end) 109 self._log.log_to_output_file(lines) 110 111 def analyze(self, attempt_num): 112 """ 113 Extracts the snippet of logs for given attempt from the Chaos log file. 114 115 @param attempt_num: Attempt number for which the logs are to be 116 extracted. 117 118 """ 119 timestamp = self._extract_attempt_timestamp(attempt_num) 120 print("Attempt started at: " + time.asctime(timestamp)) 121 self._extract_message_log_lines(attempt_num) 122 self._extract_net_log_lines(timestamp) 123