blob: 2ba8b164e536d47ae8837e57d235c92c7e5bf770 [file] [log] [blame]
Sean Daguebcdba082013-03-12 15:14:16 -04001#!/usr/bin/env python
Sean Daguebcdba082013-03-12 15:14:16 -04002
3# Copyright 2013 IBM Corp.
4# All Rights Reserved.
5#
6# Licensed under the Apache License, Version 2.0 (the "License"); you may
7# not use this file except in compliance with the License. You may obtain
8# a copy of the License at
9#
10# http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15# License for the specific language governing permissions and limitations
16# under the License.
17
18import gzip
Matthew Treinish96e9e882014-06-09 18:37:19 -040019import pprint
Sean Daguebcdba082013-03-12 15:14:16 -040020import re
Masayuki Igawa134d9f72017-02-10 18:05:26 +090021import sys
22
Harshada Mangesh Kakadb3ecf652015-12-22 09:24:26 -080023import six
Yatin Kumbhare2e2c83a2016-05-30 22:45:58 +053024import six.moves.urllib.request as urlreq
Sean Daguebcdba082013-03-12 15:14:16 -040025
Matthew Treinish96e9e882014-06-09 18:37:19 -040026
Sean Dague70eef032013-03-20 13:41:15 -040027pp = pprint.PrettyPrinter()
28
29NOVA_TIMESTAMP = r"\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d\.\d\d\d"
30
31NOVA_REGEX = r"(?P<timestamp>%s) (?P<pid>\d+ )?(?P<level>(ERROR|TRACE)) " \
32 "(?P<module>[\w\.]+) (?P<msg>.*)" % (NOVA_TIMESTAMP)
33
34
35class StackTrace(object):
36 timestamp = None
37 pid = None
38 level = ""
39 module = ""
40 msg = ""
41
42 def __init__(self, timestamp=None, pid=None, level="", module="",
43 msg=""):
44 self.timestamp = timestamp
45 self.pid = pid
46 self.level = level
47 self.module = module
48 self.msg = msg
49
50 def append(self, msg):
51 self.msg = self.msg + msg
52
53 def is_same(self, data):
54 return (data['timestamp'] == self.timestamp and
55 data['level'] == self.level)
56
57 def not_none(self):
58 return self.timestamp is not None
59
60 def __str__(self):
61 buff = "<%s %s %s>\n" % (self.timestamp, self.level, self.module)
62 for line in self.msg.splitlines():
63 buff = buff + line + "\n"
64 return buff
65
Sean Daguebcdba082013-03-12 15:14:16 -040066
67def hunt_for_stacktrace(url):
68 """Return TRACE or ERROR lines out of logs."""
Yatin Kumbhare2e2c83a2016-05-30 22:45:58 +053069 req = urlreq.Request(url)
David Kranz9e3c7172013-10-09 21:45:31 -040070 req.add_header('Accept-Encoding', 'gzip')
Yatin Kumbhare2e2c83a2016-05-30 22:45:58 +053071 page = urlreq.urlopen(req)
Harshada Mangesh Kakadb3ecf652015-12-22 09:24:26 -080072 buf = six.StringIO(page.read())
Sean Daguebcdba082013-03-12 15:14:16 -040073 f = gzip.GzipFile(fileobj=buf)
74 content = f.read()
Sean Dague70eef032013-03-20 13:41:15 -040075
76 traces = []
77 trace = StackTrace()
78 for line in content.splitlines():
79 m = re.match(NOVA_REGEX, line)
80 if m:
81 data = m.groupdict()
82 if trace.not_none() and trace.is_same(data):
83 trace.append(data['msg'] + "\n")
84 else:
85 trace = StackTrace(
86 timestamp=data.get('timestamp'),
87 pid=data.get('pid'),
88 level=data.get('level'),
89 module=data.get('module'),
90 msg=data.get('msg'))
91
92 else:
93 if trace.not_none():
94 traces.append(trace)
95 trace = StackTrace()
96
97 # once more at the end to pick up any stragglers
98 if trace.not_none():
99 traces.append(trace)
100
101 return traces
Sean Daguebcdba082013-03-12 15:14:16 -0400102
103
104def log_url(url, log):
105 return "%s/%s" % (url, log)
106
107
108def collect_logs(url):
Yatin Kumbhare2e2c83a2016-05-30 22:45:58 +0530109 page = urlreq.urlopen(url)
Sean Daguebcdba082013-03-12 15:14:16 -0400110 content = page.read()
111 logs = re.findall('(screen-[\w-]+\.txt\.gz)</a>', content)
112 return logs
113
114
115def usage():
Dirk Mueller1db5db22013-06-23 20:21:32 +0200116 print("""
Sean Daguebcdba082013-03-12 15:14:16 -0400117Usage: find_stack_traces.py <logurl>
118
119Hunts for stack traces in a devstack run. Must provide it a base log url
120from a tempest devstack run. Should start with http and end with /logs/.
121
122Returns a report listing stack traces out of the various files where
123they are found.
Dirk Mueller1db5db22013-06-23 20:21:32 +0200124""")
Sean Daguebcdba082013-03-12 15:14:16 -0400125 sys.exit(0)
126
127
Sean Dague70eef032013-03-20 13:41:15 -0400128def print_stats(items, fname, verbose=False):
129 errors = len(filter(lambda x: x.level == "ERROR", items))
130 traces = len(filter(lambda x: x.level == "TRACE", items))
Dirk Mueller1db5db22013-06-23 20:21:32 +0200131 print("%d ERRORS found in %s" % (errors, fname))
132 print("%d TRACES found in %s" % (traces, fname))
Sean Dague70eef032013-03-20 13:41:15 -0400133
134 if verbose:
135 for item in items:
Dirk Mueller1db5db22013-06-23 20:21:32 +0200136 print(item)
137 print("\n\n")
Sean Dague70eef032013-03-20 13:41:15 -0400138
139
Sean Daguebcdba082013-03-12 15:14:16 -0400140def main():
141 if len(sys.argv) == 2:
142 url = sys.argv[1]
143 loglist = collect_logs(url)
144
145 # probably wrong base url
146 if not loglist:
147 usage()
148
149 for log in loglist:
150 logurl = log_url(url, log)
151 traces = hunt_for_stacktrace(logurl)
Sean Dague70eef032013-03-20 13:41:15 -0400152
Sean Daguebcdba082013-03-12 15:14:16 -0400153 if traces:
Sean Dague70eef032013-03-20 13:41:15 -0400154 print_stats(traces, log, verbose=True)
155
Sean Daguebcdba082013-03-12 15:14:16 -0400156 else:
157 usage()
158
159if __name__ == '__main__':
160 main()