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