aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRob Hudson2009-01-23 10:59:59 -0800
committerRob Hudson2009-01-23 10:59:59 -0800
commit85bd265bd5fe9dda44c67c2b2e618a79c2fb8fdc (patch)
tree6b4b43b112ef98321b2e78d1b486f494cc4dd1e8
parentc3bcecfd13d8c82c9ac0a88a578bdeb4ad7cfc9d (diff)
downloaddjango-debug-toolbar-85bd265bd5fe9dda44c67c2b2e618a79c2fb8fdc.tar.bz2
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.
-rw-r--r--debug_toolbar/media/toolbar.js7
-rw-r--r--debug_toolbar/media/toolbar.min.js2
-rw-r--r--debug_toolbar/panels/sql.py29
-rw-r--r--debug_toolbar/templates/debug_toolbar/panels/sql.html22
4 files changed, 58 insertions, 2 deletions
diff --git a/debug_toolbar/media/toolbar.js b/debug_toolbar/media/toolbar.js
index b6baa2c..bff9b21 100644
--- a/debug_toolbar/media/toolbar.js
+++ b/debug_toolbar/media/toolbar.js
@@ -35,12 +35,19 @@ jQuery(function($) {
});
$('#djDebugTemplatePanel a.djTemplateShowContext').click(function() {
$.djDebug.toggle_content($(this).parent().next());
+ return false;
+ });
+ $('#djDebugSQLPanel a.djSQLShowStacktrace').click(function() {
+ $.djDebug.toggle_content($(this).parent().next());
+ return false;
});
$('#djHideToolBarButton').click(function() {
$.djDebug.hide_toolbar(true);
+ return false;
});
$('#djShowToolBarButton').click(function() {
$.djDebug.show_toolbar();
+ return false;
});
if ($.cookie(COOKIE_NAME)) {
$.djDebug.hide_toolbar(false);
diff --git a/debug_toolbar/media/toolbar.min.js b/debug_toolbar/media/toolbar.min.js
index 43bfead..4c8364e 100644
--- a/debug_toolbar/media/toolbar.min.js
+++ b/debug_toolbar/media/toolbar.min.js
@@ -1 +1 @@
-var _$=window.$;jQuery.noConflict();jQuery(function(a){var b="dj_debug_panel";a.djDebug=function(d,c){a.djDebug.init()};a.extend(a.djDebug,{init:function(){var c=null;a("#djDebugPanelList li a").click(function(){c=a("#djDebug #"+this.className);if(c.is(":visible")){a(document).trigger("close.djDebug")}else{a(".panelContent").hide();c.show();a.djDebug.open()}return false});a("#djDebug a.close").click(function(){a(document).trigger("close.djDebug");return false});a("#djDebug a.remoteCall").click(function(){a("#djDebugWindow").load(this.href,{},function(){a("#djDebugWindow a.back").click(function(){a(this).parent().hide();return false})});a("#djDebugWindow").show();return false});a("#djDebugTemplatePanel a.djTemplateShowContext").click(function(){a.djDebug.toggle_content(a(this).parent().next())});a("#djHideToolBarButton").click(function(){a.djDebug.hide_toolbar(true)});a("#djShowToolBarButton").click(function(){a.djDebug.show_toolbar()});if(a.cookie(b)){a.djDebug.hide_toolbar(false)}else{a("#djDebugToolbar").show()}},open:function(){a(document).bind("keydown.djDebug",function(c){if(c.keyCode==27){a.djDebug.close()}})},toggle_content:function(c){if(c.is(":visible")){c.hide()}else{c.show()}},close:function(){a(document).trigger("close.djDebug");return false},hide_toolbar:function(c){a("#djDebugToolbar").hide("fast");a("#djDebugToolbarHandle").show();if(c){a.cookie(b,"hide",{path:"/",expires:10})}},show_toolbar:function(){a("#djDebugToolbarHandle").hide();a("#djDebugToolbar").show("fast");a.cookie(b,null,{path:"/",expires:-1})}});a(document).bind("close.djDebug",function(){a(document).unbind("keydown.djDebug");a(".panelContent").hide()})});jQuery(function(){jQuery.djDebug()});$=_$;jQuery.cookie=function(b,j,m){if(typeof j!="undefined"){m=m||{};if(j===null){j="";m.expires=-1}var e="";if(m.expires&&(typeof m.expires=="number"||m.expires.toUTCString)){var f;if(typeof m.expires=="number"){f=new Date();f.setTime(f.getTime()+(m.expires*24*60*60*1000))}else{f=m.expires}e="; expires="+f.toUTCString()}var l=m.path?"; path="+(m.path):"";var g=m.domain?"; domain="+(m.domain):"";var a=m.secure?"; secure":"";document.cookie=[b,"=",encodeURIComponent(j),e,l,g,a].join("")}else{var d=null;if(document.cookie&&document.cookie!=""){var k=document.cookie.split(";");for(var h=0;h<k.length;h++){var c=jQuery.trim(k[h]);if(c.substring(0,b.length+1)==(b+"=")){d=decodeURIComponent(c.substring(b.length+1));break}}}return d}}; \ No newline at end of file
+var _$=window.$;jQuery.noConflict();jQuery(function(A){var B="dj_debug_panel";A.djDebug=function(D,C){A.djDebug.init()};A.extend(A.djDebug,{init:function(){var C=null;A("#djDebugPanelList li a").click(function(){C=A("#djDebug #"+this.className);if(C.is(":visible")){A(document).trigger("close.djDebug")}else{A(".panelContent").hide();C.show();A.djDebug.open()}return false});A("#djDebug a.close").click(function(){A(document).trigger("close.djDebug");return false});A("#djDebug a.remoteCall").click(function(){A("#djDebugWindow").load(this.href,{},function(){A("#djDebugWindow a.back").click(function(){A(this).parent().hide();return false})});A("#djDebugWindow").show();return false});A("#djDebugTemplatePanel a.djTemplateShowContext").click(function(){A.djDebug.toggle_content(A(this).parent().next());return false});A("#djDebugSQLPanel a.djSQLShowStacktrace").click(function(){A.djDebug.toggle_content(A(this).parent().next());return false});A("#djHideToolBarButton").click(function(){A.djDebug.hide_toolbar(true);return false});A("#djShowToolBarButton").click(function(){A.djDebug.show_toolbar();return false});if(A.cookie(B)){A.djDebug.hide_toolbar(false)}else{A("#djDebugToolbar").show()}},open:function(){A(document).bind("keydown.djDebug",function(C){if(C.keyCode==27){A.djDebug.close()}})},toggle_content:function(C){if(C.is(":visible")){C.hide()}else{C.show()}},close:function(){A(document).trigger("close.djDebug");return false},hide_toolbar:function(C){A("#djDebugToolbar").hide("fast");A("#djDebugToolbarHandle").show();if(C){A.cookie(B,"hide",{path:"/",expires:10})}},show_toolbar:function(){A("#djDebugToolbarHandle").hide();A("#djDebugToolbar").show("fast");A.cookie(B,null,{path:"/",expires:-1})}});A(document).bind("close.djDebug",function(){A(document).unbind("keydown.djDebug");A(".panelContent").hide()})});jQuery(function(){jQuery.djDebug()});$=_$;jQuery.cookie=function(B,I,L){if(typeof I!="undefined"){L=L||{};if(I===null){I="";L.expires=-1}var E="";if(L.expires&&(typeof L.expires=="number"||L.expires.toUTCString)){var F;if(typeof L.expires=="number"){F=new Date();F.setTime(F.getTime()+(L.expires*24*60*60*1000))}else{F=L.expires}E="; expires="+F.toUTCString()}var K=L.path?"; path="+(L.path):"";var G=L.domain?"; domain="+(L.domain):"";var A=L.secure?"; secure":"";document.cookie=[B,"=",encodeURIComponent(I),E,K,G,A].join("")}else{var D=null;if(document.cookie&&document.cookie!=""){var J=document.cookie.split(";");for(var H=0;H<J.length;H++){var C=jQuery.trim(J[H]);if(C.substring(0,B.length+1)==(B+"=")){D=decodeURIComponent(C.substring(B.length+1));break}}}return D}}; \ No newline at end of file
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
diff --git a/debug_toolbar/templates/debug_toolbar/panels/sql.html b/debug_toolbar/templates/debug_toolbar/panels/sql.html
index 7de2220..1896e3c 100644
--- a/debug_toolbar/templates/debug_toolbar/panels/sql.html
+++ b/debug_toolbar/templates/debug_toolbar/panels/sql.html
@@ -4,6 +4,7 @@
<tr>
<th>Time&nbsp;(ms)</th>
<th>Action</th>
+ <th>Stacktrace</th>
<th>Query</th>
</tr>
</thead>
@@ -20,6 +21,27 @@
{% endif %}
{% endif %}
</td>
+ <td>
+ {% if query.stacktrace %}
+ <div class="djSQLShowStacktraceDiv"><a class="djSQLShowStacktrace" href="#">Toggle Stacktrace</a></div>
+ <div class="djSQLHideStacktraceDiv" style="display:none;">
+ <table>
+ <tr>
+ <th>Line</th>
+ <th>Method</th>
+ <th>File</th>
+ </tr>
+ {% for s in query.stacktrace %}
+ <tr>
+ <td>{{ s.1 }}</td>
+ <td><pre>{{ s.2|escape }}<pre></td>
+ <td><pre>{{ s.0|escape }}</pre></td>
+ </tr>
+ {% endfor %}
+ </table>
+ </div>
+ {% endif %}
+ </td>
<td class="syntax">{{ query.sql|safe }}</td>
</tr>
{% endfor %}