blob: 0ce1500b471b820255b3c53f632196a73e53cc71 [file] [log] [blame]
Sean Daguebcdba082013-03-12 15:14:16 -04001#!/usr/bin/env python
2# vim: tabstop=4 shiftwidth=4 softtabstop=4
3
4# Copyright 2013 IBM Corp.
5# All Rights Reserved.
6#
7# Licensed under the Apache License, Version 2.0 (the "License"); you may
8# not use this file except in compliance with the License. You may obtain
9# a copy of the License at
10#
11# http://www.apache.org/licenses/LICENSE-2.0
12#
13# Unless required by applicable law or agreed to in writing, software
14# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
16# License for the specific language governing permissions and limitations
17# under the License.
18
19import gzip
20import re
21import StringIO
22import sys
23import urllib2
24
Sean Dague70eef032013-03-20 13:41:15 -040025import pprint
26pp = pprint.PrettyPrinter()
27
28NOVA_TIMESTAMP = r"\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d\.\d\d\d"
29
30NOVA_REGEX = r"(?P<timestamp>%s) (?P<pid>\d+ )?(?P<level>(ERROR|TRACE)) " \
31 "(?P<module>[\w\.]+) (?P<msg>.*)" % (NOVA_TIMESTAMP)
32
33
34class StackTrace(object):
35 timestamp = None
36 pid = None
37 level = ""
38 module = ""
39 msg = ""
40
41 def __init__(self, timestamp=None, pid=None, level="", module="",
42 msg=""):
43 self.timestamp = timestamp
44 self.pid = pid
45 self.level = level
46 self.module = module
47 self.msg = msg
48
49 def append(self, msg):
50 self.msg = self.msg + msg
51
52 def is_same(self, data):
53 return (data['timestamp'] == self.timestamp and
54 data['level'] == self.level)
55
56 def not_none(self):
57 return self.timestamp is not None
58
59 def __str__(self):
60 buff = "<%s %s %s>\n" % (self.timestamp, self.level, self.module)
61 for line in self.msg.splitlines():
62 buff = buff + line + "\n"
63 return buff
64
Sean Daguebcdba082013-03-12 15:14:16 -040065
66def hunt_for_stacktrace(url):
67 """Return TRACE or ERROR lines out of logs."""
68 page = urllib2.urlopen(url)
69 buf = StringIO.StringIO(page.read())
70 f = gzip.GzipFile(fileobj=buf)
71 content = f.read()
Sean Dague70eef032013-03-20 13:41:15 -040072
73 traces = []
74 trace = StackTrace()
75 for line in content.splitlines():
76 m = re.match(NOVA_REGEX, line)
77 if m:
78 data = m.groupdict()
79 if trace.not_none() and trace.is_same(data):
80 trace.append(data['msg'] + "\n")
81 else:
82 trace = StackTrace(
83 timestamp=data.get('timestamp'),
84 pid=data.get('pid'),
85 level=data.get('level'),
86 module=data.get('module'),
87 msg=data.get('msg'))
88
89 else:
90 if trace.not_none():
91 traces.append(trace)
92 trace = StackTrace()
93
94 # once more at the end to pick up any stragglers
95 if trace.not_none():
96 traces.append(trace)
97
98 return traces
Sean Daguebcdba082013-03-12 15:14:16 -040099
100
101def log_url(url, log):
102 return "%s/%s" % (url, log)
103
104
105def collect_logs(url):
106 page = urllib2.urlopen(url)
107 content = page.read()
108 logs = re.findall('(screen-[\w-]+\.txt\.gz)</a>', content)
109 return logs
110
111
112def usage():
Dirk Mueller1db5db22013-06-23 20:21:32 +0200113 print("""
Sean Daguebcdba082013-03-12 15:14:16 -0400114Usage: find_stack_traces.py <logurl>
115
116Hunts for stack traces in a devstack run. Must provide it a base log url
117from a tempest devstack run. Should start with http and end with /logs/.
118
119Returns a report listing stack traces out of the various files where
120they are found.
Dirk Mueller1db5db22013-06-23 20:21:32 +0200121""")
Sean Daguebcdba082013-03-12 15:14:16 -0400122 sys.exit(0)
123
124
Sean Dague70eef032013-03-20 13:41:15 -0400125def print_stats(items, fname, verbose=False):
126 errors = len(filter(lambda x: x.level == "ERROR", items))
127 traces = len(filter(lambda x: x.level == "TRACE", items))
Dirk Mueller1db5db22013-06-23 20:21:32 +0200128 print("%d ERRORS found in %s" % (errors, fname))
129 print("%d TRACES found in %s" % (traces, fname))
Sean Dague70eef032013-03-20 13:41:15 -0400130
131 if verbose:
132 for item in items:
Dirk Mueller1db5db22013-06-23 20:21:32 +0200133 print(item)
134 print("\n\n")
Sean Dague70eef032013-03-20 13:41:15 -0400135
136
Sean Daguebcdba082013-03-12 15:14:16 -0400137def main():
138 if len(sys.argv) == 2:
139 url = sys.argv[1]
140 loglist = collect_logs(url)
141
142 # probably wrong base url
143 if not loglist:
144 usage()
145
146 for log in loglist:
147 logurl = log_url(url, log)
148 traces = hunt_for_stacktrace(logurl)
Sean Dague70eef032013-03-20 13:41:15 -0400149
Sean Daguebcdba082013-03-12 15:14:16 -0400150 if traces:
Sean Dague70eef032013-03-20 13:41:15 -0400151 print_stats(traces, log, verbose=True)
152
Sean Daguebcdba082013-03-12 15:14:16 -0400153 else:
154 usage()
155
156if __name__ == '__main__':
157 main()