Merge lp:~matsubara/launchpad/bug-436640 into lp:launchpad/db-devel
- bug-436640
- Merge into db-devel
Proposed by
Diogo Matsubara
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Diogo Matsubara | ||||
Approved revision: | no longer in the source branch. | ||||
Merged at revision: | not available | ||||
Proposed branch: | lp:~matsubara/launchpad/bug-436640 | ||||
Merge into: | lp:launchpad/db-devel | ||||
Diff against target: |
372 lines 6 files modified
lib/canonical/launchpad/webapp/errorlog.py (+17/-4) lib/canonical/launchpad/webapp/publication.py (+1/-1) lib/canonical/launchpad/webapp/tests/test_errorlog.py (+80/-32) lib/canonical/launchpad/webapp/tests/test_publication.py (+3/-0) lib/canonical/launchpad/webapp/tests/test_user_requested_oops.py (+10/-0) lib/lp/bugs/doc/checkwatches.txt (+2/-0) |
||||
To merge this branch: | bzr merge lp:~matsubara/launchpad/bug-436640 | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Gary Poster (community) | Approve | ||
Review via email: mp+13016@code.launchpad.net |
Commit message
[r=gary][ui=none][bug=436640] add api to generate informational oopses in errorlog.py and flag DisconnectionErrors and UserRequestOops as informational only.
Description of the change
To post a comment you must log in.
Revision history for this message
Diogo Matsubara (matsubara) wrote : | # |
Revision history for this message
Gary Poster (gary) wrote : | # |
Diogo, this is a great branch! I don't have any suggestions. Thank you!
Gary
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'lib/canonical/launchpad/webapp/errorlog.py' | |||
2 | --- lib/canonical/launchpad/webapp/errorlog.py 2009-08-06 14:25:28 +0000 | |||
3 | +++ lib/canonical/launchpad/webapp/errorlog.py 2009-10-08 01:11:12 +0000 | |||
4 | @@ -132,7 +132,7 @@ | |||
5 | 132 | implements(IErrorReport) | 132 | implements(IErrorReport) |
6 | 133 | 133 | ||
7 | 134 | def __init__(self, id, type, value, time, pageid, tb_text, username, | 134 | def __init__(self, id, type, value, time, pageid, tb_text, username, |
9 | 135 | url, duration, req_vars, db_statements): | 135 | url, duration, req_vars, db_statements, informational): |
10 | 136 | self.id = id | 136 | self.id = id |
11 | 137 | self.type = type | 137 | self.type = type |
12 | 138 | self.value = value | 138 | self.value = value |
13 | @@ -146,6 +146,7 @@ | |||
14 | 146 | self.db_statements = db_statements | 146 | self.db_statements = db_statements |
15 | 147 | self.branch_nick = versioninfo.branch_nick | 147 | self.branch_nick = versioninfo.branch_nick |
16 | 148 | self.revno = versioninfo.revno | 148 | self.revno = versioninfo.revno |
17 | 149 | self.informational = informational | ||
18 | 149 | 150 | ||
19 | 150 | def __repr__(self): | 151 | def __repr__(self): |
20 | 151 | return '<ErrorReport %s %s: %s>' % (self.id, self.type, self.value) | 152 | return '<ErrorReport %s %s: %s>' % (self.id, self.type, self.value) |
21 | @@ -161,6 +162,7 @@ | |||
22 | 161 | fp.write('User: %s\n' % _normalise_whitespace(self.username)) | 162 | fp.write('User: %s\n' % _normalise_whitespace(self.username)) |
23 | 162 | fp.write('URL: %s\n' % _normalise_whitespace(self.url)) | 163 | fp.write('URL: %s\n' % _normalise_whitespace(self.url)) |
24 | 163 | fp.write('Duration: %s\n' % self.duration) | 164 | fp.write('Duration: %s\n' % self.duration) |
25 | 165 | fp.write('Informational: %s\n' % self.informational) | ||
26 | 164 | fp.write('\n') | 166 | fp.write('\n') |
27 | 165 | safe_chars = ';/\\?:@&+$, ()*!' | 167 | safe_chars = ';/\\?:@&+$, ()*!' |
28 | 166 | for key, value in self.req_vars: | 168 | for key, value in self.req_vars: |
29 | @@ -184,6 +186,7 @@ | |||
30 | 184 | username = msg.getheader('user') | 186 | username = msg.getheader('user') |
31 | 185 | url = msg.getheader('url') | 187 | url = msg.getheader('url') |
32 | 186 | duration = int(float(msg.getheader('duration', '-1'))) | 188 | duration = int(float(msg.getheader('duration', '-1'))) |
33 | 189 | informational = msg.getheader('informational') | ||
34 | 187 | 190 | ||
35 | 188 | # Explicitely use an iterator so we can process the file | 191 | # Explicitely use an iterator so we can process the file |
36 | 189 | # sequentially. In most instances the iterator will actually | 192 | # sequentially. In most instances the iterator will actually |
37 | @@ -217,7 +220,8 @@ | |||
38 | 217 | tb_text = ''.join(lines) | 220 | tb_text = ''.join(lines) |
39 | 218 | 221 | ||
40 | 219 | return cls(id, exc_type, exc_value, date, pageid, tb_text, | 222 | return cls(id, exc_type, exc_value, date, pageid, tb_text, |
42 | 220 | username, url, duration, req_vars, statements) | 223 | username, url, duration, req_vars, statements, |
43 | 224 | informational) | ||
44 | 221 | 225 | ||
45 | 222 | 226 | ||
46 | 223 | class ErrorReportingUtility: | 227 | class ErrorReportingUtility: |
47 | @@ -396,6 +400,10 @@ | |||
48 | 396 | 400 | ||
49 | 397 | def raising(self, info, request=None, now=None): | 401 | def raising(self, info, request=None, now=None): |
50 | 398 | """See IErrorReportingUtility.raising()""" | 402 | """See IErrorReportingUtility.raising()""" |
51 | 403 | self._raising(info, request=request, now=now, informational=False) | ||
52 | 404 | |||
53 | 405 | def _raising(self, info, request=None, now=None, informational=False): | ||
54 | 406 | """Private method used by raising() and handling().""" | ||
55 | 399 | if now is not None: | 407 | if now is not None: |
56 | 400 | now = now.astimezone(UTC) | 408 | now = now.astimezone(UTC) |
57 | 401 | else: | 409 | else: |
58 | @@ -483,7 +491,8 @@ | |||
59 | 483 | 491 | ||
60 | 484 | entry = ErrorReport(oopsid, strtype, strv, now, pageid, tb_text, | 492 | entry = ErrorReport(oopsid, strtype, strv, now, pageid, tb_text, |
61 | 485 | username, strurl, duration, | 493 | username, strurl, duration, |
63 | 486 | req_vars, statements) | 494 | req_vars, statements, |
64 | 495 | informational) | ||
65 | 487 | entry.write(open(filename, 'wb')) | 496 | entry.write(open(filename, 'wb')) |
66 | 488 | 497 | ||
67 | 489 | if request: | 498 | if request: |
68 | @@ -495,6 +504,10 @@ | |||
69 | 495 | finally: | 504 | finally: |
70 | 496 | info = None | 505 | info = None |
71 | 497 | 506 | ||
72 | 507 | def handling(self, info, request=None, now=None): | ||
73 | 508 | """Flag ErrorReport as informational only.""" | ||
74 | 509 | self._raising(info, request=request, now=now, informational=True) | ||
75 | 510 | |||
76 | 498 | def _do_copy_to_zlog(self, now, strtype, url, info, oopsid): | 511 | def _do_copy_to_zlog(self, now, strtype, url, info, oopsid): |
77 | 499 | distant_past = datetime.datetime(1970, 1, 1, 0, 0, 0, tzinfo=UTC) | 512 | distant_past = datetime.datetime(1970, 1, 1, 0, 0, 0, tzinfo=UTC) |
78 | 500 | when = _rate_restrict_pool.get(strtype, distant_past) | 513 | when = _rate_restrict_pool.get(strtype, distant_past) |
79 | @@ -618,7 +631,7 @@ | |||
80 | 618 | request.oopsid is not None or | 631 | request.oopsid is not None or |
81 | 619 | not request.annotations.get(LAZR_OOPS_USER_REQUESTED_KEY, False)): | 632 | not request.annotations.get(LAZR_OOPS_USER_REQUESTED_KEY, False)): |
82 | 620 | return None | 633 | return None |
84 | 621 | globalErrorUtility.raising( | 634 | globalErrorUtility.handling( |
85 | 622 | (UserRequestOops, UserRequestOops(), None), request) | 635 | (UserRequestOops, UserRequestOops(), None), request) |
86 | 623 | return request.oopsid | 636 | return request.oopsid |
87 | 624 | 637 | ||
88 | 625 | 638 | ||
89 | === modified file 'lib/canonical/launchpad/webapp/publication.py' | |||
90 | --- lib/canonical/launchpad/webapp/publication.py 2009-10-01 15:36:20 +0000 | |||
91 | +++ lib/canonical/launchpad/webapp/publication.py 2009-10-08 01:11:12 +0000 | |||
92 | @@ -484,7 +484,7 @@ | |||
93 | 484 | # Log a soft OOPS for DisconnectionErrors as per Bug #373837. | 484 | # Log a soft OOPS for DisconnectionErrors as per Bug #373837. |
94 | 485 | # We need to do this before we re-raise the exception as a Retry. | 485 | # We need to do this before we re-raise the exception as a Retry. |
95 | 486 | if isinstance(exc_info[1], DisconnectionError): | 486 | if isinstance(exc_info[1], DisconnectionError): |
97 | 487 | getUtility(IErrorReportingUtility).raising(exc_info, request) | 487 | getUtility(IErrorReportingUtility).handling(exc_info, request) |
98 | 488 | 488 | ||
99 | 489 | def should_retry(exc_info): | 489 | def should_retry(exc_info): |
100 | 490 | if not retry_allowed: | 490 | if not retry_allowed: |
101 | 491 | 491 | ||
102 | === modified file 'lib/canonical/launchpad/webapp/tests/test_errorlog.py' | |||
103 | --- lib/canonical/launchpad/webapp/tests/test_errorlog.py 2009-07-29 14:28:18 +0000 | |||
104 | +++ lib/canonical/launchpad/webapp/tests/test_errorlog.py 2009-10-08 01:11:12 +0000 | |||
105 | @@ -58,7 +58,8 @@ | |||
106 | 58 | [('name1', 'value1'), ('name2', 'value2'), | 58 | [('name1', 'value1'), ('name2', 'value2'), |
107 | 59 | ('name1', 'value3')], | 59 | ('name1', 'value3')], |
108 | 60 | [(1, 5, 'store_a', 'SELECT 1'), | 60 | [(1, 5, 'store_a', 'SELECT 1'), |
110 | 61 | (5, 10, 'store_b', 'SELECT 2')]) | 61 | (5, 10, 'store_b', 'SELECT 2')], |
111 | 62 | False) | ||
112 | 62 | self.assertEqual(entry.id, 'id') | 63 | self.assertEqual(entry.id, 'id') |
113 | 63 | self.assertEqual(entry.type, 'exc-type') | 64 | self.assertEqual(entry.type, 'exc-type') |
114 | 64 | self.assertEqual(entry.value, 'exc-value') | 65 | self.assertEqual(entry.value, 'exc-value') |
115 | @@ -69,6 +70,7 @@ | |||
116 | 69 | self.assertEqual(entry.username, 'username') | 70 | self.assertEqual(entry.username, 'username') |
117 | 70 | self.assertEqual(entry.url, 'url') | 71 | self.assertEqual(entry.url, 'url') |
118 | 71 | self.assertEqual(entry.duration, 42) | 72 | self.assertEqual(entry.duration, 42) |
119 | 73 | self.assertEqual(entry.informational, False) | ||
120 | 72 | self.assertEqual(len(entry.req_vars), 3) | 74 | self.assertEqual(len(entry.req_vars), 3) |
121 | 73 | self.assertEqual(entry.req_vars[0], ('name1', 'value1')) | 75 | self.assertEqual(entry.req_vars[0], ('name1', 'value1')) |
122 | 74 | self.assertEqual(entry.req_vars[1], ('name2', 'value2')) | 76 | self.assertEqual(entry.req_vars[1], ('name2', 'value2')) |
123 | @@ -93,7 +95,7 @@ | |||
124 | 93 | ('HTTP_REFERER', 'http://localhost:9000/'), | 95 | ('HTTP_REFERER', 'http://localhost:9000/'), |
125 | 94 | ('name=foo', 'hello\nworld')], | 96 | ('name=foo', 'hello\nworld')], |
126 | 95 | [(1, 5, 'store_a', 'SELECT 1'), | 97 | [(1, 5, 'store_a', 'SELECT 1'), |
128 | 96 | (5, 10,'store_b', 'SELECT\n2')]) | 98 | (5, 10,'store_b', 'SELECT\n2')], False) |
129 | 97 | fp = StringIO.StringIO() | 99 | fp = StringIO.StringIO() |
130 | 98 | entry.write(fp) | 100 | entry.write(fp) |
131 | 99 | self.assertEqual(fp.getvalue(), dedent("""\ | 101 | self.assertEqual(fp.getvalue(), dedent("""\ |
132 | @@ -107,6 +109,7 @@ | |||
133 | 107 | User: Sample User | 109 | User: Sample User |
134 | 108 | URL: http://localhost:9000/foo | 110 | URL: http://localhost:9000/foo |
135 | 109 | Duration: 42 | 111 | Duration: 42 |
136 | 112 | Informational: False | ||
137 | 110 | 113 | ||
138 | 111 | HTTP_USER_AGENT=Mozilla/5.0 | 114 | HTTP_USER_AGENT=Mozilla/5.0 |
139 | 112 | HTTP_REFERER=http://localhost:9000/ | 115 | HTTP_REFERER=http://localhost:9000/ |
140 | @@ -377,19 +380,20 @@ | |||
141 | 377 | self.assertEqual(lines[7], 'User: None\n') | 380 | self.assertEqual(lines[7], 'User: None\n') |
142 | 378 | self.assertEqual(lines[8], 'URL: None\n') | 381 | self.assertEqual(lines[8], 'URL: None\n') |
143 | 379 | self.assertEqual(lines[9], 'Duration: -1\n') | 382 | self.assertEqual(lines[9], 'Duration: -1\n') |
145 | 380 | self.assertEqual(lines[10], '\n') | 383 | self.assertEqual(lines[10], 'Informational: False\n') |
146 | 384 | self.assertEqual(lines[11], '\n') | ||
147 | 381 | 385 | ||
148 | 382 | # no request vars | 386 | # no request vars |
150 | 383 | self.assertEqual(lines[11], '\n') | 387 | self.assertEqual(lines[12], '\n') |
151 | 384 | 388 | ||
152 | 385 | # no database statements | 389 | # no database statements |
154 | 386 | self.assertEqual(lines[12], '\n') | 390 | self.assertEqual(lines[13], '\n') |
155 | 387 | 391 | ||
156 | 388 | # traceback | 392 | # traceback |
158 | 389 | self.assertEqual(lines[13], 'Traceback (most recent call last):\n') | 393 | self.assertEqual(lines[14], 'Traceback (most recent call last):\n') |
159 | 390 | # Module canonical.launchpad.webapp.ftests.test_errorlog, ... | 394 | # Module canonical.launchpad.webapp.ftests.test_errorlog, ... |
160 | 391 | # raise ArbitraryException(\'xyz\') | 395 | # raise ArbitraryException(\'xyz\') |
162 | 392 | self.assertEqual(lines[16], 'ArbitraryException: xyz\n') | 396 | self.assertEqual(lines[17], 'ArbitraryException: xyz\n') |
163 | 393 | 397 | ||
164 | 394 | def test_raising_with_request(self): | 398 | def test_raising_with_request(self): |
165 | 395 | """Test ErrorReportingUtility.raising() with a request""" | 399 | """Test ErrorReportingUtility.raising() with a request""" |
166 | @@ -432,6 +436,7 @@ | |||
167 | 432 | lines.pop(0), 'User: Login, 42, title, description |\\u25a0|\n') | 436 | lines.pop(0), 'User: Login, 42, title, description |\\u25a0|\n') |
168 | 433 | self.assertEqual(lines.pop(0), 'URL: http://localhost:9000/foo\n') | 437 | self.assertEqual(lines.pop(0), 'URL: http://localhost:9000/foo\n') |
169 | 434 | self.assertEqual(lines.pop(0), 'Duration: -1\n') | 438 | self.assertEqual(lines.pop(0), 'Duration: -1\n') |
170 | 439 | self.assertEqual(lines.pop(0), 'Informational: False\n') | ||
171 | 435 | self.assertEqual(lines.pop(0), '\n') | 440 | self.assertEqual(lines.pop(0), '\n') |
172 | 436 | 441 | ||
173 | 437 | # request vars | 442 | # request vars |
174 | @@ -479,7 +484,7 @@ | |||
175 | 479 | errorfile = os.path.join(utility.errordir(now), '01800.T1') | 484 | errorfile = os.path.join(utility.errordir(now), '01800.T1') |
176 | 480 | self.assertTrue(os.path.exists(errorfile)) | 485 | self.assertTrue(os.path.exists(errorfile)) |
177 | 481 | lines = open(errorfile, 'r').readlines() | 486 | lines = open(errorfile, 'r').readlines() |
179 | 482 | self.assertEqual(lines[15], 'xmlrpc args=(1, 2)\n') | 487 | self.assertEqual(lines[16], 'xmlrpc args=(1, 2)\n') |
180 | 483 | 488 | ||
181 | 484 | def test_raising_with_webservice_request(self): | 489 | def test_raising_with_webservice_request(self): |
182 | 485 | # Test ErrorReportingUtility.raising() with a WebServiceRequest | 490 | # Test ErrorReportingUtility.raising() with a WebServiceRequest |
183 | @@ -546,22 +551,23 @@ | |||
184 | 546 | self.assertEqual(lines[7], 'User: None\n') | 551 | self.assertEqual(lines[7], 'User: None\n') |
185 | 547 | self.assertEqual(lines[8], 'URL: https://launchpad.net/example\n') | 552 | self.assertEqual(lines[8], 'URL: https://launchpad.net/example\n') |
186 | 548 | self.assertEqual(lines[9], 'Duration: -1\n') | 553 | self.assertEqual(lines[9], 'Duration: -1\n') |
188 | 549 | self.assertEqual(lines[10], '\n') | 554 | self.assertEqual(lines[10], 'Informational: False\n') |
189 | 555 | self.assertEqual(lines[11], '\n') | ||
190 | 550 | 556 | ||
191 | 551 | # request vars | 557 | # request vars |
198 | 552 | self.assertEqual(lines[11], 'name1=value1\n') | 558 | self.assertEqual(lines[12], 'name1=value1\n') |
199 | 553 | self.assertEqual(lines[12], 'name1=value3\n') | 559 | self.assertEqual(lines[13], 'name1=value3\n') |
200 | 554 | self.assertEqual(lines[13], 'name2=value2\n') | 560 | self.assertEqual(lines[14], 'name2=value2\n') |
195 | 555 | self.assertEqual(lines[14], '\n') | ||
196 | 556 | |||
197 | 557 | # no database statements | ||
201 | 558 | self.assertEqual(lines[15], '\n') | 561 | self.assertEqual(lines[15], '\n') |
202 | 559 | 562 | ||
203 | 563 | # no database statements | ||
204 | 564 | self.assertEqual(lines[16], '\n') | ||
205 | 565 | |||
206 | 560 | # traceback | 566 | # traceback |
208 | 561 | self.assertEqual(lines[16], 'Traceback (most recent call last):\n') | 567 | self.assertEqual(lines[17], 'Traceback (most recent call last):\n') |
209 | 562 | # Module canonical.launchpad.webapp.ftests.test_errorlog, ... | 568 | # Module canonical.launchpad.webapp.ftests.test_errorlog, ... |
210 | 563 | # raise ArbitraryException(\'xyz\') | 569 | # raise ArbitraryException(\'xyz\') |
212 | 564 | self.assertEqual(lines[19], 'ArbitraryException: xyz\n') | 570 | self.assertEqual(lines[20], 'ArbitraryException: xyz\n') |
213 | 565 | 571 | ||
214 | 566 | # verify that the oopsid was set on the request | 572 | # verify that the oopsid was set on the request |
215 | 567 | self.assertEqual(request.oopsid, 'OOPS-91T1') | 573 | self.assertEqual(request.oopsid, 'OOPS-91T1') |
216 | @@ -598,20 +604,21 @@ | |||
217 | 598 | self.assertEqual(lines[7], 'User: None\n') | 604 | self.assertEqual(lines[7], 'User: None\n') |
218 | 599 | self.assertEqual(lines[8], 'URL: None\n') | 605 | self.assertEqual(lines[8], 'URL: None\n') |
219 | 600 | self.assertEqual(lines[9], 'Duration: -1\n') | 606 | self.assertEqual(lines[9], 'Duration: -1\n') |
221 | 601 | self.assertEqual(lines[10], '\n') | 607 | self.assertEqual(lines[10], 'Informational: False\n') |
222 | 608 | self.assertEqual(lines[11], '\n') | ||
223 | 602 | 609 | ||
224 | 603 | # no request vars | 610 | # no request vars |
226 | 604 | self.assertEqual(lines[11], '\n') | 611 | self.assertEqual(lines[12], '\n') |
227 | 605 | 612 | ||
228 | 606 | # no database statements | 613 | # no database statements |
230 | 607 | self.assertEqual(lines[12], '\n') | 614 | self.assertEqual(lines[13], '\n') |
231 | 608 | 615 | ||
232 | 609 | # traceback | 616 | # traceback |
234 | 610 | self.assertEqual(lines[13], 'Traceback (most recent call last):\n') | 617 | self.assertEqual(lines[14], 'Traceback (most recent call last):\n') |
235 | 611 | # Module canonical.launchpad.webapp.ftests.test_errorlog, ... | 618 | # Module canonical.launchpad.webapp.ftests.test_errorlog, ... |
236 | 612 | # raise UnprintableException() | 619 | # raise UnprintableException() |
237 | 613 | self.assertEqual( | 620 | self.assertEqual( |
239 | 614 | lines[16], 'UnprintableException: <unprintable instance object>\n' | 621 | lines[17], 'UnprintableException: <unprintable instance object>\n' |
240 | 615 | ) | 622 | ) |
241 | 616 | 623 | ||
242 | 617 | def test_raising_unauthorized_without_request(self): | 624 | def test_raising_unauthorized_without_request(self): |
243 | @@ -715,16 +722,57 @@ | |||
244 | 715 | self.assertEqual(lines[7], 'User: None\n') | 722 | self.assertEqual(lines[7], 'User: None\n') |
245 | 716 | self.assertEqual(lines[8], 'URL: None\n') | 723 | self.assertEqual(lines[8], 'URL: None\n') |
246 | 717 | self.assertEqual(lines[9], 'Duration: -1\n') | 724 | self.assertEqual(lines[9], 'Duration: -1\n') |
257 | 718 | self.assertEqual(lines[10], '\n') | 725 | self.assertEqual(lines[10], 'Informational: False\n') |
258 | 719 | 726 | self.assertEqual(lines[11], '\n') | |
259 | 720 | # no request vars | 727 | |
260 | 721 | self.assertEqual(lines[11], '\n') | 728 | # no request vars |
261 | 722 | 729 | self.assertEqual(lines[12], '\n') | |
262 | 723 | # no database statements | 730 | |
263 | 724 | self.assertEqual(lines[12], '\n') | 731 | # no database statements |
264 | 725 | 732 | self.assertEqual(lines[13], '\n') | |
265 | 726 | # traceback | 733 | |
266 | 727 | self.assertEqual(''.join(lines[13:17]), exc_tb) | 734 | # traceback |
267 | 735 | self.assertEqual(''.join(lines[14:18]), exc_tb) | ||
268 | 736 | |||
269 | 737 | def test_handling(self): | ||
270 | 738 | """Test ErrorReportingUtility.handling().""" | ||
271 | 739 | utility = ErrorReportingUtility() | ||
272 | 740 | now = datetime.datetime(2006, 04, 01, 00, 30, 00, tzinfo=UTC) | ||
273 | 741 | |||
274 | 742 | try: | ||
275 | 743 | raise ArbitraryException('xyz') | ||
276 | 744 | except ArbitraryException: | ||
277 | 745 | utility.handling(sys.exc_info(), now=now) | ||
278 | 746 | |||
279 | 747 | errorfile = os.path.join(utility.errordir(now), '01800.T1') | ||
280 | 748 | self.assertTrue(os.path.exists(errorfile)) | ||
281 | 749 | lines = open(errorfile, 'r').readlines() | ||
282 | 750 | |||
283 | 751 | # the header | ||
284 | 752 | self.assertEqual(lines[0], 'Oops-Id: OOPS-91T1\n') | ||
285 | 753 | self.assertEqual(lines[1], 'Exception-Type: ArbitraryException\n') | ||
286 | 754 | self.assertEqual(lines[2], 'Exception-Value: xyz\n') | ||
287 | 755 | self.assertEqual(lines[3], 'Date: 2006-04-01T00:30:00+00:00\n') | ||
288 | 756 | self.assertEqual(lines[4], 'Page-Id: \n') | ||
289 | 757 | self.assertEqual(lines[5], 'Branch: %s\n' % versioninfo.branch_nick) | ||
290 | 758 | self.assertEqual(lines[6], 'Revision: %s\n'% versioninfo.revno) | ||
291 | 759 | self.assertEqual(lines[7], 'User: None\n') | ||
292 | 760 | self.assertEqual(lines[8], 'URL: None\n') | ||
293 | 761 | self.assertEqual(lines[9], 'Duration: -1\n') | ||
294 | 762 | self.assertEqual(lines[10], 'Informational: True\n') | ||
295 | 763 | self.assertEqual(lines[11], '\n') | ||
296 | 764 | |||
297 | 765 | # no request vars | ||
298 | 766 | self.assertEqual(lines[12], '\n') | ||
299 | 767 | |||
300 | 768 | # no database statements | ||
301 | 769 | self.assertEqual(lines[13], '\n') | ||
302 | 770 | |||
303 | 771 | # traceback | ||
304 | 772 | self.assertEqual(lines[14], 'Traceback (most recent call last):\n') | ||
305 | 773 | # Module canonical.launchpad.webapp.ftests.test_errorlog, ... | ||
306 | 774 | # raise ArbitraryException(\'xyz\') | ||
307 | 775 | self.assertEqual(lines[17], 'ArbitraryException: xyz\n') | ||
308 | 728 | 776 | ||
309 | 729 | 777 | ||
310 | 730 | class TestSensitiveRequestVariables(unittest.TestCase): | 778 | class TestSensitiveRequestVariables(unittest.TestCase): |
311 | 731 | 779 | ||
312 | === modified file 'lib/canonical/launchpad/webapp/tests/test_publication.py' | |||
313 | --- lib/canonical/launchpad/webapp/tests/test_publication.py 2009-09-17 14:29:22 +0000 | |||
314 | +++ lib/canonical/launchpad/webapp/tests/test_publication.py 2009-10-08 01:11:12 +0000 | |||
315 | @@ -115,6 +115,9 @@ | |||
316 | 115 | # Ensure the OOPS mentions the correct exception | 115 | # Ensure the OOPS mentions the correct exception |
317 | 116 | self.assertNotEqual(repr(next_oops).find("DisconnectionError"), -1) | 116 | self.assertNotEqual(repr(next_oops).find("DisconnectionError"), -1) |
318 | 117 | 117 | ||
319 | 118 | # Ensure the OOPS is correctly marked as informational only. | ||
320 | 119 | self.assertEqual(next_oops.informational, 'True') | ||
321 | 120 | |||
322 | 118 | # Ensure that it is different to the last logged OOPS. | 121 | # Ensure that it is different to the last logged OOPS. |
323 | 119 | self.assertNotEqual(repr(last_oops), repr(next_oops)) | 122 | self.assertNotEqual(repr(last_oops), repr(next_oops)) |
324 | 120 | 123 | ||
325 | 121 | 124 | ||
326 | === modified file 'lib/canonical/launchpad/webapp/tests/test_user_requested_oops.py' | |||
327 | --- lib/canonical/launchpad/webapp/tests/test_user_requested_oops.py 2009-07-22 23:31:29 +0000 | |||
328 | +++ lib/canonical/launchpad/webapp/tests/test_user_requested_oops.py 2009-10-08 01:11:12 +0000 | |||
329 | @@ -6,6 +6,9 @@ | |||
330 | 6 | 6 | ||
331 | 7 | import unittest | 7 | import unittest |
332 | 8 | 8 | ||
333 | 9 | from zope.component import getUtility | ||
334 | 10 | from zope.error.interfaces import IErrorReportingUtility | ||
335 | 11 | |||
336 | 9 | from lazr.restful.utils import get_current_browser_request | 12 | from lazr.restful.utils import get_current_browser_request |
337 | 10 | 13 | ||
338 | 11 | from canonical.launchpad.webapp.errorlog import ( | 14 | from canonical.launchpad.webapp.errorlog import ( |
339 | @@ -68,6 +71,13 @@ | |||
340 | 68 | self.assertIs(context, result) | 71 | self.assertIs(context, result) |
341 | 69 | self.assertTrue(request.annotations.get(LAZR_OOPS_USER_REQUESTED_KEY)) | 72 | self.assertTrue(request.annotations.get(LAZR_OOPS_USER_REQUESTED_KEY)) |
342 | 70 | 73 | ||
343 | 74 | def test_user_requested_oops_marked_informational(self): | ||
344 | 75 | # User requested oopses are flagged as informational only. | ||
345 | 76 | error_reporting_utility = getUtility(IErrorReportingUtility) | ||
346 | 77 | last_oops = error_reporting_utility.getLastOopsReport() | ||
347 | 78 | self.assertEqual(last_oops.type, 'UserRequestOops') | ||
348 | 79 | self.assertEqual(last_oops.informational, 'True') | ||
349 | 80 | |||
350 | 71 | 81 | ||
351 | 72 | def test_suite(): | 82 | def test_suite(): |
352 | 73 | return unittest.TestLoader().loadTestsFromName(__name__) | 83 | return unittest.TestLoader().loadTestsFromName(__name__) |
353 | 74 | 84 | ||
354 | === modified file 'lib/lp/bugs/doc/checkwatches.txt' | |||
355 | --- lib/lp/bugs/doc/checkwatches.txt 2009-10-05 09:18:38 +0000 | |||
356 | +++ lib/lp/bugs/doc/checkwatches.txt 2009-10-08 01:11:12 +0000 | |||
357 | @@ -86,6 +86,7 @@ | |||
358 | 86 | User: None | 86 | User: None |
359 | 87 | URL: None | 87 | URL: None |
360 | 88 | Duration: -1 | 88 | Duration: -1 |
361 | 89 | Informational: False | ||
362 | 89 | <BLANKLINE> | 90 | <BLANKLINE> |
363 | 90 | error-explanation=ExternalBugtracker for ... is not known. | 91 | error-explanation=ExternalBugtracker for ... is not known. |
364 | 91 | 92 | ||
365 | @@ -157,6 +158,7 @@ | |||
366 | 157 | User: None | 158 | User: None |
367 | 158 | URL: http://bugs.example.com | 159 | URL: http://bugs.example.com |
368 | 159 | Duration: -1 | 160 | Duration: -1 |
369 | 161 | Informational: False | ||
370 | 160 | <BLANKLINE> | 162 | <BLANKLINE> |
371 | 161 | baseurl=http://bugs.example.com | 163 | baseurl=http://bugs.example.com |
372 | 162 | bugtracker=example-bugs | 164 | bugtracker=example-bugs |
This branch add a new api: handling() to the ErrorReportingU tility which allows OOPS reports to be flagged as informational only. It also updates DisconnectionErrors and UserRequestOops to use said API.
Tests
$ bin/test -u test_errorlog requested_ oops
$ bin/test -u test_publication
$ bin/test -u test_user_
Demo and Q/A
1. Open https:/ /staging. launchpad. net/++oops+ + /lp-oops. canonical. com/oops/ ?oopsid= $oops_id_ from_step_ 2
2. Grab the OOPS id in the html source code
3. Wait 10 minutes for the oops to be synced from staging to devpad
4. https:/
5. Check that the OOPS have the flag Informational: True in the left column