diff options
Diffstat (limited to 'debug_toolbar')
| -rw-r--r-- | debug_toolbar/middleware.py | 10 | ||||
| -rw-r--r-- | debug_toolbar/panels/__init__.py | 27 | ||||
| -rw-r--r-- | debug_toolbar/panels/cache.py | 25 | ||||
| -rw-r--r-- | debug_toolbar/panels/logger.py | 3 | ||||
| -rw-r--r-- | debug_toolbar/panels/sql.py | 25 | ||||
| -rw-r--r-- | debug_toolbar/panels/template.py | 21 | 
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) | 
