aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRob Hudson2008-09-08 11:23:20 -0700
committerRob Hudson2008-09-08 11:23:20 -0700
commitb74c985614caa336fdb1b956df5b1af69cdf8f85 (patch)
tree98e74989f63c262a26dba6aa1dd5576ef4933ed1
parent44ae80ddd1ed1f14b1d9c00113fc629616c2283c (diff)
downloaddjango-debug-toolbar-b74c985614caa336fdb1b956df5b1af69cdf8f85.tar.bz2
I forgot to really *add* the cache stats panel by David Cramer.
-rw-r--r--debug_toolbar/panels/cache.py102
-rw-r--r--debug_toolbar/templates/debug_toolbar/panels/cache.html54
2 files changed, 156 insertions, 0 deletions
diff --git a/debug_toolbar/panels/cache.py b/debug_toolbar/panels/cache.py
new file mode 100644
index 0000000..fa6bdd0
--- /dev/null
+++ b/debug_toolbar/panels/cache.py
@@ -0,0 +1,102 @@
+import time
+import inspect
+try:
+ from cStringIO import StringIO
+except ImportError:
+ import StringIO
+from django.core import cache
+from django.core.cache.backends.base import BaseCache
+from django.template.loader import render_to_string
+from debug_toolbar.panels import DebugPanel
+
+class CacheStatTracker(BaseCache):
+ """A small class used to track cache calls."""
+ def __init__(self, cache):
+ self.cache = cache
+ self.reset()
+
+ def reset(self):
+ self.calls = []
+ self.hits = 0
+ self.misses = 0
+ self.sets = 0
+ self.gets = 0
+ 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)
+ this_time = time.time() - t
+ self.total_time += this_time * 1000
+ if value is None:
+ self.misses += 1
+ else:
+ self.hits += 1
+ 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.instance.delete(key, value)
+ 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)
+ this_time = time.time() - t
+ self.total_time += this_time * 1000
+ self.get_many += 1
+ for key, value in results.iteritems():
+ if value is None:
+ self.misses += 1
+ else:
+ self.hits += 1
+ self.calls.append((this_time, 'get_many', (keys,), self._get_func_info()))
+
+class CacheDebugPanel(DebugPanel):
+ """
+ Panel that displays the cache statistics.
+ """
+ name = 'Cache'
+
+ def __init__(self, request):
+ # 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
+ super(CacheDebugPanel, self).__init__(request)
+
+ def title(self):
+ return 'Cache: %.2fms' % self.cache.total_time
+
+ def url(self):
+ return ''
+
+ def content(self):
+ context = dict(
+ 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) \ No newline at end of file
diff --git a/debug_toolbar/templates/debug_toolbar/panels/cache.html b/debug_toolbar/templates/debug_toolbar/panels/cache.html
new file mode 100644
index 0000000..9d51404
--- /dev/null
+++ b/debug_toolbar/templates/debug_toolbar/panels/cache.html
@@ -0,0 +1,54 @@
+<h3>Cache Usage</h3>
+<table>
+ <colgroup>
+ <col width="12%"/>
+ <col width="12%"/>
+ <col width="12%"/>
+ <col width="12%"/>
+ <col width="12%"/>
+ <col width="12%"/>
+ <col width="12%"/>
+ <col width="12%"/>
+ </colgroup>
+ <tr>
+ <th>Total Calls</th>
+ <td>{{ cache_calls }}</td>
+ <th>Total Time</th>
+ <td>{{ cache_time }}</td>
+ <th>Hits</th>
+ <td>{{ cache.hits }}</td>
+ <th>Misses</th>
+ <td>{{ cache.misses }}</td>
+ </tr>
+ <tr>
+ <th>gets</th>
+ <td>{{ cache.gets }}</td>
+ <th>sets</th>
+ <td>{{ cache.sets }}</td>
+ <th>deletes</th>
+ <td>{{ cache.deletes }}</td>
+ <th>get_many</th>
+ <td>{{ cache.get_many }}</td>
+ </tr>
+</table>
+<h3>Breakdown</h3>
+<table>
+ <thead>
+ <tr>
+ <th>Time&nbsp;(ms)</th>
+ <th>Type</th>
+ <th>Parameters</th>
+ <th>Function</th>
+ </tr>
+ </thead>
+ <tbody>
+ {% for query in cache.calls %}
+ <tr class="{% cycle 'row1' 'row2' %}">
+ <td>{{ query.0|floatformat:"4" }}</td>
+ <td>{{ query.1|escape }}</td>
+ <td>{{ query.2|escape }}</td>
+ <td><acronym title="{{ query.3.0 }}:{{ query.3.1 }}">{{ query.3.2|escape }}</acronym>: {{ query.3.3.0|escape }}</td>
+ </tr>
+ {% endfor %}
+ </tbody>
+</table> \ No newline at end of file