Merge lp:~gary/launchpad/bug838869 into lp:launchpad

Proposed by Gary Poster
Status: Merged
Approved by: Gary Poster
Approved revision: no longer in the source branch.
Merged at revision: 13857
Proposed branch: lp:~gary/launchpad/bug838869
Merge into: lp:launchpad
Diff against target: 613 lines (+439/-41)
4 files modified
lib/canonical/launchpad/webapp/adapter.py (+120/-37)
lib/canonical/launchpad/webapp/tests/test_statementtracer.py (+316/-0)
lib/lp/services/profile/profile.py (+2/-2)
lib/lp/services/profile/tests.py (+1/-2)
To merge this branch: bzr merge lp:~gary/launchpad/bug838869
Reviewer Review Type Date Requested Status
j.c.sackett (community) Approve
Review via email: mp+73695@code.launchpad.net

Commit message

[r=jcsackett][bug=838869] Add tests for Launchpad SQL logger; add a more surgical SQL logger for outside of a request than what we had before; and add the ability to conditionally request tracebacks in the SQL logger.

Description of the change

This branch does three things.

- It adds a helper for tracking queries piecemeal in tests and make harness, SQLLogger. This is different than the QueryCollector in lib/lp/testing/_webservice.py because it does not require a browser request to work. I use this in a branch stacked on this one, https://code.launchpad.net/~gary/launchpad/bug811447 , to check queries generated from a piece of model code.

- It adds the ability to conditionally collect stacktraces, and renames the associated function from `start_sql_traceback_logging` to `start_sql_logging`. I use this in a branch stacked on this one, https://code.launchpad.net/~gary/launchpad/bug838878 , to support ++profile++sqltrace conditionally generating stacktraces.

- It adds tests for the logger, which had not existed at all as far as I could find. I include tests for the functionality I added as well as existing functionality. This is bug 838869.

Lint is happy.

Reasonable QA will be to go to https://qastaging.launchpad.net/++profile++sqltrace and make sure it still generates a sql trace analysis; and run `make run LP_DEBUG_SQL=1` on a dev machine to make sure it still produces SQL.

Thank you.

To post a comment you must log in.
Revision history for this message
j.c.sackett (jcsackett) wrote :

Gary, this looks good.

review: Approve
Revision history for this message
Robert Collins (lifeless) wrote :

There was/is a non browser query collector in testing/. This may be
duplicative.

On 3/09/2011 4:34 AM, <email address hidden> wrote:

The proposal to merge lp:~gary/launchpad/bug838869 into lp:launchpad has
been updated.

   Status: Approved => Merged

For more details, see:
https://code.launchpad.net/~gary/launchpad/bug838869/+merge/73695

--
https://code.launchpad.net/~gary/launchpad/bug838869/+merge/73695
Your team Launchpad code revie...

--
launchpad-reviews mailing list
<email address hidden>
https://lists.ubuntu.com/ma...

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/canonical/launchpad/webapp/adapter.py'
--- lib/canonical/launchpad/webapp/adapter.py 2011-08-26 13:13:20 +0000
+++ lib/canonical/launchpad/webapp/adapter.py 2011-09-02 16:31:40 +0000
@@ -6,13 +6,16 @@
66
7__metaclass__ = type7__metaclass__ = type
88
9from functools import partial
9import logging10import logging
10import os11import os
11import re12import re
13import StringIO
12import sys14import sys
13import thread15import thread
14import threading16import threading
15from time import time17from time import time
18import traceback
16import warnings19import warnings
1720
18import psycopg221import psycopg2
@@ -79,6 +82,7 @@
79 )82 )
80from lp.services.stacktrace import (83from lp.services.stacktrace import (
81 extract_stack,84 extract_stack,
85 extract_tb,
82 print_list,86 print_list,
83 )87 )
8488
@@ -91,9 +95,11 @@
91 'get_request_start_time',95 'get_request_start_time',
92 'get_request_duration',96 'get_request_duration',
93 'get_store_name',97 'get_store_name',
98 'print_queries',
94 'soft_timeout_expired',99 'soft_timeout_expired',
95 'start_sql_traceback_logging',100 'SQLLogger',
96 'stop_sql_traceback_logging',101 'start_sql_logging',
102 'stop_sql_logging',
97 'StoreSelector',103 'StoreSelector',
98 ]104 ]
99105
@@ -202,7 +208,9 @@
202 warnings.warn('clear_request_started() called outside of a request',208 warnings.warn('clear_request_started() called outside of a request',
203 stacklevel=2)209 stacklevel=2)
204 _local.request_start_time = None210 _local.request_start_time = None
205 _local.sql_trace = None211 _local.sql_logging = None
212 _local.sql_logging_start = None
213 _local.sql_logging_tracebacks_if = None
206 request = get_current_browser_request()214 request = get_current_browser_request()
207 set_request_timeline(request, Timeline())215 set_request_timeline(request, Timeline())
208 if getattr(_local, 'commit_logger', None) is not None:216 if getattr(_local, 'commit_logger', None) is not None:
@@ -351,18 +359,75 @@
351 return True359 return True
352360
353361
354def start_sql_traceback_logging():362def start_sql_logging(tracebacks_if=False):
355 """Set the sql traceback data logging for the current request."""363 """Turn the sql data logging on."""
356 _local.sql_trace = []364 if getattr(_local, 'sql_logging', None) is not None:
357365 warnings.warn('SQL logging already started')
358366 return
359def stop_sql_traceback_logging():367 _local.sql_logging_tracebacks_if = tracebacks_if
360 """Stop the sql traceback data logging and return the result."""368 _local.sql_logging = []
361 result = getattr(_local, 'sql_trace', None)369 _local.sql_logging_start = int(time() * 1000)
362 _local.sql_trace = None370
371
372def stop_sql_logging():
373 """Turn off the sql data logging and return the result."""
374 result = getattr(_local, 'sql_logging', None)
375 _local.sql_logging_tracebacks_if = None
376 _local.sql_logging = None
377 _local.sql_logging_start = None
378 if result is None:
379 warnings.warn('SQL logging not started')
363 return result380 return result
364381
365382
383class SQLLogger:
384
385 def __init__(self, tracebacks_if=False):
386 self.tracebacks_if = tracebacks_if
387
388 queries = None
389
390 def __enter__(self):
391 self.queries = None
392 start_sql_logging(self.tracebacks_if)
393
394 def __exit__(self, exc_type, exc_value, tb):
395 self.queries = stop_sql_logging()
396
397 def __str__(self):
398 if self.queries is None:
399 return '(no queries)'
400 else:
401 out = StringIO.StringIO()
402 print_queries(self.queries, file=out)
403 return out.getvalue()
404
405
406def print_queries(queries, file=None):
407 if file is None:
408 file = sys.stdout
409 for query in queries:
410 stack = query['stack']
411 if stack is not None:
412 exception = query['exception']
413 if exception is not None:
414 file.write(
415 'Error when determining whether to generate a '
416 'stacktrace.\n')
417 file.write('Traceback (most recent call last):\n')
418 print_list(stack, file)
419 if exception is not None:
420 lines = traceback.format_exception_only(*exception)
421 file.write(' '.join(lines))
422 file.write("." * 70 + "\n")
423 sql = query['sql']
424 if sql is not None:
425 file.write('%d-%d@%s %s\n' % sql)
426 else:
427 file.write('(no SQL recorded)\n')
428 file.write("-" * 70 + "\n")
429
430
366# ---- Prevent database access in the main thread of the app server431# ---- Prevent database access in the main thread of the app server
367432
368class StormAccessFromMainThread(Exception):433class StormAccessFromMainThread(Exception):
@@ -625,23 +690,14 @@
625class LaunchpadStatementTracer:690class LaunchpadStatementTracer:
626 """Storm tracer class to log executed statements."""691 """Storm tracer class to log executed statements."""
627692
693 _normalize_whitespace = partial(re.compile('\s+').sub, ' ')
694
628 def __init__(self):695 def __init__(self):
629 self._debug_sql = bool(os.environ.get('LP_DEBUG_SQL'))696 self._debug_sql = bool(os.environ.get('LP_DEBUG_SQL'))
630 self._debug_sql_extra = bool(os.environ.get('LP_DEBUG_SQL_EXTRA'))697 self._debug_sql_extra = bool(os.environ.get('LP_DEBUG_SQL_EXTRA'))
631698
632 def connection_raw_execute(self, connection, raw_cursor,699 def connection_raw_execute(self, connection, raw_cursor,
633 statement, params):700 statement, params):
634 sql_trace = getattr(_local, 'sql_trace', None)
635 if sql_trace is not None or self._debug_sql_extra:
636 # Gather data for the [start|stop]_sql_traceback_logging
637 # feature, as exposed by ++profile++sqltrace, and/or for stderr,
638 # as exposed by LP_DEBUG_SQL_EXTRA.
639 stack = extract_stack()
640 if sql_trace is not None:
641 sql_trace.append(dict(stack=stack, sql=None))
642 if self._debug_sql_extra:
643 print_list(stack)
644 sys.stderr.write("." * 70 + "\n")
645 statement_to_log = statement701 statement_to_log = statement
646 if params:702 if params:
647 # There are some bind parameters so we want to insert them into703 # There are some bind parameters so we want to insert them into
@@ -656,18 +712,43 @@
656 param_strings = [repr(p) if isinstance(p, basestring) else p712 param_strings = [repr(p) if isinstance(p, basestring) else p
657 for p in query_params]713 for p in query_params]
658 statement_to_log = quoted_statement % tuple(param_strings)714 statement_to_log = quoted_statement % tuple(param_strings)
715 # Record traceback to log, if requested.
716 print_traceback = self._debug_sql_extra
717 log_sql = getattr(_local, 'sql_logging', None)
718 log_traceback = False
719 if log_sql is not None:
720 log_sql.append(dict(stack=None, sql=None, exception=None))
721 conditional = getattr(_local, 'sql_logging_tracebacks_if', None)
722 if callable(conditional):
723 try:
724 log_traceback = conditional(
725 self._normalize_whitespace(
726 statement_to_log.strip()).upper())
727 except (MemoryError, SystemExit, KeyboardInterrupt):
728 raise
729 except:
730 exc_type, exc_value, tb = sys.exc_info()
731 log_sql[-1]['exception'] = (exc_type, exc_value)
732 log_sql[-1]['stack'] = extract_tb(tb)
733 else:
734 log_traceback = bool(conditional)
735 if print_traceback or log_traceback:
736 stack = extract_stack()
737 if log_traceback:
738 log_sql[-1]['stack'] = stack
739 if print_traceback:
740 print_list(stack)
741 sys.stderr.write("." * 70 + "\n")
659 # store the last executed statement as an attribute on the current742 # store the last executed statement as an attribute on the current
660 # thread743 # thread
661 threading.currentThread().lp_last_sql_statement = statement744 threading.currentThread().lp_last_sql_statement = statement
662 request_starttime = getattr(_local, 'request_start_time', None)745 request_starttime = getattr(_local, 'request_start_time', None)
663 if request_starttime is None:746 if request_starttime is None:
664 if (sql_trace is not None or747 if print_traceback or self._debug_sql or log_sql is not None:
665 self._debug_sql or
666 self._debug_sql_extra):
667 # Stash some information for logging at the end of the748 # Stash some information for logging at the end of the
668 # request.749 # SQL execution.
669 connection._lp_statement_info = (750 connection._lp_statement_info = (
670 time(),751 int(time() * 1000),
671 'SQL-%s' % connection._database.name,752 'SQL-%s' % connection._database.name,
672 statement_to_log)753 statement_to_log)
673 return754 return
@@ -682,24 +763,26 @@
682 # action may be None if the tracer was installed after the763 # action may be None if the tracer was installed after the
683 # statement was submitted.764 # statement was submitted.
684 action.finish()765 action.finish()
685 # Do data reporting for the [start|stop]_sql_traceback_logging766 log_sql = getattr(_local, 'sql_logging', None)
686 # feature, as exposed by ++profile++sqltrace, and/or for stderr,767 if log_sql is not None or self._debug_sql or self._debug_sql_extra:
687 # as exposed by LP_DEBUG_SQL_EXTRA and LP_DEBUG_SQL.
688 sql_trace = getattr(_local, 'sql_trace', None)
689 if sql_trace is not None or self._debug_sql or self._debug_sql_extra:
690 data = None768 data = None
691 if action is not None:769 if action is not None:
692 data = action.logTuple()770 data = action.logTuple()
693 else:771 else:
694 info = getattr(connection, '_lp_statement_info', None)772 info = getattr(connection, '_lp_statement_info', None)
695 if info is not None:773 if info is not None:
774 stop = int(time() * 1000)
696 start, dbname, statement = info775 start, dbname, statement = info
776 logging_start = (
777 getattr(_local, 'sql_logging_start', None) or start)
697 # Times are in milliseconds, to mirror actions.778 # Times are in milliseconds, to mirror actions.
698 duration = int((time() - start) * 1000)779 start = start - logging_start
699 data = (0, duration, dbname, statement)780 stop = stop - logging_start
781 data = (start, stop, dbname, statement)
782 connection._lp_statement_info = None
700 if data is not None:783 if data is not None:
701 if sql_trace and sql_trace[-1]['sql'] is None:784 if log_sql and log_sql[-1]['sql'] is None:
702 sql_trace[-1]['sql'] = data785 log_sql[-1]['sql'] = data
703 if self._debug_sql or self._debug_sql_extra:786 if self._debug_sql or self._debug_sql_extra:
704 sys.stderr.write('%d-%d@%s %s\n' % data)787 sys.stderr.write('%d-%d@%s %s\n' % data)
705 sys.stderr.write("-" * 70 + "\n")788 sys.stderr.write("-" * 70 + "\n")
706789
=== added file 'lib/canonical/launchpad/webapp/tests/test_statementtracer.py'
--- lib/canonical/launchpad/webapp/tests/test_statementtracer.py 1970-01-01 00:00:00 +0000
+++ lib/canonical/launchpad/webapp/tests/test_statementtracer.py 2011-09-02 16:31:40 +0000
@@ -0,0 +1,316 @@
1# Copyright 2011 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).
3
4"""Tests for the LaunchpadStatementTracer."""
5
6__metaclass__ = type
7
8from contextlib import contextmanager
9import StringIO
10import sys
11
12from lp.testing import (
13 person_logged_in,
14 TestCase,
15 TestCaseWithFactory,
16 )
17from lp.services.osutils import override_environ
18from lp.services.timeline.requesttimeline import get_request_timeline
19from canonical.launchpad.webapp import adapter as da
20from canonical.lazr.utils import get_current_browser_request
21from canonical.testing import DatabaseFunctionalLayer
22
23
24@contextmanager
25def stdout():
26 file = StringIO.StringIO()
27 original = sys.stdout
28 sys.stdout = file
29 try:
30 yield file
31 finally:
32 sys.stdout = original
33
34
35@contextmanager
36def stderr():
37 file = StringIO.StringIO()
38 original = sys.stderr
39 sys.stderr = file
40 try:
41 yield file
42 finally:
43 sys.stderr = original
44
45
46class StubTime:
47
48 time = 1000.0
49
50 def __call__(self):
51 result = self.time
52 self.time += 0.001
53 return result
54
55
56class StubConnection:
57
58 def __init__(self):
59 self._database = type('StubDatabase', (), dict(name='stub-database'))
60
61
62class TestLoggingOutsideOfRequest(TestCase):
63
64 def setUp(self):
65 super(TestLoggingOutsideOfRequest, self).setUp()
66 self.connection = StubConnection()
67 original_time = da.time
68 self.addCleanup(setattr, da, 'time', original_time)
69 da.time = StubTime()
70
71 def execute(self, statement=None, params=None, **environ):
72 with override_environ(**environ):
73 tracer = da.LaunchpadStatementTracer()
74 if statement is None:
75 statement = 'SELECT * FROM bar WHERE bing = 42'
76 tracer.connection_raw_execute(
77 self.connection, None, statement, params)
78 tracer.connection_raw_execute_success(
79 self.connection, None, statement, params)
80
81 def test_no_logging(self):
82 with stderr() as file:
83 self.execute()
84 self.assertEqual('', file.getvalue())
85
86 def test_stderr(self):
87 with stderr() as file:
88 self.execute(LP_DEBUG_SQL='1')
89 self.assertEqual(
90 '0-1@SQL-stub-database SELECT * FROM bar WHERE bing = 42\n' +
91 "-" * 70 + "\n",
92 file.getvalue())
93
94 def test_stderr_with_stacktrace(self):
95 with stderr() as file:
96 self.execute(LP_DEBUG_SQL_EXTRA='1')
97 self.assertStartsWith(
98 file.getvalue(),
99 ' File "')
100 self.assertEndsWith(
101 file.getvalue(),
102 "." * 70 + "\n" +
103 '0-1@SQL-stub-database SELECT * FROM bar WHERE bing = 42\n' +
104 "-" * 70 + "\n")
105
106 def test_data_logging(self):
107 da.start_sql_logging()
108 with stderr() as file:
109 self.execute()
110 self.assertEqual('', file.getvalue())
111 result = da.stop_sql_logging()
112 self.assertEqual(1, len(result))
113 self.assertIs(None, result[0]['stack'])
114 self.assertIs(None, result[0]['exception'])
115 self.assertEqual(
116 (1, 2, 'SQL-stub-database', 'SELECT * FROM bar WHERE bing = 42'),
117 result[0]['sql'])
118
119 def test_data_logging_with_stacktrace(self):
120 da.start_sql_logging(tracebacks_if=True)
121 with stderr() as file:
122 self.execute()
123 self.assertEqual('', file.getvalue())
124 result = da.stop_sql_logging()
125 self.assertEqual(1, len(result))
126 self.assertIsNot(None, result[0]['stack'])
127 self.assertIs(None, result[0]['exception'])
128 self.assertEqual(
129 (1, 2, 'SQL-stub-database', 'SELECT * FROM bar WHERE bing = 42'),
130 result[0]['sql'])
131
132 def test_data_logging_with_conditional_stacktrace(self):
133 # Conditions must be normalized to uppercase.
134 da.start_sql_logging(tracebacks_if=lambda sql: 'KUMQUAT' in sql)
135 with stderr() as file:
136 self.execute()
137 self.execute(statement='SELECT * FROM kumquat WHERE bing = 42')
138 self.assertEqual('', file.getvalue())
139 result = da.stop_sql_logging()
140 self.assertEqual(2, len(result))
141 self.assertIs(None, result[0]['stack'])
142 self.assertIsNot(None, result[1]['stack'])
143
144 def test_data_logging_with_conditional_stacktrace_normalized_whitespace(
145 self):
146 # The whitespace in the SQL is normalized
147 da.start_sql_logging(
148 tracebacks_if=lambda sql: 'FROM KUMQUAT WHERE' in sql)
149 self.execute(
150 statement='SELECT * FROM kumquat \nWHERE bing = 42')
151 result = da.stop_sql_logging()
152 self.assertEqual(1, len(result))
153 self.assertIsNot(None, result[0]['stack'])
154
155 def test_data_logging_with_broken_conditional_stacktrace(self):
156 error = ValueError('rutebega')
157
158 def ow(sql):
159 raise error
160 da.start_sql_logging(tracebacks_if=ow)
161 with stderr() as file:
162 self.execute()
163 self.assertEqual('', file.getvalue())
164 result = da.stop_sql_logging()
165 self.assertEqual(1, len(result))
166 self.assertIsNot(None, result[0]['stack'])
167 self.assertEqual((ValueError, error), result[0]['exception'])
168 self.assertEqual(
169 (1, 2, 'SQL-stub-database', 'SELECT * FROM bar WHERE bing = 42'),
170 result[0]['sql'])
171
172 def test_print_queries_with_tracebacks(self):
173 da.start_sql_logging(tracebacks_if=True)
174 self.execute()
175 result = da.stop_sql_logging()
176 with stdout() as file:
177 da.print_queries(result)
178 self.assertStartsWith(
179 file.getvalue(),
180 ' File "')
181 self.assertEndsWith(
182 file.getvalue(),
183 "." * 70 + "\n" +
184 '1-2@SQL-stub-database SELECT * FROM bar WHERE bing = 42\n' +
185 "-" * 70 + "\n")
186
187 def test_print_queries_without_tracebacks(self):
188 da.start_sql_logging()
189 self.execute()
190 result = da.stop_sql_logging()
191 with stdout() as file:
192 da.print_queries(result)
193 self.assertEqual(
194 '1-2@SQL-stub-database SELECT * FROM bar WHERE bing = 42\n' +
195 "-" * 70 + "\n",
196 file.getvalue())
197
198 def test_print_queries_with_exceptions(self):
199 def ow(sql):
200 raise ValueError('rutebega')
201 da.start_sql_logging(tracebacks_if=ow)
202 self.execute()
203 result = da.stop_sql_logging()
204 with stdout() as file:
205 da.print_queries(result)
206 self.assertStartsWith(
207 file.getvalue(),
208 'Error when determining whether to generate a stacktrace.\n' +
209 'Traceback (most recent call last):\n' +
210 ' File "')
211 self.assertEndsWith(
212 file.getvalue(),
213 "ValueError: rutebega\n" +
214 "." * 70 + "\n" +
215 '1-2@SQL-stub-database SELECT * FROM bar WHERE bing = 42\n' +
216 "-" * 70 + "\n")
217
218 def test_context_manager(self):
219 logger = da.SQLLogger()
220 with logger:
221 self.execute()
222 self.assertEqual(1, len(logger.queries))
223 self.assertIs(None, logger.queries[0]['stack'])
224 self.assertIs(None, logger.queries[0]['exception'])
225 self.assertEqual(
226 (1, 2, 'SQL-stub-database', 'SELECT * FROM bar WHERE bing = 42'),
227 logger.queries[0]['sql'])
228 with stdout() as file:
229 result = str(logger)
230 self.assertEqual('', file.getvalue())
231 self.assertEqual(
232 '1-2@SQL-stub-database SELECT * FROM bar WHERE bing = 42\n' +
233 "-" * 70 + "\n",
234 result)
235
236 def test_context_manager_with_stacktrace(self):
237 logger = da.SQLLogger(tracebacks_if=True)
238 with logger:
239 self.execute()
240 self.assertEqual(1, len(logger.queries))
241 self.assertIsNot(None, logger.queries[0]['stack'])
242
243 def test_sql_parameters(self):
244 logger = da.SQLLogger()
245 with logger:
246 self.execute(statement='SELECT * FROM bar WHERE bing = %s',
247 params=(142,))
248 self.assertEqual(
249 (1, 2, 'SQL-stub-database', 'SELECT * FROM bar WHERE bing = 142'),
250 logger.queries[0]['sql'])
251
252
253class TestLoggingWithinRequest(TestCaseWithFactory):
254 # When called with a request, the code uses the request timeline and
255 # its action objects.
256
257 layer = DatabaseFunctionalLayer
258
259 def setUp(self):
260 super(TestLoggingWithinRequest, self).setUp()
261 self.connection = StubConnection()
262 self.person = self.factory.makePerson()
263 da.set_request_started(1000.0)
264 self.addCleanup(da.clear_request_started)
265
266 def test_logger(self):
267 tracer = da.LaunchpadStatementTracer()
268 logger = da.SQLLogger()
269 with person_logged_in(self.person):
270 with logger:
271 tracer.connection_raw_execute(
272 self.connection, None,
273 'SELECT * FROM bar WHERE bing = 42', ())
274 timeline = get_request_timeline(get_current_browser_request())
275 action = timeline.actions[-1]
276 self.assertEqual(
277 'SELECT * FROM bar WHERE bing = 42',
278 action.detail)
279 self.assertEqual('SQL-stub-database', action.category)
280 self.assertIs(None, action.duration)
281 # Now we change the detail to verify that the action is the
282 # source of the final log.
283 action.detail = 'SELECT * FROM surprise'
284 tracer.connection_raw_execute_success(
285 self.connection, None,
286 'SELECT * FROM bar WHERE bing = 42', ())
287 self.assertIsNot(None, action.duration)
288 self.assertEqual(
289 'SELECT * FROM surprise', logger.queries[0]['sql'][3])
290
291 def test_stderr(self):
292 with override_environ(LP_DEBUG_SQL='1'):
293 tracer = da.LaunchpadStatementTracer()
294 with person_logged_in(self.person):
295 with stderr() as file:
296 tracer.connection_raw_execute(
297 self.connection, None,
298 'SELECT * FROM bar WHERE bing = 42', ())
299 timeline = get_request_timeline(get_current_browser_request())
300 action = timeline.actions[-1]
301 self.assertEqual(
302 'SELECT * FROM bar WHERE bing = 42',
303 action.detail)
304 self.assertEqual('SQL-stub-database', action.category)
305 self.assertIs(None, action.duration)
306 # Now we change the detail to verify that the action is the
307 # source of the final log.
308 action.detail = 'SELECT * FROM surprise'
309 tracer.connection_raw_execute_success(
310 self.connection, None,
311 'SELECT * FROM bar WHERE bing = 42', ())
312 self.assertIsNot(None, action.duration)
313 self.assertEndsWith(
314 file.getvalue(),
315 '@SQL-stub-database SELECT * FROM surprise\n' +
316 "-" * 70 + "\n")
0317
=== modified file 'lib/lp/services/profile/profile.py'
--- lib/lp/services/profile/profile.py 2011-08-24 17:24:38 +0000
+++ lib/lp/services/profile/profile.py 2011-09-02 16:31:40 +0000
@@ -213,7 +213,7 @@
213 actions.add('callgrind')213 actions.add('callgrind')
214 if actions:214 if actions:
215 if 'sqltrace' in actions:215 if 'sqltrace' in actions:
216 da.start_sql_traceback_logging()216 da.start_sql_logging(tracebacks_if=True)
217 if 'show' in actions or actions.intersection(available_profilers):217 if 'show' in actions or actions.intersection(available_profilers):
218 _profilers.profiler = Profiler()218 _profilers.profiler = Profiler()
219 _profilers.profiler.start()219 _profilers.profiler.start()
@@ -346,7 +346,7 @@
346 del prof_stats346 del prof_stats
347 trace = None347 trace = None
348 if 'sqltrace' in actions:348 if 'sqltrace' in actions:
349 trace = da.stop_sql_traceback_logging() or ()349 trace = da.stop_sql_logging() or ()
350 # The trace is a list of dicts, each with the keys "sql" and350 # The trace is a list of dicts, each with the keys "sql" and
351 # "stack". "sql" is a tuple of start time, stop time, database351 # "stack". "sql" is a tuple of start time, stop time, database
352 # name (with a "SQL-" prefix), and sql statement. "stack" is a352 # name (with a "SQL-" prefix), and sql statement. "stack" is a
353353
=== modified file 'lib/lp/services/profile/tests.py'
--- lib/lp/services/profile/tests.py 2011-08-22 19:06:39 +0000
+++ lib/lp/services/profile/tests.py 2011-09-02 16:31:40 +0000
@@ -82,7 +82,6 @@
82 profile._profilers.actions = None82 profile._profilers.actions = None
83 profile._profilers.memory_profile_start = None83 profile._profilers.memory_profile_start = None
84 profile._profilers.profiling = False84 profile._profilers.profiling = False
85 da.stop_sql_traceback_logging()
86 super(TestCleanupProfiler, self).tearDown()85 super(TestCleanupProfiler, self).tearDown()
8786
8887
@@ -275,7 +274,7 @@
275 profile.start_request(self._get_start_event('/++profile++sqltrace/'))274 profile.start_request(self._get_start_event('/++profile++sqltrace/'))
276 self.assertIs(getattr(profile._profilers, 'profiler', None), None)275 self.assertIs(getattr(profile._profilers, 'profiler', None), None)
277 self.assertEquals(profile._profilers.actions, set(('sqltrace', )))276 self.assertEquals(profile._profilers.actions, set(('sqltrace', )))
278 self.assertEqual([], da.stop_sql_traceback_logging())277 self.assertEqual([], da.stop_sql_logging())
279278
280279
281class BaseRequestEndHandlerTest(BaseTest):280class BaseRequestEndHandlerTest(BaseTest):