diff options
| -rw-r--r-- | NEWS | 22 | ||||
| -rw-r--r-- | README.rst | 2 | ||||
| -rw-r--r-- | debug_toolbar/__init__.py | 2 | ||||
| -rw-r--r-- | debug_toolbar/media/debug_toolbar/css/toolbar.css | 6 | ||||
| -rw-r--r-- | debug_toolbar/media/debug_toolbar/css/toolbar.min.css | 2 | ||||
| -rw-r--r-- | debug_toolbar/media/debug_toolbar/js/toolbar.js | 6 | ||||
| -rw-r--r-- | debug_toolbar/media/debug_toolbar/js/toolbar.min.js | 2 | ||||
| -rw-r--r-- | debug_toolbar/middleware.py | 16 | ||||
| -rw-r--r-- | debug_toolbar/panels/logger.py | 83 | ||||
| -rw-r--r-- | debug_toolbar/panels/sql.py | 108 | ||||
| -rw-r--r-- | debug_toolbar/templates/debug_toolbar/base.html | 1 | ||||
| -rw-r--r-- | debug_toolbar/templates/debug_toolbar/panels/logger.html | 2 | ||||
| -rw-r--r-- | debug_toolbar/templates/debug_toolbar/panels/sql.html | 2 | 
13 files changed, 215 insertions, 39 deletions
| @@ -1,6 +1,28 @@  News for django-debug-toolbar  ============================= +0.8.5 (2011 Apr 25) +------------------- + +* Ensure if we're overriding the urlconf that we're resetting handler404/500. + +* Updated middleware logic to avoid work if content-type isn't right. + +* Change .load() calls to GET to avoid CSRF protection. + +* Updated SQL panel to match Django's which now includes logging. + +* Added basic multi-db support. + +* Some HTML validation fixes. + +* Added support for `executemany`. Thanks to postal2600. + +* Added support for LogBook. Thanks to Vincent Driessen. + +* Added clean_params method to DatabaseStatTracker to scrub non-unicode +  data for displaying on the sql panel. Thanks to Matthew J Morrison +  0.8.4 (2010 Nov 8)  ------------------ @@ -16,7 +16,7 @@ Currently, the following panels have been written and are working:  - Templates and context used, and their template paths  - SQL queries including time to execute and links to EXPLAIN each query  - List of signals, their args and receivers -- Logging output via Python's built-in logging module +- Logging output via Python's built-in logging, or via the `logbook <http://logbook.pocoo.org>`_ module  There is also one Django management command currently: diff --git a/debug_toolbar/__init__.py b/debug_toolbar/__init__.py index df5bb74..90f9e2f 100644 --- a/debug_toolbar/__init__.py +++ b/debug_toolbar/__init__.py @@ -1,2 +1,2 @@ -VERSION = (0, 8, 4) +VERSION = (0, 8, 5)  __version__ = '.'.join(map(str, VERSION)) diff --git a/debug_toolbar/media/debug_toolbar/css/toolbar.css b/debug_toolbar/media/debug_toolbar/css/toolbar.css index 223869d..c321bcc 100644 --- a/debug_toolbar/media/debug_toolbar/css/toolbar.css +++ b/debug_toolbar/media/debug_toolbar/css/toolbar.css @@ -394,3 +394,9 @@  #djDebug .highlight .nv { color:#333 } /* Name.Variable */  #djDebug .highlight .s2 { color:#333 } /* Literal.String.Double */  #djDebug .highlight .cp { color:#333 } /* Comment.Preproc */ + +@media print { +    #djDebug { +        display: none; +    } +} diff --git a/debug_toolbar/media/debug_toolbar/css/toolbar.min.css b/debug_toolbar/media/debug_toolbar/css/toolbar.min.css index 2f2d7db..0913bfb 100644 --- a/debug_toolbar/media/debug_toolbar/css/toolbar.min.css +++ b/debug_toolbar/media/debug_toolbar/css/toolbar.min.css @@ -1 +1 @@ -#djDebug{color:#000;background:#FFF;}#djDebug,#djDebug div,#djDebug span,#djDebug applet,#djDebug object,#djDebug iframe,#djDebug h1,#djDebug h2,#djDebug h3,#djDebug h4,#djDebug h5,#djDebug h6,#djDebug p,#djDebug blockquote,#djDebug pre,#djDebug a,#djDebug abbr,#djDebug acronym,#djDebug address,#djDebug big,#djDebug cite,#djDebug code,#djDebug del,#djDebug dfn,#djDebug em,#djDebug font,#djDebug img,#djDebug ins,#djDebug kbd,#djDebug q,#djDebug s,#djDebug samp,#djDebug small,#djDebug strike,#djDebug strong,#djDebug sub,#djDebug sup,#djDebug tt,#djDebug var,#djDebug b,#djDebug u,#djDebug i,#djDebug center,#djDebug dl,#djDebug dt,#djDebug dd,#djDebug ol,#djDebug ul,#djDebug li,#djDebug fieldset,#djDebug form,#djDebug label,#djDebug legend,#djDebug table,#djDebug caption,#djDebug tbody,#djDebug tfoot,#djDebug thead,#djDebug tr,#djDebug th,#djDebug td{margin:0;padding:0;border:0;outline:0;font-size:12px;line-height:1.5em;color:#000;vertical-align:baseline;background:transparent;font-family:sans-serif;text-align:left;}#djDebug #djDebugToolbar{background:#111;width:200px;z-index:100000000;position:fixed;top:0;bottom:0;right:0;opacity:.9;}#djDebug #djDebugToolbar small{color:#999;}#djDebug #djDebugToolbar ul{margin:0;padding:0;list-style:none;}#djDebug #djDebugToolbar li{border-bottom:1px solid #222;color:#fff;display:block;font-weight:bold;float:none;margin:0;padding:0;position:relative;width:auto;}#djDebug #djDebugToolbar li>a,#djDebug #djDebugToolbar li>div.contentless{font-weight:normal;font-style:normal;text-decoration:none;display:block;font-size:16px;padding:10px 10px 5px 25px;color:#fff;}#djDebug #djDebugToolbar li a:hover{color:#111;background-color:#ffc;}#djDebug #djDebugToolbar li.active{background-image:url(../img/indicator.png);background-repeat:no-repeat;background-position:left center;background-color:#333;padding-left:10px;}#djDebug #djDebugToolbar li.active a:hover{color:#b36a60;background-color:transparent;}#djDebug #djDebugToolbar li small{font-size:12px;color:#999;font-style:normal;text-decoration:none;font-variant:small-caps;}#djDebug #djDebugToolbarHandle{position:fixed;background:#fff;border:1px solid #111;top:30px;right:0;z-index:100000000;opacity:.75;}#djDebug a#djShowToolBarButton{display:block;height:75px;width:30px;border-right:none;border-bottom:4px solid #fff;border-top:4px solid #fff;border-left:4px solid #fff;color:#fff;font-size:10px;font-weight:bold;text-decoration:none;text-align:center;text-indent:-999999px;background:#000 url(../img/djdt_vertical.png) no-repeat left center;opacity:.5;}#djDebug a#djShowToolBarButton:hover{background-color:#111;padding-right:6px;border-top-color:#FFE761;border-left-color:#FFE761;border-bottom-color:#FFE761;opacity:1.0;}#djDebug code{display:block;font-family:Consolas,Monaco,"Bitstream Vera Sans Mono","Lucida Console",monospace;white-space:pre;overflow:auto;}#djDebug tr.djDebugOdd{background-color:#f5f5f5;}#djDebug .panelContent{display:none;position:fixed;margin:0;top:0;right:200px;bottom:0;left:0;background-color:#eee;color:#666;z-index:100000000;}#djDebug .panelContent>div{border-bottom:1px solid #ddd;}#djDebug .djDebugPanelTitle{position:absolute;background-color:#ffc;color:#666;padding-left:20px;top:0;right:0;left:0;height:50px;}#djDebug .djDebugPanelTitle code{display:inline;font-size:inherit;}#djDebug .djDebugPanelContent{position:absolute;top:50px;right:0;bottom:0;left:0;height:auto;padding:0 0 0 20px;}#djDebug .djDebugPanelContent .scroll{height:100%;overflow:auto;display:block;padding:0 10px 0 0;}#djDebug h3{font-size:24px;font-weight:normal;line-height:50px;}#djDebug h4{font-size:20px;font-weight:bold;margin-top:.8em;}#djDebug .panelContent table{border:1px solid #ccc;border-collapse:collapse;width:100%;background-color:#fff;display:block;margin-top:.8em;overflow:auto;}#djDebug .panelContent tbody td,#djDebug .panelContent tbody th{vertical-align:top;padding:2px 3px;}#djDebug .panelContent thead th{padding:1px 6px 1px 3px;text-align:left;font-weight:bold;font-size:14px;}#djDebug .panelContent tbody th{width:12em;text-align:right;color:#666;padding-right:.5em;}#djDebug .djTemplateHideContextDiv{background-color:#fff;}#djDebug .panelContent .djDebugClose{text-indent:-9999999px;display:block;position:absolute;top:4px;right:15px;height:40px;width:40px;background:url(../img/close.png) no-repeat center center;}#djDebug .panelContent .djDebugClose:hover{background-image:url(../img/close_hover.png);}#djDebug .panelContent .djDebugClose.djDebugBack{background-image:url(../img/back.png);}#djDebug .panelContent .djDebugClose.djDebugBack:hover{background-image:url(../img/back_hover.png);}#djDebug .panelContent dt,#djDebug .panelContent dd{display:block;}#djDebug .panelContent dt{margin-top:.75em;}#djDebug .panelContent dd{margin-left:10px;}#djDebug a.toggleTemplate{padding:4px;background-color:#bbb;-moz-border-radius:3px;-webkit-border-radius:3px;}#djDebug a.toggleTemplate:hover{padding:4px;background-color:#444;color:#ffe761;-moz-border-radius:3px;-webkit-border-radius:3px;}#djDebug a.djTemplateShowContext,#djDebug a.djTemplateShowContext span.toggleArrow{color:#999;}#djDebug a.djTemplateShowContext:hover,#djDebug a.djTemplateShowContext:hover span.toggleArrow{color:#000;cursor:pointer;}#djDebug .djDebugSqlWrap{position:relative;}#djDebug .djDebugSql{z-index:100000002;}#djDebug .djSQLHideStacktraceDiv tbody th{text-align:left;}#djDebug .djSqlExplain td{white-space:pre;}#djDebug span.djDebugLineChart{background-color:#777;height:3px;position:absolute;bottom:0;top:0;left:0;display:block;z-index:1000000001;}#djDebug span.djDebugLineChartWarning{background-color:#900;}#djDebug .highlight{color:#000;}#djDebug .highlight .err{color:#000;}#djDebug .highlight .g{color:#000;}#djDebug .highlight .k{color:#000;font-weight:bold;}#djDebug .highlight .o{color:#000;}#djDebug .highlight .n{color:#000;}#djDebug .highlight .mi{color:#000;font-weight:bold;}#djDebug .highlight .l{color:#000;}#djDebug .highlight .x{color:#000;}#djDebug .highlight .p{color:#000;}#djDebug .highlight .m{color:#000;font-weight:bold;}#djDebug .highlight .s{color:#333;}#djDebug .highlight .w{color:#888;}#djDebug .highlight .il{color:#000;font-weight:bold;}#djDebug .highlight .na{color:#333;}#djDebug .highlight .nt{color:#000;font-weight:bold;}#djDebug .highlight .nv{color:#333;}#djDebug .highlight .s2{color:#333;}#djDebug .highlight .cp{color:#333;}
\ No newline at end of file +#djDebug{color:#000;background:#FFF;}#djDebug,#djDebug div,#djDebug span,#djDebug applet,#djDebug object,#djDebug iframe,#djDebug h1,#djDebug h2,#djDebug h3,#djDebug h4,#djDebug h5,#djDebug h6,#djDebug p,#djDebug blockquote,#djDebug pre,#djDebug a,#djDebug abbr,#djDebug acronym,#djDebug address,#djDebug big,#djDebug cite,#djDebug code,#djDebug del,#djDebug dfn,#djDebug em,#djDebug font,#djDebug img,#djDebug ins,#djDebug kbd,#djDebug q,#djDebug s,#djDebug samp,#djDebug small,#djDebug strike,#djDebug strong,#djDebug sub,#djDebug sup,#djDebug tt,#djDebug var,#djDebug b,#djDebug u,#djDebug i,#djDebug center,#djDebug dl,#djDebug dt,#djDebug dd,#djDebug ol,#djDebug ul,#djDebug li,#djDebug fieldset,#djDebug form,#djDebug label,#djDebug legend,#djDebug table,#djDebug caption,#djDebug tbody,#djDebug tfoot,#djDebug thead,#djDebug tr,#djDebug th,#djDebug td{margin:0;padding:0;border:0;outline:0;font-size:12px;line-height:1.5em;color:#000;vertical-align:baseline;background:transparent;font-family:sans-serif;text-align:left;}#djDebug #djDebugToolbar{background:#111;width:200px;z-index:100000000;position:fixed;top:0;bottom:0;right:0;opacity:.9;}#djDebug #djDebugToolbar small{color:#999;}#djDebug #djDebugToolbar ul{margin:0;padding:0;list-style:none;}#djDebug #djDebugToolbar li{border-bottom:1px solid #222;color:#fff;display:block;font-weight:bold;float:none;margin:0;padding:0;position:relative;width:auto;}#djDebug #djDebugToolbar li>a,#djDebug #djDebugToolbar li>div.contentless{font-weight:normal;font-style:normal;text-decoration:none;display:block;font-size:16px;padding:10px 10px 5px 25px;color:#fff;}#djDebug #djDebugToolbar li a:hover{color:#111;background-color:#ffc;}#djDebug #djDebugToolbar li.active{background-image:url(../img/indicator.png);background-repeat:no-repeat;background-position:left center;background-color:#333;padding-left:10px;}#djDebug #djDebugToolbar li.active a:hover{color:#b36a60;background-color:transparent;}#djDebug #djDebugToolbar li small{font-size:12px;color:#999;font-style:normal;text-decoration:none;font-variant:small-caps;}#djDebug #djDebugToolbarHandle{position:fixed;background:#fff;border:1px solid #111;top:30px;right:0;z-index:100000000;opacity:.75;}#djDebug a#djShowToolBarButton{display:block;height:75px;width:30px;border-right:none;border-bottom:4px solid #fff;border-top:4px solid #fff;border-left:4px solid #fff;color:#fff;font-size:10px;font-weight:bold;text-decoration:none;text-align:center;text-indent:-999999px;background:#000 url(../img/djdt_vertical.png) no-repeat left center;opacity:.5;}#djDebug a#djShowToolBarButton:hover{background-color:#111;padding-right:6px;border-top-color:#FFE761;border-left-color:#FFE761;border-bottom-color:#FFE761;opacity:1.0;}#djDebug code{display:block;font-family:Consolas,Monaco,"Bitstream Vera Sans Mono","Lucida Console",monospace;white-space:pre;overflow:auto;}#djDebug tr.djDebugOdd{background-color:#f5f5f5;}#djDebug .panelContent{display:none;position:fixed;margin:0;top:0;right:200px;bottom:0;left:0;background-color:#eee;color:#666;z-index:100000000;}#djDebug .panelContent>div{border-bottom:1px solid #ddd;}#djDebug .djDebugPanelTitle{position:absolute;background-color:#ffc;color:#666;padding-left:20px;top:0;right:0;left:0;height:50px;}#djDebug .djDebugPanelTitle code{display:inline;font-size:inherit;}#djDebug .djDebugPanelContent{position:absolute;top:50px;right:0;bottom:0;left:0;height:auto;padding:0 0 0 20px;}#djDebug .djDebugPanelContent .scroll{height:100%;overflow:auto;display:block;padding:0 10px 0 0;}#djDebug h3{font-size:24px;font-weight:normal;line-height:50px;}#djDebug h4{font-size:20px;font-weight:bold;margin-top:.8em;}#djDebug .panelContent table{border:1px solid #ccc;border-collapse:collapse;width:100%;background-color:#fff;display:block;margin-top:.8em;overflow:auto;}#djDebug .panelContent tbody td,#djDebug .panelContent tbody th{vertical-align:top;padding:2px 3px;}#djDebug .panelContent thead th{padding:1px 6px 1px 3px;text-align:left;font-weight:bold;font-size:14px;}#djDebug .panelContent tbody th{width:12em;text-align:right;color:#666;padding-right:.5em;}#djDebug .djTemplateHideContextDiv{background-color:#fff;}#djDebug .panelContent .djDebugClose{text-indent:-9999999px;display:block;position:absolute;top:4px;right:15px;height:40px;width:40px;background:url(../img/close.png) no-repeat center center;}#djDebug .panelContent .djDebugClose:hover{background-image:url(../img/close_hover.png);}#djDebug .panelContent .djDebugClose.djDebugBack{background-image:url(../img/back.png);}#djDebug .panelContent .djDebugClose.djDebugBack:hover{background-image:url(../img/back_hover.png);}#djDebug .panelContent dt,#djDebug .panelContent dd{display:block;}#djDebug .panelContent dt{margin-top:.75em;}#djDebug .panelContent dd{margin-left:10px;}#djDebug a.toggleTemplate{padding:4px;background-color:#bbb;-moz-border-radius:3px;-webkit-border-radius:3px;}#djDebug a.toggleTemplate:hover{padding:4px;background-color:#444;color:#ffe761;-moz-border-radius:3px;-webkit-border-radius:3px;}#djDebug a.djTemplateShowContext,#djDebug a.djTemplateShowContext span.toggleArrow{color:#999;}#djDebug a.djTemplateShowContext:hover,#djDebug a.djTemplateShowContext:hover span.toggleArrow{color:#000;cursor:pointer;}#djDebug .djDebugSqlWrap{position:relative;}#djDebug .djDebugSql{z-index:100000002;}#djDebug .djSQLHideStacktraceDiv tbody th{text-align:left;}#djDebug .djSqlExplain td{white-space:pre;}#djDebug span.djDebugLineChart{background-color:#777;height:3px;position:absolute;bottom:0;top:0;left:0;display:block;z-index:1000000001;}#djDebug span.djDebugLineChartWarning{background-color:#900;}#djDebug .highlight{color:#000;}#djDebug .highlight .err{color:#000;}#djDebug .highlight .g{color:#000;}#djDebug .highlight .k{color:#000;font-weight:bold;}#djDebug .highlight .o{color:#000;}#djDebug .highlight .n{color:#000;}#djDebug .highlight .mi{color:#000;font-weight:bold;}#djDebug .highlight .l{color:#000;}#djDebug .highlight .x{color:#000;}#djDebug .highlight .p{color:#000;}#djDebug .highlight .m{color:#000;font-weight:bold;}#djDebug .highlight .s{color:#333;}#djDebug .highlight .w{color:#888;}#djDebug .highlight .il{color:#000;font-weight:bold;}#djDebug .highlight .na{color:#333;}#djDebug .highlight .nt{color:#000;font-weight:bold;}#djDebug .highlight .nv{color:#333;}#djDebug .highlight .s2{color:#333;}#djDebug .highlight .cp{color:#333;}@media print{#djDebug{display:none;}}
\ No newline at end of file diff --git a/debug_toolbar/media/debug_toolbar/js/toolbar.js b/debug_toolbar/media/debug_toolbar/js/toolbar.js index 33970f8..a43d834 100644 --- a/debug_toolbar/media/debug_toolbar/js/toolbar.js +++ b/debug_toolbar/media/debug_toolbar/js/toolbar.js @@ -44,7 +44,11 @@  				return false;  			});  			$('#djDebug a.remoteCall').click(function() { -				$('#djDebugWindow').load(this.href, {}, function() { +				$('#djDebugWindow').load(this.href, function(response, status, xhr) { +					if (status == "error") { +						var message = '<div class="djDebugPanelTitle"><a class="djDebugClose djDebugBack" href="">Back</a><h3>'+xhr.status+': '+xhr.statusText+'</h3></div>'; +						$('#djDebugWindow').html(message); +					}  					$('#djDebugWindow a.djDebugBack').click(function() {  						$(this).parent().parent().hide();  						return false; diff --git a/debug_toolbar/media/debug_toolbar/js/toolbar.min.js b/debug_toolbar/media/debug_toolbar/js/toolbar.min.js index 81b3f85..73c01b8 100644 --- a/debug_toolbar/media/debug_toolbar/js/toolbar.min.js +++ b/debug_toolbar/media/debug_toolbar/js/toolbar.min.js @@ -1 +1 @@ -(function(g,a,b,i){var f,h;var e=false;if(!(f=g.jQuery)||b>f.fn.jquery||i(f)){var c=a.createElement("script");c.type="text/javascript";c.src=DEBUG_TOOLBAR_MEDIA_URL+"js/jquery.js";c.onload=c.onreadystatechange=function(){if(!e&&(!(h=this.readyState)||h=="loaded"||h=="complete")){i((f=g.jQuery).noConflict(1),e=true);f(c).remove()}};a.documentElement.childNodes[0].appendChild(c)}})(window,document,"1.3",function(b,a){b.cookie=function(f,n,q){if(typeof n!="undefined"){q=q||{};if(n===null){n="";q.expires=-1}var j="";if(q.expires&&(typeof q.expires=="number"||q.expires.toUTCString)){var k;if(typeof q.expires=="number"){k=new Date();k.setTime(k.getTime()+(q.expires*24*60*60*1000))}else{k=q.expires}j="; expires="+k.toUTCString()}var p=q.path?"; path="+(q.path):"";var l=q.domain?"; domain="+(q.domain):"";var e=q.secure?"; secure":"";document.cookie=[f,"=",encodeURIComponent(n),j,p,l,e].join("")}else{var h=null;if(document.cookie&&document.cookie!=""){var o=document.cookie.split(";");for(var m=0;m<o.length;m++){var g=b.trim(o[m]);if(g.substring(0,f.length+1)==(f+"=")){h=decodeURIComponent(g.substring(f.length+1));break}}}return h}};b("head").append('<link rel="stylesheet" href="'+DEBUG_TOOLBAR_MEDIA_URL+'css/toolbar.min.css" type="text/css" />');var d="djdt";var c={init:function(){b("#djDebug").show();var e=null;b("#djDebugPanelList li a").click(function(){if(!this.className){return false}e=b("#djDebug #"+this.className);if(e.is(":visible")){b(document).trigger("close.djDebug");b(this).parent().removeClass("active")}else{b(".panelContent").hide();e.show();b("#djDebugToolbar li").removeClass("active");b(this).parent().addClass("active")}return false});b("#djDebug a.djDebugClose").click(function(){b(document).trigger("close.djDebug");b("#djDebugToolbar li").removeClass("active");return false});b("#djDebug a.remoteCall").click(function(){b("#djDebugWindow").load(this.href,{},function(){b("#djDebugWindow a.djDebugBack").click(function(){b(this).parent().parent().hide();return false})});b("#djDebugWindow").show();return false});b("#djDebugTemplatePanel a.djTemplateShowContext").click(function(){c.toggle_arrow(b(this).children(".toggleArrow"));c.toggle_content(b(this).parent().next());return false});b("#djDebugSQLPanel a.djSQLShowStacktrace").click(function(){c.toggle_content(b(".djSQLHideStacktraceDiv",b(this).parents("tr")));return false});b("#djHideToolBarButton").click(function(){c.hide_toolbar(true);return false});b("#djShowToolBarButton").click(function(){c.show_toolbar();return false});b(document).bind("close.djDebug",function(){if(b("#djDebugWindow").is(":visible")){b("#djDebugWindow").hide();return}if(b(".panelContent").is(":visible")){b(".panelContent").hide();return}if(b("#djDebugToolbar").is(":visible")){c.hide_toolbar(true);return}});if(b.cookie(d)){c.hide_toolbar(false)}else{c.show_toolbar(false)}},toggle_content:function(e){if(e.is(":visible")){e.hide()}else{e.show()}},close:function(){b(document).trigger("close.djDebug");return false},hide_toolbar:function(e){b("#djDebugWindow").hide();b(".panelContent").hide();b("#djDebugToolbar li").removeClass("active");b("#djDebugToolbar").hide("fast");b("#djDebugToolbarHandle").show();b(document).unbind("keydown.djDebug");if(e){b.cookie(d,"hide",{path:"/",expires:10})}},show_toolbar:function(e){b(document).bind("keydown.djDebug",function(f){if(f.keyCode==27){c.close()}});b("#djDebugToolbarHandle").hide();if(e){b("#djDebugToolbar").show("fast")}else{b("#djDebugToolbar").show()}b.cookie(d,null,{path:"/",expires:-1})},toggle_arrow:function(f){var e=String.fromCharCode(9654);var g=String.fromCharCode(9660);f.html(f.html()==e?g:e)}};b(document).ready(function(){c.init()})});
\ No newline at end of file +(function(g,a,b,i){var f,h;var e=false;if(!(f=g.jQuery)||b>f.fn.jquery||i(f)){var c=a.createElement("script");c.type="text/javascript";c.src=DEBUG_TOOLBAR_MEDIA_URL+"js/jquery.js";c.onload=c.onreadystatechange=function(){if(!e&&(!(h=this.readyState)||h=="loaded"||h=="complete")){i((f=g.jQuery).noConflict(1),e=true);f(c).remove()}};a.documentElement.childNodes[0].appendChild(c)}})(window,document,"1.3",function(b,a){b.cookie=function(f,n,q){if(typeof n!="undefined"){q=q||{};if(n===null){n="";q.expires=-1}var j="";if(q.expires&&(typeof q.expires=="number"||q.expires.toUTCString)){var k;if(typeof q.expires=="number"){k=new Date();k.setTime(k.getTime()+(q.expires*24*60*60*1000))}else{k=q.expires}j="; expires="+k.toUTCString()}var p=q.path?"; path="+(q.path):"";var l=q.domain?"; domain="+(q.domain):"";var e=q.secure?"; secure":"";document.cookie=[f,"=",encodeURIComponent(n),j,p,l,e].join("")}else{var h=null;if(document.cookie&&document.cookie!=""){var o=document.cookie.split(";");for(var m=0;m<o.length;m++){var g=b.trim(o[m]);if(g.substring(0,f.length+1)==(f+"=")){h=decodeURIComponent(g.substring(f.length+1));break}}}return h}};b("head").append('<link rel="stylesheet" href="'+DEBUG_TOOLBAR_MEDIA_URL+'css/toolbar.min.css" type="text/css" />');var d="djdt";var c={init:function(){b("#djDebug").show();var e=null;b("#djDebugPanelList li a").click(function(){if(!this.className){return false}e=b("#djDebug #"+this.className);if(e.is(":visible")){b(document).trigger("close.djDebug");b(this).parent().removeClass("active")}else{b(".panelContent").hide();e.show();b("#djDebugToolbar li").removeClass("active");b(this).parent().addClass("active")}return false});b("#djDebug a.djDebugClose").click(function(){b(document).trigger("close.djDebug");b("#djDebugToolbar li").removeClass("active");return false});b("#djDebug a.remoteCall").click(function(){b("#djDebugWindow").load(this.href,function(g,f,i){if(f=="error"){var h='<div class="djDebugPanelTitle"><a class="djDebugClose djDebugBack" href="">Back</a><h3>'+i.status+": "+i.statusText+"</h3></div>";b("#djDebugWindow").html(h)}b("#djDebugWindow a.djDebugBack").click(function(){b(this).parent().parent().hide();return false})});b("#djDebugWindow").show();return false});b("#djDebugTemplatePanel a.djTemplateShowContext").click(function(){c.toggle_arrow(b(this).children(".toggleArrow"));c.toggle_content(b(this).parent().next());return false});b("#djDebugSQLPanel a.djSQLShowStacktrace").click(function(){c.toggle_content(b(".djSQLHideStacktraceDiv",b(this).parents("tr")));return false});b("#djHideToolBarButton").click(function(){c.hide_toolbar(true);return false});b("#djShowToolBarButton").click(function(){c.show_toolbar();return false});b(document).bind("close.djDebug",function(){if(b("#djDebugWindow").is(":visible")){b("#djDebugWindow").hide();return}if(b(".panelContent").is(":visible")){b(".panelContent").hide();return}if(b("#djDebugToolbar").is(":visible")){c.hide_toolbar(true);return}});if(b.cookie(d)){c.hide_toolbar(false)}else{c.show_toolbar(false)}},toggle_content:function(e){if(e.is(":visible")){e.hide()}else{e.show()}},close:function(){b(document).trigger("close.djDebug");return false},hide_toolbar:function(e){b("#djDebugWindow").hide();b(".panelContent").hide();b("#djDebugToolbar li").removeClass("active");b("#djDebugToolbar").hide("fast");b("#djDebugToolbarHandle").show();b(document).unbind("keydown.djDebug");if(e){b.cookie(d,"hide",{path:"/",expires:10})}},show_toolbar:function(e){b(document).bind("keydown.djDebug",function(f){if(f.keyCode==27){c.close()}});b("#djDebugToolbarHandle").hide();if(e){b("#djDebugToolbar").show("fast")}else{b("#djDebugToolbar").show()}b.cookie(d,null,{path:"/",expires:-1})},toggle_arrow:function(f){var e=String.fromCharCode(9654);var g=String.fromCharCode(9660);f.html(f.html()==e?g:e)}};b(document).ready(function(){c.init()})});
\ No newline at end of file diff --git a/debug_toolbar/middleware.py b/debug_toolbar/middleware.py index de78254..7f0f4bc 100644 --- a/debug_toolbar/middleware.py +++ b/debug_toolbar/middleware.py @@ -67,10 +67,14 @@ class DebugToolbarMiddleware(object):      def process_request(self, request):          if self.show_toolbar(request):              if self.override_url: -                original_urlconf = getattr(request, 'urlconf', settings.ROOT_URLCONF) +                original_urlconf = __import__(getattr(request, 'urlconf', settings.ROOT_URLCONF), {}, {}, ['*'])                  debug_toolbar.urls.urlpatterns += patterns('',                      ('', include(original_urlconf)),                  ) +                if hasattr(original_urlconf, 'handler404'): +                    debug_toolbar.urls.handler404 = original_urlconf.handler404 +                if hasattr(original_urlconf, 'handler500'): +                    debug_toolbar.urls.handler500 = original_urlconf.handler500                  self.override_url = False              request.urlconf = 'debug_toolbar.urls' @@ -96,15 +100,15 @@ class DebugToolbarMiddleware(object):                          {'redirect_to': redirect_to}                      )                      response.cookies = cookies -        if response.status_code == 200: -            for panel in self.debug_toolbars[request].panels: -                panel.process_response(request, response) +        if response.status_code == 200 and 'gzip' not in response.get('Content-Encoding', ''):              if response['Content-Type'].split(';')[0] in _HTML_TYPES: +                for panel in self.debug_toolbars[request].panels: +                    panel.process_response(request, response)                  response.content = replace_insensitive(                      smart_unicode(response.content),                       self.tag,                      smart_unicode(self.debug_toolbars[request].render_toolbar() + self.tag)) -            if response.get('Content-Length', None): -                response['Content-Length'] = len(response.content) +                if response.get('Content-Length', None): +                    response['Content-Length'] = len(response.content)          del self.debug_toolbars[request]          return response diff --git a/debug_toolbar/panels/logger.py b/debug_toolbar/panels/logger.py index 620102e..5e82a13 100644 --- a/debug_toolbar/panels/logger.py +++ b/debug_toolbar/panels/logger.py @@ -8,16 +8,21 @@ from django.template.loader import render_to_string  from django.utils.translation import ugettext_lazy as _  from debug_toolbar.panels import DebugPanel -class ThreadTrackingHandler(logging.Handler): + +class LogCollector(object):      def __init__(self):          if threading is None:              raise NotImplementedError("threading module is not available, \                  the logging panel cannot be used without it") -        logging.Handler.__init__(self)          self.records = {} # a dictionary that maps threads to log records -    def emit(self, record): -        self.get_records().append(record) +    def add_record(self, record, thread=None): +        # Avoid logging SQL queries since they are already in the SQL panel +        # TODO: Make this check whether SQL panel is enabled +        if record.get('channel', '') == 'django.db.backends': +            return + +        self.get_records(thread).append(record)      def get_records(self, thread=None):          """ @@ -36,20 +41,67 @@ class ThreadTrackingHandler(logging.Handler):          if thread in self.records:              del self.records[thread] -handler = ThreadTrackingHandler() + +class ThreadTrackingHandler(logging.Handler): +    def __init__(self, collector): +        logging.Handler.__init__(self) +        self.collector = collector + +    def emit(self, record): +        record = { +            'message': record.getMessage(), +            'time': datetime.datetime.fromtimestamp(record.created), +            'level': record.levelname, +            'file': record.pathname, +            'line': record.lineno, +            'channel': record.name, +        } +        self.collector.add_record(record) + + +collector = LogCollector() +logging_handler = ThreadTrackingHandler(collector)  logging.root.setLevel(logging.NOTSET) -logging.root.addHandler(handler) +logging.root.addHandler(logging_handler)  # register with logging + +try: +    import logbook +    logbook_supported = True +except ImportError: +    # logbook support is optional, so fail silently +    logbook_supported = False + +if logbook_supported: +    class LogbookThreadTrackingHandler(logbook.handlers.Handler): +        def __init__(self, collector): +            logbook.handlers.Handler.__init__(self, bubble=True) +            self.collector = collector + +        def emit(self, record): +            record = { +                'message': record.message, +                'time': record.time, +                'level': record.level_name, +                'file': record.filename, +                'line': record.lineno, +                'channel': record.channel, +            } +            self.collector.add_record(record) + + +    logbook_handler = LogbookThreadTrackingHandler(collector) +    logbook_handler.push_application()        # register with logbook  class LoggingPanel(DebugPanel):      name = 'Logging'      has_content = True      def process_request(self, request): -        handler.clear_records() +        collector.clear_records()      def get_and_delete(self): -        records = handler.get_records() -        handler.clear_records() +        records = collector.get_records() +        collector.clear_records()          return records      def nav_title(self): @@ -57,7 +109,7 @@ class LoggingPanel(DebugPanel):      def nav_subtitle(self):          # FIXME l10n: use ngettext -        return "%s message%s" % (len(handler.get_records()), (len(handler.get_records()) == 1) and '' or 's') +        return "%s message%s" % (len(collector.get_records()), (len(collector.get_records()) == 1) and '' or 's')      def title(self):          return _('Log Messages') @@ -66,16 +118,7 @@ class LoggingPanel(DebugPanel):          return ''      def content(self): -        records = [] -        for record in self.get_and_delete(): -            records.append({ -                'message': record.getMessage(), -                'time': datetime.datetime.fromtimestamp(record.created), -                'level': record.levelname, -                'file': record.pathname, -                'line': record.lineno, -            }) - +        records = self.get_and_delete()          context = self.context.copy()          context.update({'records': records}) diff --git a/debug_toolbar/panels/sql.py b/debug_toolbar/panels/sql.py index e1e9bdf..c6e73a0 100644 --- a/debug_toolbar/panels/sql.py +++ b/debug_toolbar/panels/sql.py @@ -6,14 +6,29 @@ import traceback  import django  from django.conf import settings -from django.db import connection +try: +    from django.db import connections +except ImportError: +    # Compatibility with Django < 1.2 +    from django.db import connection +    connections = {'default': connection} +    connection.alias = 'default' +  from django.db.backends import util  from django.views.debug import linebreak_iter  from django.template import Node  from django.template.loader import render_to_string  from django.utils import simplejson -from django.utils.encoding import force_unicode +from django.utils.encoding import force_unicode, DjangoUnicodeDecodeError  from django.utils.hashcompat import sha_constructor +try: +    from django.utils.log import getLogger +    logger = getLogger('django.db.backends') +    has_logger = True +except ImportError: +    # Compatibility with Django < 1.2 +    has_logger = False +      from django.utils.translation import ugettext_lazy as _  from debug_toolbar.panels import DebugPanel @@ -85,12 +100,73 @@ class DatabaseStatTracker(util.CursorDebugWrapper):      Replacement for CursorDebugWrapper which stores additional information      in `connection.queries`.      """ +    def clean_params(self, params): +        clean_params = () +        for x in params: +            try: +                force_unicode(x, strings_only=True) +            except DjangoUnicodeDecodeError: +                clean_params += ("<non unicode object>", ) +            else: +                clean_params += (x, ) +        return clean_params +      def execute(self, sql, params=()):          start = datetime.now()          try:              return self.cursor.execute(sql, params)          finally:              stop = datetime.now() +            params = self.clean_params(params) +            duration = ms_from_timedelta(stop - start) +            stacktrace = tidy_stacktrace(traceback.extract_stack()) +            _params = '' +            try: +                _params = simplejson.dumps([force_unicode(x, strings_only=True) for x in params]) +            except TypeError: +                pass # object not JSON serializable + +            template_info = None +            cur_frame = sys._getframe().f_back +            try: +                while cur_frame is not None: +                    if cur_frame.f_code.co_name == 'render': +                        node = cur_frame.f_locals['self'] +                        if isinstance(node, Node): +                            template_info = get_template_info(node.source) +                            break +                    cur_frame = cur_frame.f_back +            except: +                pass +            del cur_frame + +            # Logging was added in Django 1.3 +            if has_logger: +                logger.debug('(%.3f) %s; args=%s' % (duration, sql, params), +                    extra={'duration':duration, 'sql':sql, 'params':params} +                ) + +            # We keep `sql` to maintain backwards compatibility +            self.db.queries.append({ +                'sql': self.db.ops.last_executed_query(self.cursor, sql, params), +                'duration': duration, +                'raw_sql': sql, +                'params': _params, +                'hash': sha_constructor(settings.SECRET_KEY + sql + _params).hexdigest(), +                'stacktrace': stacktrace, +                'start_time': start, +                'stop_time': stop, +                'is_slow': (duration > SQL_WARNING_THRESHOLD), +                'is_select': sql.lower().strip().startswith('select'), +                'template_info': template_info, +            }) + +    def executemany(self, sql, params=()): +        start = datetime.now() +        try: +            return self.cursor.executemany(sql, params) +        finally: +            stop = datetime.now()              duration = ms_from_timedelta(stop - start)              stacktrace = tidy_stacktrace(traceback.extract_stack())              _params = '' @@ -113,6 +189,11 @@ class DatabaseStatTracker(util.CursorDebugWrapper):                  pass              del cur_frame +            if has_logger: +                logger.debug('(%.3f) %s; args=%s' % (duration, sql, params), +                    extra={'duration':duration, 'sql':sql, 'params':params} +                ) +              # We keep `sql` to maintain backwards compatibility              self.db.queries.append({                  'sql': self.db.ops.last_executed_query(self.cursor, sql, params), @@ -139,16 +220,27 @@ class SQLDebugPanel(DebugPanel):      def __init__(self, *args, **kwargs):          super(self.__class__, self).__init__(*args, **kwargs) -        self._offset = len(connection.queries) +        self._offset = dict((conn, len(connections[conn].queries)) for conn in connections)          self._sql_time = 0          self._queries = [] +        self._databases = {}      def nav_title(self):          return _('SQL')      def nav_subtitle(self): -        self._queries = connection.queries[self._offset:] -        self._sql_time = sum([q['duration'] for q in self._queries]) +        self._queries = [] +        self._databases = {} +        for alias in connections: +            db_queries = connections[alias].queries[self._offset[alias]:] +            self._databases[alias] = { +                'time_spent': sum(q['duration'] for q in db_queries), +                'queries': len(db_queries), +            } +            self._queries.extend([(alias, q) for q in db_queries]) + +        self._queries.sort(key=lambda x: x[1]['start_time']) +        self._sql_time = sum([d['time_spent'] for d in self._databases.itervalues()])          num_queries = len(self._queries)          # TODO l10n: use ngettext          return "%d %s in %.2fms" % ( @@ -165,7 +257,8 @@ class SQLDebugPanel(DebugPanel):      def content(self):          width_ratio_tally = 0 -        for query in self._queries: +        for alias, query in self._queries: +            query['alias'] = alias              query['sql'] = reformat_sql(query['sql'])              try:                  query['width_ratio'] = (query['duration'] / self._sql_time) * 100 @@ -176,7 +269,8 @@ class SQLDebugPanel(DebugPanel):          context = self.context.copy()          context.update({ -            'queries': self._queries, +            'databases': sorted(self._databases.items(), key=lambda x: -x[1]['time_spent']), +            'queries': [q for a, q in self._queries],              'sql_time': self._sql_time,              'is_mysql': settings.DATABASE_ENGINE == 'mysql',          }) diff --git a/debug_toolbar/templates/debug_toolbar/base.html b/debug_toolbar/templates/debug_toolbar/base.html index 0fe64ca..7f7a535 100644 --- a/debug_toolbar/templates/debug_toolbar/base.html +++ b/debug_toolbar/templates/debug_toolbar/base.html @@ -1,5 +1,4 @@  {% load i18n %} -<style type="text/css">@media print { #djDebug {display:none;}}</style>  <script type="text/javascript">  // <