From 52adaed63ed1d6255f66a0be7abb84dadcf457bb Mon Sep 17 00:00:00 2001 From: Rob Hudson Date: Fri, 23 Jan 2009 09:31:49 -0800 Subject: Fix triggering an extra SQL query via the auth context processor. Fixed by moving the template panel's context_processor introspection to the content method so this happens at the process_response time instead of at process_request time. Since context processors _were_ happening at the process_response end, it was triggering the query `user.get_and_delete_messages()` which was also getting triggered separately if used in templates (i.e. the admin templates) resulting in duplicate queries showing up in the toolbar. --- debug_toolbar/panels/template.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'debug_toolbar/panels') diff --git a/debug_toolbar/panels/template.py b/debug_toolbar/panels/template.py index fa85cb8..7dc7b06 100644 --- a/debug_toolbar/panels/template.py +++ b/debug_toolbar/panels/template.py @@ -48,11 +48,15 @@ class TemplateDebugPanel(DebugPanel): return '' def process_request(self, request): - self.context_processors = dict( - [("%s.%s" % (k.__module__, k.__name__), pformat(k(request))) for k in get_standard_processors()] - ) + self.request = request def content(self): + context_processors = dict( + [ + ("%s.%s" % (k.__module__, k.__name__), + pformat(k(self.request))) for k in get_standard_processors() + ] + ) template_context = [] for i, d in enumerate(self.templates): info = {} @@ -73,6 +77,6 @@ class TemplateDebugPanel(DebugPanel): context = { 'templates': template_context, 'template_dirs': [normpath(x) for x in settings.TEMPLATE_DIRS], - 'context_processors': self.context_processors, + 'context_processors': context_processors, } return render_to_string('debug_toolbar/panels/templates.html', context) -- cgit v1.2.3 From 85bd265bd5fe9dda44c67c2b2e618a79c2fb8fdc Mon Sep 17 00:00:00 2001 From: Rob Hudson Date: Fri, 23 Jan 2009 10:59:59 -0800 Subject: Add preliminary support for stacktraces to see where SQL queries are coming from. I'm wanting to test this a bit more before I merge it in fully. Feedback welcome.--- debug_toolbar/panels/sql.py | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) (limited to 'debug_toolbar/panels') diff --git a/debug_toolbar/panels/sql.py b/debug_toolbar/panels/sql.py index 7396c3a..0369a59 100644 --- a/debug_toolbar/panels/sql.py +++ b/debug_toolbar/panels/sql.py @@ -1,5 +1,8 @@ +import os +import SocketServer import time -from debug_toolbar.panels import DebugPanel +import traceback +import django from django.conf import settings from django.db import connection from django.db.backends import util @@ -7,6 +10,28 @@ from django.template.loader import render_to_string from django.utils import simplejson from django.utils.encoding import force_unicode from django.utils.hashcompat import sha_constructor +from debug_toolbar.panels import DebugPanel + +# Figure out some paths +django_path = os.path.realpath(os.path.dirname(django.__file__)) +socketserver_path = os.path.realpath(os.path.dirname(SocketServer.__file__)) + +def tidy_stacktrace(strace): + """ + Clean up stacktrace and remove all entries that: + 1. Are part of Django (except contrib apps) + 2. Are part of SocketServer (used by Django's dev server) + 3. Are the last entry (which is part of our stacktracing code) + """ + trace = [] + for s in strace[:-1]: + s_path = os.path.realpath(s[0]) + if django_path in s_path and not 'django/contrib' in s_path: + continue + if socketserver_path in s_path: + continue + trace.append((s[0], s[1], s[2], s[3])) + return trace class DatabaseStatTracker(util.CursorDebugWrapper): """ @@ -19,6 +44,7 @@ class DatabaseStatTracker(util.CursorDebugWrapper): return self.cursor.execute(sql, params) finally: stop = time.time() + stacktrace = tidy_stacktrace(traceback.extract_stack()) _params = '' try: _params = simplejson.dumps([force_unicode(x) for x in params]) @@ -31,6 +57,7 @@ class DatabaseStatTracker(util.CursorDebugWrapper): 'raw_sql': sql, 'params': _params, 'hash': sha_constructor(settings.SECRET_KEY + sql + _params).hexdigest(), + 'stacktrace': stacktrace, }) util.CursorDebugWrapper = DatabaseStatTracker -- cgit v1.2.3 From 50ec9c1b979abd765e7b0f6d0cfa4e24cff18834 Mon Sep 17 00:00:00 2001 From: Malcolm Tredinnick Date: Wed, 18 Feb 2009 15:48:12 +1100 Subject: Fixed "FROM" formatting in SQL panel layout. We always start a new line for "FROM" now. Previous code was assuming MySQL quoting of identifiers (MySQL is a bit non-standard in using backquotes) and was assuming the last thing before the FROM would be a quoted identifier, which wasn't always true, particularly when extra() is used on querysets. Signed-off-by: Rob Hudson --- debug_toolbar/panels/sql.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'debug_toolbar/panels') diff --git a/debug_toolbar/panels/sql.py b/debug_toolbar/panels/sql.py index 7396c3a..1aa42f7 100644 --- a/debug_toolbar/panels/sql.py +++ b/debug_toolbar/panels/sql.py @@ -73,7 +73,7 @@ class SQLDebugPanel(DebugPanel): def reformat_sql(sql): sql = sql.replace('`,`', '`, `') sql = sql.replace('SELECT ', 'SELECT\n\t') - sql = sql.replace('` FROM ', '`\nFROM\n\t') + sql = sql.replace(' FROM ', '\nFROM\n\t') sql = sql.replace(' WHERE ', '\nWHERE\n\t') sql = sql.replace(' INNER JOIN ', '\nINNER JOIN\n\t') sql = sql.replace(' LEFT OUTER JOIN ', '\nLEFT OUTER JOIN\n\t') -- cgit v1.2.3 From 0bed471bb06e3997d29bdc7dc8dec6a0e0c161a0 Mon Sep 17 00:00:00 2001 From: Malcolm Tredinnick Date: Wed, 18 Feb 2009 15:50:52 +1100 Subject: Tidied up SQL formatting in SQL panel. Added formatting of "GROUP BY" and "HAVING", which now show up in trunk queries and could have shown up previously if manually patched into the QuerySet.query instance. Also indent joined tables a bit more underneath the "FROM" statement: they are sub-statements of "FROM". Signed-off-by: Rob Hudson --- debug_toolbar/panels/sql.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'debug_toolbar/panels') diff --git a/debug_toolbar/panels/sql.py b/debug_toolbar/panels/sql.py index 1aa42f7..b9800b8 100644 --- a/debug_toolbar/panels/sql.py +++ b/debug_toolbar/panels/sql.py @@ -75,9 +75,11 @@ def reformat_sql(sql): sql = sql.replace('SELECT ', 'SELECT\n\t') sql = sql.replace(' FROM ', '\nFROM\n\t') sql = sql.replace(' WHERE ', '\nWHERE\n\t') - sql = sql.replace(' INNER JOIN ', '\nINNER JOIN\n\t') - sql = sql.replace(' LEFT OUTER JOIN ', '\nLEFT OUTER JOIN\n\t') + sql = sql.replace(' INNER JOIN', '\n\tINNER JOIN') + sql = sql.replace(' LEFT OUTER JOIN' , '\n\tLEFT OUTER JOIN') sql = sql.replace(' ORDER BY ', '\nORDER BY\n\t') + sql = sql.replace(' HAVING ', '\nHAVING\n\t') + sql = sql.replace(' GROUP BY ', '\nGROUP BY\n\t') # Use Pygments to highlight SQL if it's available try: from pygments import highlight -- cgit v1.2.3 From 1956ee540c75f9d4226f0352df5b06aae5c02703 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Wed, 27 May 2009 19:17:24 -0700 Subject: Adding a signals panel to display list of signals and their providing arguments and receivers. Thanks Alex Gaynor! Signed-off-by: Rob Hudson --- debug_toolbar/panels/signals.py | 73 +++++++++++++++++++++++++++++++++++++++++ debug_toolbar/panels/sql.py | 2 +- 2 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 debug_toolbar/panels/signals.py (limited to 'debug_toolbar/panels') diff --git a/debug_toolbar/panels/signals.py b/debug_toolbar/panels/signals.py new file mode 100644 index 0000000..0a75282 --- /dev/null +++ b/debug_toolbar/panels/signals.py @@ -0,0 +1,73 @@ +import sys + +from django.conf import settings +from django.core.signals import request_started, request_finished, \ + got_request_exception +from django.db.backends.signals import connection_created +from django.db.models.signals import class_prepared, pre_init, post_init, \ + pre_save, post_save, pre_delete, post_delete, post_syncdb +from django.dispatch.dispatcher import WEAKREF_TYPES +from django.template.loader import render_to_string + +from debug_toolbar.panels import DebugPanel + +class SignalDebugPanel(DebugPanel): + name = "Signals" + has_content = True + + SIGNALS = { + 'request_started': request_started, + 'request_finished': request_finished, + 'got_request_exception': got_request_exception, + 'connection_created': connection_created, + 'class_prepared': class_prepared, + 'pre_init': pre_init, + 'post_init': post_init, + 'pre_save': pre_save, + 'post_save': post_save, + 'pre_delete': pre_delete, + 'post_delete': post_delete, + 'post_syncdb': post_syncdb, + } + + def title(self): + return "Signals" + + def url(self): + return '' + + def signals(self): + signals = self.SIGNALS.copy() + if hasattr(settings, 'DEBUG_TOOLBAR_CONFIG'): + extra_signals = settings.DEBUG_TOOLBAR_CONFIG.get('EXTRA_SIGNALS', []) + else: + extra_signals = [] + for signal in extra_signals: + parts = signal.split('.') + path = '.'.join(parts[:-1]) + __import__(path) + signals[parts[-1]] = getattr(sys.modules[path], parts[-1]) + return signals + signals = property(signals) + + def content(self): + signals = [] + keys = self.signals.keys() + keys.sort() + for name in keys: + signal = self.signals[name] + receivers = [] + for (receiverkey, r_senderkey), receiver in signal.receivers: + if isinstance(receiver, WEAKREF_TYPES): + receiver = receiver() + if receiver is None: + continue + if getattr(receiver, 'im_self', None) is not None: + text = "method %s on %s object" % (receiver.__name__, receiver.im_self.__class__.__name__) + elif getattr(receiver, 'im_class', None) is not None: + text = "method %s on %s" % (receiver.__name__, receiver.im_class.__name__) + else: + text = "function %s" % receiver.__name__ + receivers.append(text) + signals.append((name, signal, receivers)) + return render_to_string('debug_toolbar/panels/signals.html', {'signals': signals}) diff --git a/debug_toolbar/panels/sql.py b/debug_toolbar/panels/sql.py index 8934fb3..d3ac7f3 100644 --- a/debug_toolbar/panels/sql.py +++ b/debug_toolbar/panels/sql.py @@ -98,7 +98,7 @@ class SQLDebugPanel(DebugPanel): return render_to_string('debug_toolbar/panels/sql.html', context) def reformat_sql(sql): - sql = sql.replace('`,`', '`, `') + sql = sql.replace(',', ', ') sql = sql.replace('SELECT ', 'SELECT\n\t') sql = sql.replace(' FROM ', '\nFROM\n\t') sql = sql.replace(' WHERE ', '\nWHERE\n\t') -- cgit v1.2.3 From c9cbbb4c5b636676674379367f072c08c0e78f84 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Sat, 13 Jun 2009 00:14:37 +0800 Subject: only conditionally include the Django 1.1 signal for backwards compatibility Signed-off-by: Rob Hudson --- debug_toolbar/panels/signals.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'debug_toolbar/panels') diff --git a/debug_toolbar/panels/signals.py b/debug_toolbar/panels/signals.py index 0a75282..7fe382e 100644 --- a/debug_toolbar/panels/signals.py +++ b/debug_toolbar/panels/signals.py @@ -3,12 +3,16 @@ import sys from django.conf import settings from django.core.signals import request_started, request_finished, \ got_request_exception -from django.db.backends.signals import connection_created from django.db.models.signals import class_prepared, pre_init, post_init, \ pre_save, post_save, pre_delete, post_delete, post_syncdb from django.dispatch.dispatcher import WEAKREF_TYPES from django.template.loader import render_to_string +try: + from django.db.backends.signals import connection_created +except ImportError: + connection_created = None + from debug_toolbar.panels import DebugPanel class SignalDebugPanel(DebugPanel): @@ -56,6 +60,8 @@ class SignalDebugPanel(DebugPanel): keys.sort() for name in keys: signal = self.signals[name] + if signal is None: + continue receivers = [] for (receiverkey, r_senderkey), receiver in signal.receivers: if isinstance(receiver, WEAKREF_TYPES): -- cgit v1.2.3