blob: c9059766991bc23d91306ea8d4c08cb926a567eb [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
19import re
20import StringIO
21import sys
22import urllib2
23
Sean Dague70eef032013-03-20 13:41:15 -040024import pprint
25pp = pprint.PrettyPrinter()
26
27NOVA_TIMESTAMP = r"\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d\.\d\d\d"
28
29NOVA_REGEX = r"(?P<timestamp>%s) (?P<pid>\d+ )?(?P<level>(ERROR|TRACE)) " \
30 "(?P<module>[\w\.]+) (?P<msg>.*)" % (NOVA_TIMESTAMP)
31
32
33class StackTrace(object):
34 timestamp = None
35 pid = None
36 level = ""
37 module = ""
38 msg = ""
39
40 def __init__(self, timestamp=None, pid=None, level="", module="",
41 msg=""):
42 self.timestamp = timestamp
43 self.pid = pid
44 self.level = level
45 self.module = module
46 self.msg = msg
47
48 def append(self, msg):
49 self.msg = self.msg + msg
50
51 def is_same(self, data):
52 return (data['timestamp'] == self.timestamp and
53 data['level'] == self.level)
54
55 def not_none(self):
56 return self.timestamp is not None
57
58 def __str__(self):
59 buff = "<%s %s %s>\n" % (self.timestamp, self.level, self.module)
60 for line in self.msg.splitlines():
61 buff = buff + line + "\n"
62 return buff
63
Sean Daguebcdba082013-03-12 15:14:16 -040064
65def hunt_for_stacktrace(url):
66 """Return TRACE or ERROR lines out of logs."""
David Kranz9e3c7172013-10-09 21:45:31 -040067 req = urllib2.Request(url)
68 req.add_header('Accept-Encoding', 'gzip')
69 page = urllib2.urlopen(req)
Sean Daguebcdba082013-03-12 15:14:16 -040070 buf = StringIO.StringIO(page.read())
71 f = gzip.GzipFile(fileobj=buf)
72 content = f.read()
Sean Dague70eef032013-03-20 13:41:15 -040073
74 traces = []
75 trace = StackTrace()
76 for line in content.splitlines():
77 m = re.match(NOVA_REGEX, line)
78 if m:
79 data = m.groupdict()
80 if trace.not_none() and trace.is_same(data):
81 trace.append(data['msg'] + "\n")
82 else:
83 trace = StackTrace(
84 timestamp=data.get('timestamp'),
85 pid=data.get('pid'),
86 level=data.get('level'),
87 module=data.get('module'),
88 msg=data.get('msg'))
89
90 else:
91 if trace.not_none():
92 traces.append(trace)
93 trace = StackTrace()
94
95 # once more at the end to pick up any stragglers
96 if trace.not_none():
97 traces.append(trace)
98
99 return traces
Sean Daguebcdba082013-03-12 15:14:16 -0400100
101
102def log_url(url, log):
103 return "%s/%s" % (url, log)
104
105
106def collect_logs(url):
107 page = urllib2.urlopen(url)
108 content = page.read()
109 logs = re.findall('(screen-[\w-]+\.txt\.gz)</a>', content)
110 return logs
111
112
113def usage():
Dirk Mueller1db5db22013-06-23 20:21:32 +0200114 print("""
Sean Daguebcdba082013-03-12 15:14:16 -0400115Usage: find_stack_traces.py <logurl>
116
117Hunts for stack traces in a devstack run. Must provide it a base log url
118from a tempest devstack run. Should start with http and end with /logs/.
119
120Returns a report listing stack traces out of the various files where
121they are found.
Dirk Mueller1db5db22013-06-23 20:21:32 +0200122""")
Sean Daguebcdba082013-03-12 15:14:16 -0400123 sys.exit(0)
124
125
Sean Dague70eef032013-03-20 13:41:15 -0400126def print_stats(items, fname, verbose=False):
127 errors = len(filter(lambda x: x.level == "ERROR", items))
128 traces = len(filter(lambda x: x.level == "TRACE", items))
Dirk Mueller1db5db22013-06-23 20:21:32 +0200129 print("%d ERRORS found in %s" % (errors, fname))
130 print("%d TRACES found in %s" % (traces, fname))
Sean Dague70eef032013-03-20 13:41:15 -0400131
132 if verbose:
133 for item in items:
Dirk Mueller1db5db22013-06-23 20:21:32 +0200134 print(item)
135 print("\n\n")
Sean Dague70eef032013-03-20 13:41:15 -0400136
137
Sean Daguebcdba082013-03-12 15:14:16 -0400138def main():
139 if len(sys.argv) == 2:
140 url = sys.argv[1]
141 loglist = collect_logs(url)
142
143 # probably wrong base url
144 if not loglist:
145 usage()
146
147 for log in loglist:
148 logurl = log_url(url, log)
149 traces = hunt_for_stacktrace(logurl)
Sean Dague70eef032013-03-20 13:41:15 -0400150
Sean Daguebcdba082013-03-12 15:14:16 -0400151 if traces:
Sean Dague70eef032013-03-20 13:41:15 -0400152 print_stats(traces, log, verbose=True)
153
Sean Daguebcdba082013-03-12 15:14:16 -0400154 else:
155 usage()
156
157if __name__ == '__main__':
158 main()