aboutsummaryrefslogtreecommitdiffstats
path: root/debug_toolbar
diff options
context:
space:
mode:
Diffstat (limited to 'debug_toolbar')
-rw-r--r--debug_toolbar/middleware.py10
-rw-r--r--debug_toolbar/panels/__init__.py27
-rw-r--r--debug_toolbar/panels/cache.py25
-rw-r--r--debug_toolbar/panels/logger.py3
-rw-r--r--debug_toolbar/panels/sql.py25
-rw-r--r--debug_toolbar/panels/template.py21
6 files changed, 67 insertions, 44 deletions
diff --git a/debug_toolbar/middleware.py b/debug_toolbar/middleware.py
index 4f5b9bd..80e9305 100644
--- a/debug_toolbar/middleware.py
+++ b/debug_toolbar/middleware.py
@@ -95,6 +95,7 @@ class DebugToolbarMiddleware(object):
panel.enabled = panel.dom_id() not in request.COOKIES
if not panel.enabled:
continue
+ panel.enable_instrumentation()
panel.process_request(request)
self.__class__.debug_toolbars[threading.current_thread().ident] = toolbar
@@ -130,12 +131,13 @@ class DebugToolbarMiddleware(object):
{'redirect_to': redirect_to}
)
response.cookies = cookies
+ for panel in toolbar.panels:
+ if not panel.enabled:
+ continue
+ panel.process_response(request, response)
+ panel.disable_instrumentation()
if ('gzip' not in response.get('Content-Encoding', '') and
response.get('Content-Type', '').split(';')[0] in _HTML_TYPES):
- for panel in toolbar.panels:
- if not panel.enabled:
- continue
- panel.process_response(request, response)
response.content = replace_insensitive(
force_text(response.content, encoding=settings.DEFAULT_CHARSET),
self.tag,
diff --git a/debug_toolbar/panels/__init__.py b/debug_toolbar/panels/__init__.py
index d4a61da..9586e80 100644
--- a/debug_toolbar/panels/__init__.py
+++ b/debug_toolbar/panels/__init__.py
@@ -22,31 +22,44 @@ class DebugPanel(object):
context = {}
# Panel methods
+
def __init__(self, toolbar, context={}):
self.toolbar = toolbar
self.context.update(context)
self.slug = slugify(self.name)
+ def content(self):
+ if self.has_content:
+ context = self.context.copy()
+ context.update(self.get_stats())
+ return render_to_string(self.template, context)
+
def dom_id(self):
return 'djDebug%sPanel' % (self.name.replace(' ', ''))
+ # Titles and subtitles
+
def nav_title(self):
- """Title showing in toolbar"""
+ """Title showing in sidebar"""
raise NotImplementedError
def nav_subtitle(self):
- """Subtitle showing until title in toolbar"""
+ """Subtitle showing under title in sidebar"""
return ''
def title(self):
"""Title showing in panel"""
raise NotImplementedError
- def content(self):
- if self.has_content:
- context = self.context.copy()
- context.update(self.get_stats())
- return render_to_string(self.template, context)
+ # Enable and disable (expensive) instrumentation
+
+ def enable_instrumentation(self):
+ pass
+
+ def disable_instrumentation(self):
+ pass
+
+ # Store and retrieve stats (shared between panels)
def record_stats(self, stats):
panel_stats = self.toolbar.stats.get(self.slug)
diff --git a/debug_toolbar/panels/cache.py b/debug_toolbar/panels/cache.py
index 57da54b..a6b06ce 100644
--- a/debug_toolbar/panels/cache.py
+++ b/debug_toolbar/panels/cache.py
@@ -6,7 +6,7 @@ import time
from django.conf import settings
from django.core import cache
-from django.core.cache import get_cache as base_get_cache
+from django.core.cache import cache as original_cache, get_cache as original_get_cache
from django.core.cache.backends.base import BaseCache
from django.dispatch import Signal
from django.template import Node
@@ -123,6 +123,10 @@ class CacheStatTracker(BaseCache):
return self.cache.decr_version(*args, **kwargs)
+def get_cache(*args, **kwargs):
+ return CacheStatTracker(original_get_cache(*args, **kwargs))
+
+
class CacheDebugPanel(DebugPanel):
"""
Panel that displays the cache statistics.
@@ -195,6 +199,16 @@ class CacheDebugPanel(DebugPanel):
'Cache calls from %(count)d backends',
count) % dict(count=count)
+ def enable_instrumentation(self):
+ # This isn't thread-safe because cache connections aren't thread-local
+ # in Django, unlike database connections.
+ cache.cache = CacheStatTracker(cache.cache)
+ cache.get_cache = get_cache
+
+ def disable_instrumentation(self):
+ cache.cache = original_cache
+ cache.get_cache = original_get_cache
+
def process_response(self, request, response):
self.record_stats({
'total_calls': len(self.calls),
@@ -204,12 +218,3 @@ class CacheDebugPanel(DebugPanel):
'misses': self.misses,
'counts': self.counts,
})
-
-
-def get_cache_debug(*args, **kwargs):
- base_cache = base_get_cache(*args, **kwargs)
- return CacheStatTracker(base_cache)
-
-
-cache.cache = CacheStatTracker(cache.cache)
-cache.get_cache = get_cache_debug
diff --git a/debug_toolbar/panels/logger.py b/debug_toolbar/panels/logger.py
index 644de30..45c995a 100644
--- a/debug_toolbar/panels/logger.py
+++ b/debug_toolbar/panels/logger.py
@@ -73,6 +73,9 @@ logging_handler = ThreadTrackingHandler(collector)
logging.root.setLevel(logging.NOTSET)
logging.root.addHandler(logging_handler) # register with logging
+# We don't use enable/disable_instrumentation because we can't make these
+# functions thread-safe and (hopefully) logging isn't too expensive.
+
try:
import logbook
logbook_supported = True
diff --git a/debug_toolbar/panels/sql.py b/debug_toolbar/panels/sql.py
index 1ced049..53b9f53 100644
--- a/debug_toolbar/panels/sql.py
+++ b/debug_toolbar/panels/sql.py
@@ -4,28 +4,13 @@ import uuid
from copy import copy
from django.db import connections
-from django.db.backends import BaseDatabaseWrapper
from django.utils.translation import ugettext_lazy as _, ungettext_lazy as __
from debug_toolbar.forms import SQLSelectForm
-from debug_toolbar.middleware import DebugToolbarMiddleware
from debug_toolbar.panels import DebugPanel
from debug_toolbar.utils import render_stacktrace
from debug_toolbar.utils.sql import reformat_sql
from debug_toolbar.utils.tracking.db import CursorWrapper
-from debug_toolbar.utils.tracking import replace_method
-
-
-@replace_method(BaseDatabaseWrapper, 'cursor')
-def cursor(original, self):
- result = original(self)
-
- djdt = DebugToolbarMiddleware.get_current()
- if not djdt:
- return result
- logger = djdt.get_panel(SQLDebugPanel)
-
- return CursorWrapper(result, self, logger=logger)
def get_isolation_level_display(engine, level):
@@ -131,6 +116,16 @@ class SQLDebugPanel(DebugPanel):
'SQL Queries from %(count)d connections',
count) % dict(count=count)
+ def enable_instrumentation(self):
+ # This is thread-safe because database connections are thread-local.
+ for connection in connections.all():
+ old_cursor = connection.cursor
+ connection.cursor = lambda: CursorWrapper(old_cursor(), connection, self)
+
+ def disable_instrumentation(self):
+ for connection in connections.all():
+ del connection.cursor
+
def process_response(self, request, response):
if self._queries:
width_ratio_tally = 0
diff --git a/debug_toolbar/panels/template.py b/debug_toolbar/panels/template.py
index 5bc0d2b..6523155 100644
--- a/debug_toolbar/panels/template.py
+++ b/debug_toolbar/panels/template.py
@@ -18,9 +18,9 @@ from debug_toolbar.utils.settings import CONFIG
# Code taken and adapted from Simon Willison and Django Snippets:
# http://www.djangosnippets.org/snippets/766/
-# Monkeypatch instrumented test renderer from django.test.utils - we could use
-# django.test.utils.setup_test_environment for this but that would also set up
-# e-mail interception, which we don't want
+# Monkey-patch to enable the template_rendered signal. The receiver returns
+# immediately when the panel is disabled to keep the overhead small.
+
from django.test.utils import instrumented_test_render
from django.template import Template
@@ -56,13 +56,17 @@ class TemplateDebugPanel(DebugPanel):
template_rendered.connect(self._store_template_info)
def _store_template_info(self, sender, **kwargs):
- t = kwargs['template']
- if t.name and t.name.startswith('debug_toolbar/'):
- return # skip templates that we are generating through the debug toolbar.
- context_data = kwargs['context']
+ if not self.enabled:
+ return
+
+ template, context = kwargs['template'], kwargs['context']
+
+ # Skip templates that we are generating through the debug toolbar.
+ if template.name and template.name.startswith('debug_toolbar/'):
+ return
context_list = []
- for context_layer in context_data.dicts:
+ for context_layer in context.dicts:
temp_layer = {}
if hasattr(context_layer, 'items'):
for key, value in context_layer.items():
@@ -102,6 +106,7 @@ class TemplateDebugPanel(DebugPanel):
context_list.append(pformat(temp_layer))
except UnicodeEncodeError:
pass
+
kwargs['context'] = [force_text(item) for item in context_list]
self.templates.append(kwargs)