Skip to content

Commit

Permalink
Merge pull request jazzband#206 from lincolnloop/stats_decouple
Browse files Browse the repository at this point in the history
Decouple statistics from presentation
  • Loading branch information
dcramer committed Sep 16, 2011
2 parents 5a3813d + 69f1c77 commit 8278bb0
Show file tree
Hide file tree
Showing 18 changed files with 302 additions and 260 deletions.
49 changes: 34 additions & 15 deletions debug_toolbar/panels/__init__.py
@@ -1,48 +1,67 @@
"""Base DebugPanel class"""
from django.template.defaultfilters import slugify
from django.template.loader import render_to_string
from debug_toolbar.middleware import DebugToolbarMiddleware


class DebugPanel(object):
"""
Base class for debug panels.
"""
# name = Base
# name = 'Base'
# template = 'debug_toolbar/panels/base.html'
has_content = False # If content returns something, set to true in subclass

# We'll maintain a local context instance so we can expose our template
# context variables to panels which need them:
context = {}

# Panel methods
def __init__(self, context={}):
self.context.update(context)

self.slug = slugify(self.name)

def dom_id(self):
return 'djDebug%sPanel' % (self.name.replace(' ', ''))

def nav_title(self):
"""Title showing in toolbar"""
raise NotImplementedError

def nav_subtitle(self):
"""Subtitle showing until title in toolbar"""
return ''

def title(self):
"""Title showing in panel"""
raise NotImplementedError

def url(self):
raise NotImplementedError

def content(self):
raise NotImplementedError

if self.has_content:
context = self.context.copy()
context.update(self.get_stats())
return render_to_string(self.template, context)

def record_stats(self, stats):
toolbar = DebugToolbarMiddleware.get_current()
panel_stats = toolbar.stats.get(self.slug)
if panel_stats:
panel_stats.update(stats)
else:
toolbar.stats[self.slug] = stats

def get_stats(self):
toolbar = DebugToolbarMiddleware.get_current()
return toolbar.stats.get(self.slug, {})

# Standard middleware methods
def process_request(self, request):
pass

def process_view(self, request, view_func, view_args, view_kwargs):
pass

def process_response(self, request, response):
pass

32 changes: 15 additions & 17 deletions debug_toolbar/panels/cache.py
Expand Up @@ -3,7 +3,6 @@

from django.core import cache
from django.core.cache.backends.base import BaseCache
from django.template.loader import render_to_string
from django.utils.translation import ugettext_lazy as _
from debug_toolbar.panels import DebugPanel

Expand All @@ -12,7 +11,7 @@ class CacheStatTracker(BaseCache):
def __init__(self, cache):
self.cache = cache
self.reset()

def reset(self):
self.calls = []
self.hits = 0
Expand All @@ -22,11 +21,11 @@ def reset(self):
self.get_many = 0
self.deletes = 0
self.total_time = 0

def _get_func_info(self):
stack = inspect.stack()[2]
return (stack[1], stack[2], stack[3], stack[4])

def get(self, key, default=None):
t = time.time()
value = self.cache.get(key, default)
Expand All @@ -39,23 +38,23 @@ def get(self, key, default=None):
self.gets += 1
self.calls.append((this_time, 'get', (key,), self._get_func_info()))
return value

def set(self, key, value, timeout=None):
t = time.time()
self.cache.set(key, value, timeout)
this_time = time.time() - t
self.total_time += this_time * 1000
self.sets += 1
self.calls.append((this_time, 'set', (key, value, timeout), self._get_func_info()))

def delete(self, key):
t = time.time()
self.cache.delete(key)
this_time = time.time() - t
self.total_time += this_time * 1000
self.deletes += 1
self.calls.append((this_time, 'delete', (key,), self._get_func_info()))

def get_many(self, keys):
t = time.time()
results = self.cache.get_many(keys)
Expand All @@ -74,32 +73,31 @@ class CacheDebugPanel(DebugPanel):
Panel that displays the cache statistics.
"""
name = 'Cache'
template = 'debug_toolbar/panels/cache.html'
has_content = True

def __init__(self, *args, **kwargs):
super(self.__class__, self).__init__(*args, **kwargs)
super(CacheDebugPanel, self).__init__(*args, **kwargs)
# This is hackish but to prevent threading issues is somewhat needed
if isinstance(cache.cache, CacheStatTracker):
cache.cache.reset()
self.cache = cache.cache
else:
self.cache = CacheStatTracker(cache.cache)
cache.cache = self.cache

def nav_title(self):
return _('Cache: %.2fms') % self.cache.total_time

def title(self):
return _('Cache Usage')

def url(self):
return ''

def content(self):
context = self.context.copy()
context.update({

def process_response(self, request, response):
self.record_stats({
'cache_calls': len(self.cache.calls),
'cache_time': self.cache.total_time,
'cache': self.cache,
})
return render_to_string('debug_toolbar/panels/cache.html', context)
19 changes: 9 additions & 10 deletions debug_toolbar/panels/headers.py
@@ -1,12 +1,13 @@
from django.template.loader import render_to_string
from django.utils.translation import ugettext_lazy as _
from debug_toolbar.panels import DebugPanel


class HeaderDebugPanel(DebugPanel):
"""
A panel to display HTTP headers.
"""
name = 'Header'
template = 'debug_toolbar/panels/headers.html'
has_content = True
# List of headers we want to display
header_filter = (
Expand All @@ -31,24 +32,22 @@ class HeaderDebugPanel(DebugPanel):
'SERVER_PROTOCOL',
'SERVER_SOFTWARE',
)

def nav_title(self):
return _('HTTP Headers')

def title(self):
return _('HTTP Headers')

def url(self):
return ''

def process_request(self, request):
self.headers = dict(
[(k, request.META[k]) for k in self.header_filter if k in request.META]
)

def content(self):
context = self.context.copy()
context.update({

def process_response(self, request, response):
self.record_stats({
'headers': self.headers
})
return render_to_string('debug_toolbar/panels/headers.html', context)
42 changes: 19 additions & 23 deletions debug_toolbar/panels/logger.py
Expand Up @@ -4,7 +4,6 @@
import threading
except ImportError:
threading = None
from django.template.loader import render_to_string
from django.utils.translation import ugettext_lazy as _
from debug_toolbar.panels import DebugPanel

Expand All @@ -15,15 +14,15 @@ def __init__(self):
raise NotImplementedError("threading module is not available, \
the logging panel cannot be used without it")
self.records = {} # a dictionary that maps threads to log records

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):
"""
Returns a list of records for the provided thread, of if none is provided,
Expand All @@ -34,7 +33,7 @@ def get_records(self, thread=None):
if thread not in self.records:
self.records[thread] = []
return self.records[thread]

def clear_records(self, thread=None):
if thread is None:
thread = threading.currentThread()
Expand All @@ -46,7 +45,7 @@ class ThreadTrackingHandler(logging.Handler):
def __init__(self, collector):
logging.Handler.__init__(self)
self.collector = collector

def emit(self, record):
record = {
'message': record.getMessage(),
Expand Down Expand Up @@ -76,7 +75,7 @@ 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,
Expand All @@ -87,40 +86,37 @@ def emit(self, record):
'channel': record.channel,
}
self.collector.add_record(record)


logbook_handler = LogbookThreadTrackingHandler(collector)
logbook_handler.push_application() # register with logbook

class LoggingPanel(DebugPanel):
name = 'Logging'
template = 'debug_toolbar/panels/logger.html'
has_content = True

def process_request(self, request):
collector.clear_records()


def process_response(self, request, response):
records = self.get_and_delete()
self.record_stats({'records': records})

def get_and_delete(self):
records = collector.get_records()
collector.clear_records()
return records

def nav_title(self):
return _("Logging")

def nav_subtitle(self):
# FIXME l10n: use ngettext
return "%s message%s" % (len(collector.get_records()), (len(collector.get_records()) == 1) and '' or 's')

def title(self):
return _('Log Messages')

def url(self):
return ''

def content(self):
records = self.get_and_delete()
context = self.context.copy()
context.update({'records': records})

return render_to_string('debug_toolbar/panels/logger.html', context)

0 comments on commit 8278bb0

Please sign in to comment.