~lifeless/python-oops-tools/bug-881400

« back to all changes in this revision

Viewing changes to src/oopstools/oops/oopsgroup.py

  • Committer: Robert Collins
  • Date: 2011-10-13 20:18:51 UTC
  • Revision ID: robertc@robertcollins.net-20111013201851-ym8jmdhoeol3p83s
Export of cruft-deleted tree.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright 2005-2011 Canonical Ltd.  All rights reserved.
 
2
#
 
3
# This program is free software: you can redistribute it and/or modify
 
4
# it under the terms of the GNU Affero General Public License as published by
 
5
# the Free Software Foundation, either version 3 of the License, or
 
6
# (at your option) any later version.
 
7
#
 
8
# This program is distributed in the hope that it will be useful,
 
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
# GNU Affero General Public License for more details.
 
12
#
 
13
# You should have received a copy of the GNU Affero General Public License
 
14
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
15
 
 
16
 
 
17
import cgi
 
18
import urllib
 
19
from django.conf import settings
 
20
 
 
21
class OopsGroup:
 
22
    """Group of OOPS with a particular exception type and value"""
 
23
    def __init__(self, etype, evalue):
 
24
        self.etype = etype
 
25
        self.evalue = evalue
 
26
        self.urls = {}
 
27
        self.pageids = {}
 
28
        self.count = 0
 
29
        self.local_referrers = 0
 
30
        self.bots = 0
 
31
        self.bug = None
 
32
        self.request_methods = {"POST": 0, "GET": 0, "Other": 0}
 
33
        self.referrer_urls = {}
 
34
 
 
35
    def addOops(self, oops):
 
36
        # Ideally the assert would be done on etype and evalue. Assert is not
 
37
        # done on evalue because the evalue is pre-processed while
 
38
        # oops.exception_value is the raw value generated by the web
 
39
        # application. Assert on etype only is good enough, though.
 
40
        # XXX matsubara: this doesn't work anymore when using the Django
 
41
        # objects. Corresponding test was disabled as well.
 
42
        #assert self.etype == oops.exception_type, (
 
43
        #    "Oops etype: '%s' doesn't match group etype: '%s'" %
 
44
        #    (oops.exception_type, self.etype))
 
45
        self.urls.setdefault(oops.url, set()).add(oops.oopsid)
 
46
        pageid = oops.pageid
 
47
        if oops.url.startswith(pageid):
 
48
            pageid = 'Unknown'
 
49
        self.pageids[oops.url] = pageid
 
50
        self.count += 1
 
51
        self.bug = oops.oopsinfestation.bug
 
52
        referrer = oops.referrer
 
53
        if self._isLocalReferrer(referrer):
 
54
            self.local_referrers += 1
 
55
            if referrer in self.referrer_urls.keys():
 
56
                self.referrer_urls[referrer] += 1
 
57
            else:
 
58
                self.referrer_urls[referrer] = 1
 
59
        if oops.isBot():
 
60
            self.bots += 1
 
61
        if oops.http_method in ["POST", "GET"]:
 
62
            self.request_methods[oops.http_method] += 1
 
63
        else:
 
64
            self.request_methods["Other"] += 1
 
65
 
 
66
    def renderTXT(self, fp):
 
67
        """Render this group in plain text."""
 
68
        fp.write('%4d %s: %s\n' % (self.count, self.etype, self.evalue))
 
69
        if self.bug:
 
70
            fp.write('    Bug: https://launchpad.net/bugs/%s\n' % self.bug)
 
71
        if self.etype == 'NotFound' and self.local_referrers:
 
72
            fp.write(
 
73
                '    %s Robots: %d  Local: %d Most Common Referrer: %s\n'
 
74
                % (self.formatted_request_methods, self.bots,
 
75
                   self.local_referrers, self.printTopReferrer()))
 
76
        else:
 
77
            fp.write('    %s Robots: %d  Local: %d\n' %
 
78
                (self.formatted_request_methods, self.bots,
 
79
                 self.local_referrers))
 
80
        urls = sorted(((len(oopsids), url) for (url, oopsids)
 
81
                                               in self.urls.iteritems()),
 
82
                      reverse=True)
 
83
        # print the first three URLs
 
84
        for (count, url) in urls[:3]:
 
85
            fp.write('    %4d %s (%s)\n' % (count, url, self.pageids[url]))
 
86
            fp.write(
 
87
                '        %s\n' % ', '.join(sorted(self.urls[url])[:5]))
 
88
        if len(urls) > 3:
 
89
            fp.write('    [%s other URLs]\n' % (len(urls) - 3))
 
90
        fp.write('\n')
 
91
 
 
92
    def renderHTML(self, fp):
 
93
        """Render this group in HTML."""
 
94
        fp.write('<div class="exc">%d <b>%s</b>: %s</div>\n'
 
95
                 % (self.count, cgi.escape(self.etype),
 
96
                    cgi.escape(self.evalue)))
 
97
        if self.bug:
 
98
            bug_link = "https://launchpad.net/bugs/%s" % self.bug
 
99
            fp.write('Bug: <a href="%s">%s</a>\n' % (bug_link, self.bug))
 
100
        if self.etype == 'NotFound' and self.local_referrers:
 
101
            top_referrer = cgi.escape(self.printTopReferrer())
 
102
            fp.write('<div class="pct">%s Robots: %d  Local: %d  Most '
 
103
                     'Common Referrer: <a href="%s">%s</a></ul></div>\n'
 
104
                     % (self.formatted_request_methods, self.bots,
 
105
                        self.local_referrers, top_referrer, top_referrer))
 
106
        else:
 
107
            fp.write('<div class="pct">%s Robots: %d  Local: %d</div>\n'
 
108
                     % (self.formatted_request_methods, self.bots,
 
109
                        self.local_referrers))
 
110
        urls = self.errorUrls()
 
111
        # print the first five URLs
 
112
        fp.write('<ul>\n')
 
113
        for (count, url) in urls[:5]:
 
114
            fp.write('<li>%d <a class="errurl" href="%s">%s</a> (%s)\n' %
 
115
                     (count, cgi.escape(url), cgi.escape(url),
 
116
                      self.pageids[url]))
 
117
            sample_oopses = sorted(self.urls[url])[:10]
 
118
            sample_oops_urls = [
 
119
                '<a href="%s/oops/?oopsid=%s">%s</a>' % (
 
120
                    settings.ROOT_URL, oops, oops)
 
121
                for oops in sample_oopses]
 
122
            fp.write('<ul class="oops"><li>%s</li></ul>\n' %
 
123
                ', '.join(sample_oops_urls))
 
124
            fp.write('</li>\n')
 
125
        if len(urls) > 5:
 
126
            fp.write('<li>[%d more]</li>\n' % (len(urls) - 5))
 
127
        fp.write('</ul>\n\n')
 
128
 
 
129
    def errorUrls(self):
 
130
      #XXX missing test and docstring and better name
 
131
        return sorted(((len(oopsids), url) for (url, oopsids)
 
132
            in self.urls.iteritems()), reverse=True)
 
133
 
 
134
    def printTopReferrer(self):
 
135
        top_referrers = sorted(self.referrer_urls.items(),
 
136
                               key=lambda (x,y): (y,x), reverse=True)
 
137
        return top_referrers[0][0]
 
138
 
 
139
    @property
 
140
    def formatted_request_methods(self):
 
141
        formatted_output = ''
 
142
        for method, count in self.request_methods.iteritems():
 
143
            if self.request_methods[method]:
 
144
                formatted_output += "%s: %s " % (method, count)
 
145
        return formatted_output
 
146
 
 
147
    def _isLocalReferrer(self, referrer):
 
148
        """Return True if 'referrer' is a local domain like launchpad.net or
 
149
        ubuntu.com
 
150
        """
 
151
        url, query = urllib.splitquery(referrer)
 
152
        if 'launchpad.net' in url or 'ubuntu.com' in url:
 
153
            return True
 
154
        else:
 
155
            return False
 
156
 
 
157