Merge lp:~stub/launchpad/bug-354035 into lp:launchpad
- bug-354035
- Merge into devel
Status: | Merged |
---|---|
Merged at revision: | not available |
Proposed branch: | lp:~stub/launchpad/bug-354035 |
Merge into: | lp:launchpad |
Diff against target: | None lines |
To merge this branch: | bzr merge lp:~stub/launchpad/bug-354035 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Diogo Matsubara (community) | qa | Approve | |
Gavin Panella (community) | code | Approve | |
Review via email: mp+9419@code.launchpad.net |
Commit message
Description of the change
Stuart Bishop (stub) wrote : | # |
Gavin Panella (allenap) wrote : | # |
Hi stub,
Only a couple of minute comments. Looks good :)
Gavin.
> === modified file 'lib/canonical/
> --- lib/canonical/
> +++ lib/canonical/
> @@ -134,7 +134,7 @@
> def get_request_
> """Get the list of executed statements in the request.
>
> - The list is composed of (starttime, endtime, statement) tuples.
> + The list is composed of (starttime, endtime, db_id, statement) tuples.
> Times are given in milliseconds since the start of the request.
> """
> return getattr(_local, 'request_
> @@ -165,7 +165,11 @@
> # convert times to integer millisecond values
> starttime = int((starttime - request_starttime) * 1000)
> endtime = int((endtime - request_starttime) * 1000)
> - _local.
> + # A string containing no whitespace that lets us identify which Store
> + # is being used.
> + database_identifier = connection_
> + _local.
> + (starttime, endtime, database_
>
> # store the last executed statement as an attribute on the current
> # thread
> @@ -274,6 +278,8 @@
> # opinion on what uri is.
> # pylint: disable-msg=W0231
> self._uri = uri
> + # A unique name for this database connection.
> + self.name = uri.database
>
> def raw_connect(self):
> # Prevent database connections from the main thread if
> @@ -348,6 +354,9 @@
>
> class LaunchpadSessio
>
> + # A unique name for this database connection.
> + name = 'session'
> +
> def raw_connect(self):
> self._dsn = 'dbname=%s user=%s' % (config.
> config.
>
> === modified file 'lib/canonical/
> --- lib/canonical/
> +++ lib/canonical/
> @@ -167,9 +167,9 @@
> fp.write('%s=%s\n' % (urllib.quote(key, safe_chars),
> urllib.quote(value, safe_chars)))
> fp.write('\n')
> - for (start, end, statement) in self.db_statements:
> - fp.write('%05d-%05d %s\n' % (start, end,
> - _normalise_
> + for (start, end, database_id, statement) in self.db_statements:
> + fp.write(
> + start, end, database_id, _normalise_
> fp.write('\n')
> fp.write(
>
> @@ -206,9 +206,15 @@
> line = line.strip()
> if line == '':
> break
> - startend, statement = line.split(' ', 1)
> - start, end = startend.split('-')
> - statements.
> + preamble, statement = line.split(' ', 1)
> + star...
Gavin Panella (allenap) wrote : | # |
> start, end, db_id, statement = re.match(
> r'^(\d+
Or even:
start, end, db_id, statement = re.match(
Stuart Bishop (stub) wrote : | # |
On Wed, Jul 29, 2009 at 7:40 PM, Gavin
Panella<email address hidden> wrote:
> Review: Approve code
> Hi stub,
>
> Only a couple of minute comments. Looks good :)
I've taken all your suggestions on board. Thanks!
--
Stuart Bishop <email address hidden>
http://
Diogo Matsubara (matsubara) wrote : | # |
Hi Stuart,
thanks for subscribing me. I checked your branch and indeed the changes break oops-tools. I'm working on a fix that should be ready by Monday. I'm marking as needs fixing and will add a bug task for oops-tools to the bug report. Once I have the fix for oops-tools ready, it'll be ok to land this branch.
Thanks.
Diogo
Diogo Matsubara (matsubara) wrote : | # |
Hi Stuart,
I've updated oops-tools to cope with database_id on SQL statement logs. Feel free to land your branch. Thanks!
Preview Diff
1 | === modified file 'lib/canonical/launchpad/webapp/adapter.py' | |||
2 | --- lib/canonical/launchpad/webapp/adapter.py 2009-07-28 22:35:01 +0000 | |||
3 | +++ lib/canonical/launchpad/webapp/adapter.py 2009-07-29 11:37:51 +0000 | |||
4 | @@ -134,7 +134,7 @@ | |||
5 | 134 | def get_request_statements(): | 134 | def get_request_statements(): |
6 | 135 | """Get the list of executed statements in the request. | 135 | """Get the list of executed statements in the request. |
7 | 136 | 136 | ||
9 | 137 | The list is composed of (starttime, endtime, statement) tuples. | 137 | The list is composed of (starttime, endtime, db_id, statement) tuples. |
10 | 138 | Times are given in milliseconds since the start of the request. | 138 | Times are given in milliseconds since the start of the request. |
11 | 139 | """ | 139 | """ |
12 | 140 | return getattr(_local, 'request_statements', []) | 140 | return getattr(_local, 'request_statements', []) |
13 | @@ -165,7 +165,11 @@ | |||
14 | 165 | # convert times to integer millisecond values | 165 | # convert times to integer millisecond values |
15 | 166 | starttime = int((starttime - request_starttime) * 1000) | 166 | starttime = int((starttime - request_starttime) * 1000) |
16 | 167 | endtime = int((endtime - request_starttime) * 1000) | 167 | endtime = int((endtime - request_starttime) * 1000) |
18 | 168 | _local.request_statements.append((starttime, endtime, statement)) | 168 | # A string containing no whitespace that lets us identify which Store |
19 | 169 | # is being used. | ||
20 | 170 | database_identifier = connection_wrapper._database.name | ||
21 | 171 | _local.request_statements.append( | ||
22 | 172 | (starttime, endtime, database_identifier, statement)) | ||
23 | 169 | 173 | ||
24 | 170 | # store the last executed statement as an attribute on the current | 174 | # store the last executed statement as an attribute on the current |
25 | 171 | # thread | 175 | # thread |
26 | @@ -274,6 +278,8 @@ | |||
27 | 274 | # opinion on what uri is. | 278 | # opinion on what uri is. |
28 | 275 | # pylint: disable-msg=W0231 | 279 | # pylint: disable-msg=W0231 |
29 | 276 | self._uri = uri | 280 | self._uri = uri |
30 | 281 | # A unique name for this database connection. | ||
31 | 282 | self.name = uri.database | ||
32 | 277 | 283 | ||
33 | 278 | def raw_connect(self): | 284 | def raw_connect(self): |
34 | 279 | # Prevent database connections from the main thread if | 285 | # Prevent database connections from the main thread if |
35 | @@ -348,6 +354,9 @@ | |||
36 | 348 | 354 | ||
37 | 349 | class LaunchpadSessionDatabase(Postgres): | 355 | class LaunchpadSessionDatabase(Postgres): |
38 | 350 | 356 | ||
39 | 357 | # A unique name for this database connection. | ||
40 | 358 | name = 'session' | ||
41 | 359 | |||
42 | 351 | def raw_connect(self): | 360 | def raw_connect(self): |
43 | 352 | self._dsn = 'dbname=%s user=%s' % (config.launchpad_session.dbname, | 361 | self._dsn = 'dbname=%s user=%s' % (config.launchpad_session.dbname, |
44 | 353 | config.launchpad_session.dbuser) | 362 | config.launchpad_session.dbuser) |
45 | 354 | 363 | ||
46 | === modified file 'lib/canonical/launchpad/webapp/errorlog.py' | |||
47 | --- lib/canonical/launchpad/webapp/errorlog.py 2009-07-23 05:02:47 +0000 | |||
48 | +++ lib/canonical/launchpad/webapp/errorlog.py 2009-07-29 11:37:51 +0000 | |||
49 | @@ -167,9 +167,9 @@ | |||
50 | 167 | fp.write('%s=%s\n' % (urllib.quote(key, safe_chars), | 167 | fp.write('%s=%s\n' % (urllib.quote(key, safe_chars), |
51 | 168 | urllib.quote(value, safe_chars))) | 168 | urllib.quote(value, safe_chars))) |
52 | 169 | fp.write('\n') | 169 | fp.write('\n') |
56 | 170 | for (start, end, statement) in self.db_statements: | 170 | for (start, end, database_id, statement) in self.db_statements: |
57 | 171 | fp.write('%05d-%05d %s\n' % (start, end, | 171 | fp.write('%05d-%05d@%s %s\n' % ( |
58 | 172 | _normalise_whitespace(statement))) | 172 | start, end, database_id, _normalise_whitespace(statement))) |
59 | 173 | fp.write('\n') | 173 | fp.write('\n') |
60 | 174 | fp.write(self.tb_text) | 174 | fp.write(self.tb_text) |
61 | 175 | 175 | ||
62 | @@ -206,9 +206,15 @@ | |||
63 | 206 | line = line.strip() | 206 | line = line.strip() |
64 | 207 | if line == '': | 207 | if line == '': |
65 | 208 | break | 208 | break |
69 | 209 | startend, statement = line.split(' ', 1) | 209 | preamble, statement = line.split(' ', 1) |
70 | 210 | start, end = startend.split('-') | 210 | start, suffix = preamble.split('-', 1) |
71 | 211 | statements.append((int(start), int(end), statement)) | 211 | try: |
72 | 212 | end, db_id = suffix.split('@', 1) | ||
73 | 213 | db_id = intern(database_id) # This string is repeated lots. | ||
74 | 214 | except ValueError: | ||
75 | 215 | end, db_id = suffix, None | ||
76 | 216 | statements.append( | ||
77 | 217 | (int(start), int(end), db_id, statement)) | ||
78 | 212 | 218 | ||
79 | 213 | # The rest is traceback. | 219 | # The rest is traceback. |
80 | 214 | tb_text = ''.join(lines) | 220 | tb_text = ''.join(lines) |
81 | @@ -471,9 +477,10 @@ | |||
82 | 471 | 477 | ||
83 | 472 | duration = get_request_duration() | 478 | duration = get_request_duration() |
84 | 473 | 479 | ||
88 | 474 | statements = sorted((start, end, _safestr(statement)) | 480 | statements = sorted( |
89 | 475 | for (start, end, statement) | 481 | (start, end, _safestr(database_id), _safestr(statement)) |
90 | 476 | in get_request_statements()) | 482 | for (start, end, database_id, statement) |
91 | 483 | in get_request_statements()) | ||
92 | 477 | 484 | ||
93 | 478 | oopsid, filename = self.newOopsId(now) | 485 | oopsid, filename = self.newOopsId(now) |
94 | 479 | 486 | ||
95 | 480 | 487 | ||
96 | === modified file 'lib/canonical/launchpad/webapp/tests/test_errorlog.py' | |||
97 | --- lib/canonical/launchpad/webapp/tests/test_errorlog.py 2009-07-17 00:26:05 +0000 | |||
98 | +++ lib/canonical/launchpad/webapp/tests/test_errorlog.py 2009-07-29 11:05:49 +0000 | |||
99 | @@ -57,8 +57,8 @@ | |||
100 | 57 | 'pageid', 'traceback-text', 'username', 'url', 42, | 57 | 'pageid', 'traceback-text', 'username', 'url', 42, |
101 | 58 | [('name1', 'value1'), ('name2', 'value2'), | 58 | [('name1', 'value1'), ('name2', 'value2'), |
102 | 59 | ('name1', 'value3')], | 59 | ('name1', 'value3')], |
105 | 60 | [(1, 5, 'SELECT 1'), | 60 | [(1, 5, 'store_a', 'SELECT 1'), |
106 | 61 | (5, 10, 'SELECT 2')]) | 61 | (5, 10, 'store_b', 'SELECT 2')]) |
107 | 62 | self.assertEqual(entry.id, 'id') | 62 | self.assertEqual(entry.id, 'id') |
108 | 63 | self.assertEqual(entry.type, 'exc-type') | 63 | self.assertEqual(entry.type, 'exc-type') |
109 | 64 | self.assertEqual(entry.value, 'exc-value') | 64 | self.assertEqual(entry.value, 'exc-value') |
110 | @@ -74,8 +74,12 @@ | |||
111 | 74 | self.assertEqual(entry.req_vars[1], ('name2', 'value2')) | 74 | self.assertEqual(entry.req_vars[1], ('name2', 'value2')) |
112 | 75 | self.assertEqual(entry.req_vars[2], ('name1', 'value3')) | 75 | self.assertEqual(entry.req_vars[2], ('name1', 'value3')) |
113 | 76 | self.assertEqual(len(entry.db_statements), 2) | 76 | self.assertEqual(len(entry.db_statements), 2) |
116 | 77 | self.assertEqual(entry.db_statements[0], (1, 5, 'SELECT 1')) | 77 | self.assertEqual( |
117 | 78 | self.assertEqual(entry.db_statements[1], (5, 10, 'SELECT 2')) | 78 | entry.db_statements[0], |
118 | 79 | (1, 5, 'store_a', 'SELECT 1')) | ||
119 | 80 | self.assertEqual( | ||
120 | 81 | entry.db_statements[1], | ||
121 | 82 | (5, 10, 'store_b', 'SELECT 2')) | ||
122 | 79 | 83 | ||
123 | 80 | def test_write(self): | 84 | def test_write(self): |
124 | 81 | """Test ErrorReport.write()""" | 85 | """Test ErrorReport.write()""" |
125 | @@ -88,8 +92,8 @@ | |||
126 | 88 | [('HTTP_USER_AGENT', 'Mozilla/5.0'), | 92 | [('HTTP_USER_AGENT', 'Mozilla/5.0'), |
127 | 89 | ('HTTP_REFERER', 'http://localhost:9000/'), | 93 | ('HTTP_REFERER', 'http://localhost:9000/'), |
128 | 90 | ('name=foo', 'hello\nworld')], | 94 | ('name=foo', 'hello\nworld')], |
131 | 91 | [(1, 5, 'SELECT 1'), | 95 | [(1, 5, 'store_a', 'SELECT 1'), |
132 | 92 | (5, 10, 'SELECT\n2')]) | 96 | (5, 10,'store_b', 'SELECT\n2')]) |
133 | 93 | fp = StringIO.StringIO() | 97 | fp = StringIO.StringIO() |
134 | 94 | entry.write(fp) | 98 | entry.write(fp) |
135 | 95 | self.assertEqual(fp.getvalue(), dedent("""\ | 99 | self.assertEqual(fp.getvalue(), dedent("""\ |
136 | @@ -108,13 +112,60 @@ | |||
137 | 108 | HTTP_REFERER=http://localhost:9000/ | 112 | HTTP_REFERER=http://localhost:9000/ |
138 | 109 | name%%3Dfoo=hello%%0Aworld | 113 | name%%3Dfoo=hello%%0Aworld |
139 | 110 | 114 | ||
142 | 111 | 00001-00005 SELECT 1 | 115 | 00001-00005@store_a SELECT 1 |
143 | 112 | 00005-00010 SELECT 2 | 116 | 00005-00010@store_b SELECT 2 |
144 | 113 | 117 | ||
145 | 114 | traceback-text""" % (versioninfo.branch_nick, versioninfo.revno))) | 118 | traceback-text""" % (versioninfo.branch_nick, versioninfo.revno))) |
146 | 115 | 119 | ||
147 | 116 | def test_read(self): | 120 | def test_read(self): |
149 | 117 | """Test ErrorReport.read()""" | 121 | """Test ErrorReport.read().""" |
150 | 122 | fp = StringIO.StringIO(dedent("""\ | ||
151 | 123 | Oops-Id: OOPS-A0001 | ||
152 | 124 | Exception-Type: NotFound | ||
153 | 125 | Exception-Value: error message | ||
154 | 126 | Date: 2005-04-01T00:00:00+00:00 | ||
155 | 127 | Page-Id: IFoo:+foo-template | ||
156 | 128 | User: Sample User | ||
157 | 129 | URL: http://localhost:9000/foo | ||
158 | 130 | Duration: 42 | ||
159 | 131 | |||
160 | 132 | HTTP_USER_AGENT=Mozilla/5.0 | ||
161 | 133 | HTTP_REFERER=http://localhost:9000/ | ||
162 | 134 | name%3Dfoo=hello%0Aworld | ||
163 | 135 | |||
164 | 136 | 00001-00005@store_a SELECT 1 | ||
165 | 137 | 00005-00010@store_b SELECT 2 | ||
166 | 138 | |||
167 | 139 | traceback-text""")) | ||
168 | 140 | entry = ErrorReport.read(fp) | ||
169 | 141 | self.assertEqual(entry.id, 'OOPS-A0001') | ||
170 | 142 | self.assertEqual(entry.type, 'NotFound') | ||
171 | 143 | self.assertEqual(entry.value, 'error message') | ||
172 | 144 | # XXX jamesh 2005-11-30: | ||
173 | 145 | # this should probably convert back to a datetime | ||
174 | 146 | self.assertEqual(entry.time, datetime.datetime(2005, 4, 1)) | ||
175 | 147 | self.assertEqual(entry.pageid, 'IFoo:+foo-template') | ||
176 | 148 | self.assertEqual(entry.tb_text, 'traceback-text') | ||
177 | 149 | self.assertEqual(entry.username, 'Sample User') | ||
178 | 150 | self.assertEqual(entry.url, 'http://localhost:9000/foo') | ||
179 | 151 | self.assertEqual(entry.duration, 42) | ||
180 | 152 | self.assertEqual(len(entry.req_vars), 3) | ||
181 | 153 | self.assertEqual(entry.req_vars[0], ('HTTP_USER_AGENT', | ||
182 | 154 | 'Mozilla/5.0')) | ||
183 | 155 | self.assertEqual(entry.req_vars[1], ('HTTP_REFERER', | ||
184 | 156 | 'http://localhost:9000/')) | ||
185 | 157 | self.assertEqual(entry.req_vars[2], ('name=foo', 'hello\nworld')) | ||
186 | 158 | self.assertEqual(len(entry.db_statements), 2) | ||
187 | 159 | self.assertEqual( | ||
188 | 160 | entry.db_statements[0], | ||
189 | 161 | (1, 5, 'store_a', 'SELECT 1')) | ||
190 | 162 | self.assertEqual( | ||
191 | 163 | entry.db_statements[1], | ||
192 | 164 | (5, 10, 'store_b', 'SELECT 2')) | ||
193 | 165 | |||
194 | 166 | |||
195 | 167 | def test_read_no_store_id(self): | ||
196 | 168 | """Test ErrorReport.read() for old logs with no store_id.""" | ||
197 | 118 | fp = StringIO.StringIO(dedent("""\ | 169 | fp = StringIO.StringIO(dedent("""\ |
198 | 119 | Oops-Id: OOPS-A0001 | 170 | Oops-Id: OOPS-A0001 |
199 | 120 | Exception-Type: NotFound | 171 | Exception-Type: NotFound |
200 | @@ -152,8 +203,8 @@ | |||
201 | 152 | 'http://localhost:9000/')) | 203 | 'http://localhost:9000/')) |
202 | 153 | self.assertEqual(entry.req_vars[2], ('name=foo', 'hello\nworld')) | 204 | self.assertEqual(entry.req_vars[2], ('name=foo', 'hello\nworld')) |
203 | 154 | self.assertEqual(len(entry.db_statements), 2) | 205 | self.assertEqual(len(entry.db_statements), 2) |
206 | 155 | self.assertEqual(entry.db_statements[0], (1, 5, 'SELECT 1')) | 206 | self.assertEqual(entry.db_statements[0], (1, 5, None, 'SELECT 1')) |
207 | 156 | self.assertEqual(entry.db_statements[1], (5, 10, 'SELECT 2')) | 207 | self.assertEqual(entry.db_statements[1], (5, 10, None, 'SELECT 2')) |
208 | 157 | 208 | ||
209 | 158 | 209 | ||
210 | 159 | class TestErrorReportingUtility(unittest.TestCase): | 210 | class TestErrorReportingUtility(unittest.TestCase): |
211 | @@ -422,7 +473,7 @@ | |||
212 | 422 | # Test ErrorReportingUtility.raising() with an XML-RPC request. | 473 | # Test ErrorReportingUtility.raising() with an XML-RPC request. |
213 | 423 | request = TestRequest() | 474 | request = TestRequest() |
214 | 424 | directlyProvides(request, IXMLRPCRequest) | 475 | directlyProvides(request, IXMLRPCRequest) |
216 | 425 | request.getPositionalArguments = lambda : (1,2) | 476 | request.getPositionalArguments = lambda: (1, 2) |
217 | 426 | utility = ErrorReportingUtility() | 477 | utility = ErrorReportingUtility() |
218 | 427 | now = datetime.datetime(2006, 04, 01, 00, 30, 00, tzinfo=UTC) | 478 | now = datetime.datetime(2006, 04, 01, 00, 30, 00, tzinfo=UTC) |
219 | 428 | try: | 479 | try: |
220 | @@ -469,7 +520,6 @@ | |||
221 | 469 | utility.raising(sys.exc_info(), request, now=now) | 520 | utility.raising(sys.exc_info(), request, now=now) |
222 | 470 | self.assertEqual(request.oopsid, None) | 521 | self.assertEqual(request.oopsid, None) |
223 | 471 | 522 | ||
224 | 472 | |||
225 | 473 | def test_raising_for_script(self): | 523 | def test_raising_for_script(self): |
226 | 474 | """Test ErrorReportingUtility.raising with a ScriptRequest.""" | 524 | """Test ErrorReportingUtility.raising with a ScriptRequest.""" |
227 | 475 | utility = ErrorReportingUtility() | 525 | utility = ErrorReportingUtility() |
228 | @@ -753,7 +803,7 @@ | |||
229 | 753 | # logged will have OOPS reports generated for them. | 803 | # logged will have OOPS reports generated for them. |
230 | 754 | error_message = self.factory.getUniqueString() | 804 | error_message = self.factory.getUniqueString() |
231 | 755 | try: | 805 | try: |
233 | 756 | 1/0 | 806 | ignored = 1/0 |
234 | 757 | except ZeroDivisionError: | 807 | except ZeroDivisionError: |
235 | 758 | self.logger.exception(error_message) | 808 | self.logger.exception(error_message) |
236 | 759 | oops_report = self.error_utility.getLastOopsReport() | 809 | oops_report = self.error_utility.getLastOopsReport() |
Address Bug #354035
This branch adds an identifier for the Store being used to the SQL statement logs. At the moment, this will normally be something like 'launchpad- main-master' . launchpad is the config section in launchpad-lazr.conf being used for database configuration, 'main' is the Store realm, 'master' is the Store flavour. The format of the identifier may change in the future, but it should remain a simple string.
Care has been taken to ensure old format OOPS reports are still loaded. The Store identifier is None for these OOPS reports.
I expect this will break some of the external OOPS handling tools, so requesting a review from matsubara to help identify what this will break and what to do about it.
There were some minor drive by lint and code standard cleanups.