k8s.io/test-infra@v0.0.0-20240520184403-27c6b4c223d8/experiment/parse_build_log.py (about)

     1  #!/usr/bin/python3
     2  
     3  # Copyright 2018 The Kubernetes Authors.
     4  #
     5  # Licensed under the Apache License, Version 2.0 (the "License");
     6  # you may not use this file except in compliance with the License.
     7  # You may obtain a copy of the License at
     8  #
     9  #     http://www.apache.org/licenses/LICENSE-2.0
    10  #
    11  # Unless required by applicable law or agreed to in writing, software
    12  # distributed under the License is distributed on an "AS IS" BASIS,
    13  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14  # See the License for the specific language governing permissions and
    15  # limitations under the License.
    16  
    17  """Parser for e2e test logs.
    18  
    19  Useful for finding which tests overlapped with a certain event.
    20  """
    21  
    22  import argparse
    23  import datetime
    24  import re
    25  
    26  
    27  _LINE_RE = re.compile(r'^[IWE]111[45] \d\d:\d\d:\d\d\.\d\d\d\] ?(.*)')
    28  _DATE_FORMAT = '%Y-%m-%d %H:%M:%S'
    29  _CURRENT_YEAR = datetime.datetime.utcnow().year
    30  
    31  
    32  class TestOutput:
    33      def __init__(self):
    34          self._lines = []
    35          self._start = None
    36          self._end = None
    37          self._it = None
    38  
    39      def append(self, line):
    40          self._lines.append(line)
    41          try:
    42              timestamp = datetime.datetime.strptime(line[:19], '%b %d %H:%M:%S.%f').replace(
    43                  year=_CURRENT_YEAR)
    44          except:  # pylint: disable=bare-except
    45              pass
    46          else:
    47              if not self._start:
    48                  self._start = timestamp
    49              self._end = timestamp
    50          if line.startswith('[It] '):
    51              self._it = line
    52          if line.startswith('[BeforeEach] ') and not self._it:
    53              self._it = line
    54  
    55      def overlaps(self, after, before):
    56          if self._end and after and self._end < after:
    57              return False
    58          if self._start and before and self._start > before:
    59              return False
    60          return True
    61  
    62      def __len__(self):
    63          return len(self._lines)
    64  
    65      def __str__(self):
    66          if not self._lines:
    67              return '<empty>'
    68          return 'Test %s->%s (%5d lines) %s' % (
    69              self._start, self._end, len(self), self._it if self._it else '')
    70  
    71  
    72  def _get_tests(log):
    73      current_test = TestOutput()
    74      for line in log:
    75          line = line.rstrip()
    76          match = _LINE_RE.match(line)
    77          if not match:
    78              raise Exception('line %s does not match' % line)
    79          if '------------------------------' in line:
    80              ended_test = current_test
    81              current_test = TestOutput()
    82              if len(ended_test) <= 1:
    83                  continue
    84              yield ended_test
    85          else:
    86              current_test.append(match.group(1))
    87      yield current_test
    88  
    89  
    90  def main():
    91      parser = argparse.ArgumentParser(description=__doc__)
    92      parser.add_argument('--log_year', default=_CURRENT_YEAR,
    93                          help=('Year in which the log was created. '
    94                                'Needed because the year is omitted in the log.'))
    95      parser.add_argument('--after',
    96                          help=('Show tests which ended at or after this time '
    97                                '(format: %s).' % _DATE_FORMAT.replace('%', '%%')))
    98      parser.add_argument('--before',
    99                          help=('Show tests which started at or before this time '
   100                                '(format: %s).' % _DATE_FORMAT.replace('%', '%%')))
   101      parser.add_argument('file')
   102  
   103      args = parser.parse_args()
   104      after = datetime.datetime.strptime(args.after, _DATE_FORMAT) if args.after else None
   105      before = datetime.datetime.strptime(args.before, _DATE_FORMAT) if args.before else None
   106      if after and before and after.year != before.year:
   107          raise Exception('Logs spanning year boundary are not supported.')
   108      if not args.log_year and (after or before):
   109          year = after.year if after else before.year
   110          if year != _CURRENT_YEAR:
   111              raise Exception('Please explicitly specify the year in which the log was created.')
   112      with open(args.file) as log:
   113          for test in _get_tests(log):
   114              if test.overlaps(after, before):
   115                  print(str(test))
   116  
   117  
   118  if __name__ == '__main__':
   119      main()