diff options
Diffstat (limited to 'debug_toolbar/panels')
| -rw-r--r-- | debug_toolbar/panels/__init__.py | 49 | ||||
| -rw-r--r-- | debug_toolbar/panels/cache.py | 32 | ||||
| -rw-r--r-- | debug_toolbar/panels/headers.py | 19 | ||||
| -rw-r--r-- | debug_toolbar/panels/logger.py | 42 | ||||
| -rw-r--r-- | debug_toolbar/panels/profiling.py | 72 | ||||
| -rw-r--r-- | debug_toolbar/panels/request_vars.py | 33 | ||||
| -rw-r--r-- | debug_toolbar/panels/settings_vars.py | 16 | ||||
| -rw-r--r-- | debug_toolbar/panels/signals.py | 23 | ||||
| -rw-r--r-- | debug_toolbar/panels/sql.py | 50 | ||||
| -rw-r--r-- | debug_toolbar/panels/template.py | 31 | ||||
| -rw-r--r-- | debug_toolbar/panels/timer.py | 105 | ||||
| -rw-r--r-- | debug_toolbar/panels/version.py | 20 |
12 files changed, 258 insertions, 234 deletions
diff --git a/debug_toolbar/panels/__init__.py b/debug_toolbar/panels/__init__.py index fa2e4b6..b4f11fb 100644 --- a/debug_toolbar/panels/__init__.py +++ b/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 - diff --git a/debug_toolbar/panels/cache.py b/debug_toolbar/panels/cache.py index 5617ec2..620be86 100644 --- a/debug_toolbar/panels/cache.py +++ b/debug_toolbar/panels/cache.py @@ -3,7 +3,6 @@ import inspect 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 @@ -12,7 +11,7 @@ class CacheStatTracker(BaseCache): def __init__(self, cache): self.cache = cache self.reset() - + def reset(self): self.calls = [] self.hits = 0 @@ -22,11 +21,11 @@ class CacheStatTracker(BaseCache): 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) @@ -39,7 +38,7 @@ class CacheStatTracker(BaseCache): 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) @@ -47,7 +46,7 @@ class CacheStatTracker(BaseCache): 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) @@ -55,7 +54,7 @@ class CacheStatTracker(BaseCache): 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) @@ -74,10 +73,11 @@ 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() @@ -85,21 +85,19 @@ class CacheDebugPanel(DebugPanel): 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) diff --git a/debug_toolbar/panels/headers.py b/debug_toolbar/panels/headers.py index 1e929f6..181e88d 100644 --- a/debug_toolbar/panels/headers.py +++ b/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 = ( @@ -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) diff --git a/debug_toolbar/panels/logger.py b/debug_toolbar/panels/logger.py index 5e82a13..55ae9bd 100644 --- a/debug_toolbar/panels/logger.py +++ b/debug_toolbar/panels/logger.py @@ -4,7 +4,6 @@ try: 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 @@ -15,15 +14,15 @@ class LogCollector(object): 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, @@ -34,7 +33,7 @@ class LogCollector(object): 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() @@ -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(), @@ -76,7 +75,7 @@ if logbook_supported: def __init__(self, collector): logbook.handlers.Handler.__init__(self, bubble=True) self.collector = collector - + def emit(self, record): record = { 'message': record.message, @@ -87,40 +86,37 @@ if logbook_supported: '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) - diff --git a/debug_toolbar/panels/profiling.py b/debug_toolbar/panels/profiling.py index 289e1f7..8913621 100644 --- a/debug_toolbar/panels/profiling.py +++ b/debug_toolbar/panels/profiling.py @@ -1,6 +1,5 @@ from __future__ import division -from django.template.loader import render_to_string from django.utils.translation import ugettext_lazy as _ from django.utils.safestring import mark_safe from debug_toolbar.panels import DebugPanel @@ -8,9 +7,9 @@ from debug_toolbar.panels import DebugPanel try: from line_profiler import LineProfiler, show_func DJ_PROFILE_USE_LINE_PROFILER = True -except ImportError: +except ImportError: DJ_PROFILE_USE_LINE_PROFILER = False - + from cStringIO import StringIO import cProfile @@ -18,6 +17,7 @@ from pstats import Stats from colorsys import hsv_to_rgb import os + class DjangoDebugToolbarStats(Stats): __root = None @@ -28,9 +28,11 @@ class DjangoDebugToolbarStats(Stats): self.__root = func break return self.__root - + + class FunctionCall(object): - def __init__(self, statobj, func, depth=0, stats=None, id=0, parent_ids=[], hsv=(0,0.5,1)): + def __init__(self, statobj, func, depth=0, stats=None, + id=0, parent_ids=[], hsv=(0,0.5,1)): self.statobj = statobj self.func = func if stats: @@ -63,7 +65,7 @@ class FunctionCall(object): file_name, line_num, method = self.func idx = file_name.find('/site-packages/') if idx > -1: - file_name=file_name[idx+14:] + file_name = file_name[idx+14:] file_path, file_name = file_name.rsplit(os.sep, 1) @@ -76,7 +78,7 @@ class FunctionCall(object): def subfuncs(self): i=0 - h,s,v = self.hsv + h, s, v = self.hsv count = len(self.statobj.all_callees[self.func]) for func, stats in self.statobj.all_callees[self.func].iteritems(): i += 1 @@ -86,8 +88,8 @@ class FunctionCall(object): else: s1 = s*(stats[3]/self.stats[3]) yield FunctionCall(self.statobj, - func, - self.depth+1, + func, + self.depth+1, stats=stats, id=str(self.id) + '_' + str(i), parent_ids=self.parent_ids + [self.id], @@ -105,25 +107,25 @@ class FunctionCall(object): def tottime_per_call(self): cc, nc, tt, ct = self.stats - + if nc == 0: return 0 - + return tt/nc def cumtime_per_call(self): cc, nc, tt, ct = self.stats - + if cc == 0: return 0 - + return ct/cc - + def indent(self): return 16 * self.depth - + def line_stats_text(self): - if self._line_stats_text is None: + if self._line_stats_text is None and DJ_PROFILE_USE_LINE_PROFILER: lstats = self.statobj.line_stats if self.func in lstats.timings: out = StringIO() @@ -139,17 +141,18 @@ class ProfilingDebugPanel(DebugPanel): Panel that displays the Django version. """ name = 'Profiling' + template = 'debug_toolbar/panels/profiling.html' has_content = True - + def nav_title(self): return _('Profiling') - + def url(self): return '' def title(self): return _('Profiling') - + def _unwrap_closure_and_profile(self, func): if not hasattr(func, 'func_code'): return @@ -158,7 +161,7 @@ class ProfilingDebugPanel(DebugPanel): for cell in func.func_closure: if hasattr(cell.cell_contents, 'func_code'): self._unwrap_closure_and_profile(cell.cell_contents) - + def process_view(self, request, view_func, view_args, view_kwargs): __traceback_hide__ = True self.profiler = cProfile.Profile() @@ -173,33 +176,28 @@ class ProfilingDebugPanel(DebugPanel): self.line_profiler = None out = self.profiler.runcall(view_func, *args, **view_kwargs) return out - - def process_response(self, request, response): - self.profiler.create_stats() - self.stats = DjangoDebugToolbarStats(self.profiler) - if DJ_PROFILE_USE_LINE_PROFILER: - self.stats.line_stats = self.line_profiler.get_stats() - return response - + def add_node(self, func_list, func, max_depth, cum_time=0.1): func_list.append(func) func.has_subfuncs = False if func.depth < max_depth: for subfunc in func.subfuncs(): - if subfunc.stats[3] >= cum_time or (subfunc.func in self.stats.line_stats.timings): + if (subfunc.stats[3] >= cum_time or + (hasattr(self.stats, 'line_stats') and + (subfunc.func in self.stats.line_stats.timings))): func.has_subfuncs = True self.add_node(func_list, subfunc, max_depth, cum_time=cum_time) - def content(self): - + def process_response(self, request, response): + self.profiler.create_stats() + self.stats = DjangoDebugToolbarStats(self.profiler) + if DJ_PROFILE_USE_LINE_PROFILER: + self.stats.line_stats = self.line_profiler.get_stats() self.stats.calc_callees() + root = FunctionCall(self.stats, self.stats.get_root_func(), depth=0) func_list = [] self.add_node(func_list, root, 10, root.stats[3]/8) - context = self.context.copy() - context.update({ - 'func_list': func_list, - }) - - return render_to_string('debug_toolbar/panels/profiling.html', context) + + self.stats_record({'func_list': func_list}) diff --git a/debug_toolbar/panels/request_vars.py b/debug_toolbar/panels/request_vars.py index 9033d48..ee9cdc5 100644 --- a/debug_toolbar/panels/request_vars.py +++ b/debug_toolbar/panels/request_vars.py @@ -1,4 +1,3 @@ -from django.template.loader import render_to_string from django.utils.translation import ugettext_lazy as _ from debug_toolbar.panels import DebugPanel @@ -9,54 +8,52 @@ class RequestVarsDebugPanel(DebugPanel): A panel to display request variables (POST/GET, session, cookies). """ name = 'RequestVars' + template = 'debug_toolbar/panels/request_vars.html' has_content = True - + def __init__(self, *args, **kwargs): DebugPanel.__init__(self, *args, **kwargs) self.view_func = None self.view_args = None self.view_kwargs = None - + def nav_title(self): return _('Request Vars') - + def title(self): return _('Request Vars') - + def url(self): return '' - + def process_request(self, request): self.request = request - + def process_view(self, request, view_func, view_args, view_kwargs): self.view_func = view_func self.view_args = view_args self.view_kwargs = view_kwargs - - def content(self): - context = self.context.copy() - - context.update({ + + def process_response(self, request, response): + self.record_stats({ 'get': [(k, self.request.GET.getlist(k)) for k in self.request.GET], 'post': [(k, self.request.POST.getlist(k)) for k in self.request.POST], 'cookies': [(k, self.request.COOKIES.get(k)) for k in self.request.COOKIES], }) + if hasattr(self, 'view_func'): if self.view_func is not None: name = get_name_from_obj(self.view_func) else: name = '<no view>' - - context.update({ + + self.record_stats({ 'view_func': name, 'view_args': self.view_args, 'view_kwargs': self.view_kwargs }) - + if hasattr(self.request, 'session'): - context.update({ + self.record_stats({ 'session': [(k, self.request.session.get(k)) for k in self.request.session.iterkeys()] }) - - return render_to_string('debug_toolbar/panels/request_vars.html', context) diff --git a/debug_toolbar/panels/settings_vars.py b/debug_toolbar/panels/settings_vars.py index ebb4464..e154dda 100644 --- a/debug_toolbar/panels/settings_vars.py +++ b/debug_toolbar/panels/settings_vars.py @@ -1,5 +1,4 @@ from django.conf import settings -from django.template.loader import render_to_string from django.views.debug import get_safe_settings from django.utils.translation import ugettext_lazy as _ from debug_toolbar.panels import DebugPanel @@ -10,20 +9,19 @@ class SettingsVarsDebugPanel(DebugPanel): A panel to display all variables in django.conf.settings """ name = 'SettingsVars' + template = 'debug_toolbar/panels/settings_vars.html' has_content = True - + def nav_title(self): return _('Settings') - + def title(self): return _('Settings from <code>%s</code>') % settings.SETTINGS_MODULE - + def url(self): return '' - - def content(self): - context = self.context.copy() - context.update({ + + def process_response(self, request, response): + self.record_stats({ 'settings': get_safe_settings(), }) - return render_to_string('debug_toolbar/panels/settings_vars.html', context) diff --git a/debug_toolbar/panels/signals.py b/debug_toolbar/panels/signals.py index dbd3725..205b3c5 100644 --- a/debug_toolbar/panels/signals.py +++ b/debug_toolbar/panels/signals.py @@ -6,7 +6,6 @@ from django.core.signals import request_started, request_finished, \ from django.db.models.signals import class_prepared, pre_init, post_init, \ pre_save, post_save, pre_delete, post_delete, post_syncdb from django.dispatch.dispatcher import WEAKREF_TYPES -from django.template.loader import render_to_string from django.utils.translation import ugettext_lazy as _ try: @@ -18,8 +17,9 @@ from debug_toolbar.panels import DebugPanel class SignalDebugPanel(DebugPanel): name = "Signals" + template = 'debug_toolbar/panels/signals.html' has_content = True - + SIGNALS = { 'request_started': request_started, 'request_finished': request_finished, @@ -34,16 +34,16 @@ class SignalDebugPanel(DebugPanel): 'post_delete': post_delete, 'post_syncdb': post_syncdb, } - + def nav_title(self): return _("Signals") - + def title(self): return _("Signals") - + def url(self): return '' - + def signals(self): signals = self.SIGNALS.copy() if hasattr(settings, 'DEBUG_TOOLBAR_CONFIG'): @@ -57,8 +57,8 @@ class SignalDebugPanel(DebugPanel): signals[parts[-1]] = getattr(sys.modules[path], parts[-1]) return signals signals = property(signals) - - def content(self): + + def process_response(self, request, response): signals = [] keys = self.signals.keys() keys.sort() @@ -80,8 +80,5 @@ class SignalDebugPanel(DebugPanel): text = "function %s" % receiver.__name__ receivers.append(text) signals.append((name, signal, receivers)) - - context = self.context.copy() - context.update({'signals': signals}) - - return render_to_string('debug_toolbar/panels/signals.html', context) + + self.record_stats({'signals': signals}) diff --git a/debug_toolbar/panels/sql.py b/debug_toolbar/panels/sql.py index 841aaac..b27e6c0 100644 --- a/debug_toolbar/panels/sql.py +++ b/debug_toolbar/panels/sql.py @@ -2,7 +2,6 @@ import re import uuid from django.db.backends import BaseDatabaseWrapper -from django.template.loader import render_to_string from django.utils.html import escape from django.utils.safestring import mark_safe from django.utils.translation import ugettext_lazy as _, ungettext_lazy as __ @@ -14,11 +13,12 @@ from debug_toolbar.utils import sqlparse from debug_toolbar.utils.tracking.db import CursorWrapper from debug_toolbar.utils.tracking import replace_call + # Inject our tracking cursor @replace_call(BaseDatabaseWrapper.cursor) def cursor(func, self): result = func(self) - + djdt = DebugToolbarMiddleware.get_current() if not djdt: return result @@ -26,6 +26,7 @@ def cursor(func, self): return CursorWrapper(result, self, logger=logger) + def get_isolation_level_display(engine, level): if engine == 'psycopg2': import psycopg2.extensions @@ -41,6 +42,7 @@ def get_isolation_level_display(engine, level): return choices.get(level) + def get_transaction_status_display(engine, level): if engine == 'psycopg2': import psycopg2.extensions @@ -56,16 +58,18 @@ def get_transaction_status_display(engine, level): return choices.get(level) + class SQLDebugPanel(DebugPanel): """ Panel that displays information about the SQL queries run while processing the request. """ name = 'SQL' + template = 'debug_toolbar/panels/sql.html' has_content = True - + def __init__(self, *args, **kwargs): - super(self.__class__, self).__init__(*args, **kwargs) + super(SQLDebugPanel, self).__init__(*args, **kwargs) self._offset = dict((k, len(connections[k].queries)) for k in connections) self._sql_time = 0 self._num_queries = 0 @@ -78,20 +82,20 @@ class SQLDebugPanel(DebugPanel): conn = connections[alias].connection if not conn: return None - + engine = conn.__class__.__module__.split('.', 1)[0] if engine == 'psycopg2': cur_status = conn.get_transaction_status() else: raise ValueError(engine) - + last_status = self._transaction_status.get(alias) self._transaction_status[alias] = cur_status - + if not cur_status: # No available state return None - + if cur_status != last_status: if cur_status: self._transaction_ids[alias] = uuid.uuid4().hex @@ -112,10 +116,10 @@ class SQLDebugPanel(DebugPanel): self._databases[alias]['num_queries'] += 1 self._sql_time += kwargs['duration'] self._num_queries += 1 - + def nav_title(self): return _('SQL') - + def nav_subtitle(self): # TODO l10n: use ngettext return "%d %s in %.2fms" % ( @@ -123,18 +127,18 @@ class SQLDebugPanel(DebugPanel): (self._num_queries == 1) and 'query' or 'queries', self._sql_time ) - + def title(self): count = len(self._databases) return __('SQL Queries from %(count)d connection', 'SQL Queries from %(count)d connections', count) % dict( count=count, ) - + def url(self): return '' - - def content(self): + + def process_response(self, request, response): if self._queries: width_ratio_tally = 0 colors = [ @@ -157,7 +161,7 @@ class SQLDebugPanel(DebugPanel): nn = 0 rgb[nn] = nc db['rgb_color'] = rgb - + trans_ids = {} trans_id = None i = 0 @@ -190,25 +194,27 @@ class SQLDebugPanel(DebugPanel): query['start_offset'] = width_ratio_tally query['end_offset'] = query['width_ratio'] + query['start_offset'] width_ratio_tally += query['width_ratio'] - + stacktrace = [] for frame in query['stacktrace']: params = map(escape, frame[0].rsplit('/', 1) + list(frame[1:])) - stacktrace.append(u'<span class="path">{0}/</span><span class="file">{1}</span> in <span class="func">{3}</span>(<span class="lineno">{2}</span>)\n <span class="code">{4}</span>'.format(*params)) + try: + stacktrace.append(u'<span class="path">{0}/</span><span class="file">{1}</span> in <span class="func">{3}</span>(<span class="lineno">{2}</span>)\n <span class="code">{4}</span>'.format(*params)) + except IndexError: + # This frame doesn't have the expected format, so skip it and move on to the next one + continue query['stacktrace'] = mark_safe('\n'.join(stacktrace)) i += 1 - + if trans_id: self._queries[i-1][1]['ends_trans'] = True - context = self.context.copy() - context.update({ + self.record_stats({ '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, }) - return render_to_string('debug_toolbar/panels/sql.html', context) class BoldKeywordFilter(sqlparse.filters.Filter): """sqlparse filter to bold SQL keywords""" @@ -222,10 +228,12 @@ class BoldKeywordFilter(sqlparse.filters.Filter): if is_keyword: yield sqlparse.tokens.Text, '</strong>' + def swap_fields(sql): return re.sub('SELECT</strong> (.*) <strong>FROM', 'SELECT</strong> <a class="djDebugUncollapsed djDebugToggle" href="#">•••</a> ' + '<a class="djDebugCollapsed djDebugToggle" href="#">\g<1></a> <strong>FROM', sql) + def reformat_sql(sql): stack = sqlparse.engine.FilterStack() stack.preprocess.append(BoldKeywordFilter()) # add our custom filter diff --git a/debug_toolbar/panels/template.py b/debug_toolbar/panels/template.py index d620a38..79a4871 100644 --- a/debug_toolbar/panels/template.py +++ b/debug_toolbar/panels/template.py @@ -4,7 +4,6 @@ from pprint import pformat from django import http from django.conf import settings from django.template.context import get_standard_processors -from django.template.loader import render_to_string from django.test.signals import template_rendered from django.utils.translation import ugettext_lazy as _ from debug_toolbar.panels import DebugPanel @@ -27,6 +26,7 @@ else: Template.original_render = Template._render Template._render = instrumented_test_render + # MONSTER monkey-patch old_template_init = Template.__init__ def new_template_init(self, template_string, origin=None, name='<Unknown Template>'): @@ -34,21 +34,23 @@ def new_template_init(self, template_string, origin=None, name='<Unknown Templat self.origin = origin Template.__init__ = new_template_init + class TemplateDebugPanel(DebugPanel): """ A panel that lists all templates used during processing of a response. """ name = 'Template' + template = 'debug_toolbar/panels/templates.html' has_content = True - + def __init__(self, *args, **kwargs): - super(self.__class__, self).__init__(*args, **kwargs) + super(TemplateDebugPanel, self).__init__(*args, **kwargs) self.templates = [] template_rendered.connect(self._store_template_info) - + def _store_template_info(self, sender, **kwargs): context_data = kwargs['context'] - + context_list = [] for context_layer in context_data.dicts: temp_layer = {} @@ -74,22 +76,22 @@ class TemplateDebugPanel(DebugPanel): pass kwargs['context'] = context_list self.templates.append(kwargs) - + def nav_title(self): return _('Templates') - + def title(self): num_templates = len([t for t in self.templates if not (t['template'].name and t['template'].name.startswith('debug_toolbar/'))]) return _('Templates (%(num_templates)s rendered)') % {'num_templates': num_templates} - + def url(self): return '' - + def process_request(self, request): self.request = request - - def content(self): + + def process_response(self, request, response): context_processors = dict( [ ("%s.%s" % (k.__module__, k.__name__), @@ -116,12 +118,9 @@ class TemplateDebugPanel(DebugPanel): context_list = template_data.get('context', []) info['context'] = '\n'.join(context_list) template_context.append(info) - - context = self.context.copy() - context.update({ + + self.record_stats({ 'templates': template_context, 'template_dirs': [normpath(x) for x in settings.TEMPLATE_DIRS], 'context_processors': context_processors, }) - - return render_to_string('debug_toolbar/panels/templates.html', context) diff --git a/debug_toolbar/panels/timer.py b/debug_toolbar/panels/timer.py index d866166..7c0febf 100644 --- a/debug_toolbar/panels/timer.py +++ b/debug_toolbar/panels/timer.py @@ -7,11 +7,13 @@ from django.template.loader import render_to_string from django.utils.translation import ugettext_lazy as _ from debug_toolbar.panels import DebugPanel + class TimerDebugPanel(DebugPanel): """ Panel that displays the time a response took in milliseconds. """ name = 'Timer' + template = 'debug_toolbar/panels/timer.html' try: # if resource module not available, don't show content panel resource except NameError: @@ -20,47 +22,23 @@ class TimerDebugPanel(DebugPanel): else: has_content = True has_resource = True - + def process_request(self, request): self._start_time = time.time() if self.has_resource: self._start_rusage = resource.getrusage(resource.RUSAGE_SELF) - + def process_response(self, request, response): - self.total_time = (time.time() - self._start_time) * 1000 + total_time = (time.time() - self._start_time) * 1000 if self.has_resource: self._end_rusage = resource.getrusage(resource.RUSAGE_SELF) - - def nav_title(self): - return _('Time') - - def nav_subtitle(self): - # TODO l10n - if self.has_resource: - utime = self._end_rusage.ru_utime - self._start_rusage.ru_utime - stime = self._end_rusage.ru_stime - self._start_rusage.ru_stime - return 'CPU: %0.2fms (%0.2fms)' % ((utime + stime) * 1000.0, self.total_time) - else: - return 'TOTAL: %0.2fms' % (self.total_time) - - def title(self): - return _('Resource Usage') - - def url(self): - return '' - - def _elapsed_ru(self, name): - return getattr(self._end_rusage, name) - getattr(self._start_rusage, name) - - def content(self): - + utime = 1000 * self._elapsed_ru('ru_utime') stime = 1000 * self._elapsed_ru('ru_stime') vcsw = self._elapsed_ru('ru_nvcsw') ivcsw = self._elapsed_ru('ru_nivcsw') minflt = self._elapsed_ru('ru_minflt') majflt = self._elapsed_ru('ru_majflt') - # these are documented as not meaningful under Linux. If you're running BSD # feel free to enable them, and add any others that I hadn't gotten to before # I noticed that I was getting nothing but zeroes and that the docs agreed. :-( @@ -72,22 +50,63 @@ class TimerDebugPanel(DebugPanel): # srss = self._end_rusage.ru_ixrss # urss = self._end_rusage.ru_idrss # usrss = self._end_rusage.ru_isrss - + + self.record_stats({ + 'total_time': total_time, + 'utime': utime, + 'stime': stime, + 'vcsw': vcsw, + 'ivcsw': ivcsw, + 'minflt': minflt, + 'majflt': majflt, +# 'blkin': blkin, +# 'blkout': blkout, +# 'swap': swap, +# 'rss': rss, +# 'urss': urss, +# 'srss': srss, +# 'usrss': usrss, + }) + + def nav_title(self): + return _('Time') + + def nav_subtitle(self): + stats = self.get_stats() + + # TODO l10n + if self.has_resource: + utime = self._end_rusage.ru_utime - self._start_rusage.ru_utime + stime = self._end_rusage.ru_stime - self._start_rusage.ru_stime + return 'CPU: %0.2fms (%0.2fms)' % ((utime + stime) * 1000.0, stats['total_time']) + else: + return 'TOTAL: %0.2fms' % (stats['total_time']) + + def title(self): + return _('Resource Usage') + + def url(self): + return '' + + def _elapsed_ru(self, name): + return getattr(self._end_rusage, name) - getattr(self._start_rusage, name) + + def content(self): + stats = self.get_stats() + # TODO l10n on values rows = ( - (_('User CPU time'), '%0.3f msec' % utime), - (_('System CPU time'), '%0.3f msec' % stime), - (_('Total CPU time'), '%0.3f msec' % (utime + stime)), - (_('Elapsed time'), '%0.3f msec' % self.total_time), - (_('Context switches'), '%d voluntary, %d involuntary' % (vcsw, ivcsw)), -# ('Memory use', '%d max RSS, %d shared, %d unshared' % (rss, srss, urss + usrss)), -# ('Page faults', '%d no i/o, %d requiring i/o' % (minflt, majflt)), -# ('Disk operations', '%d in, %d out, %d swapout' % (blkin, blkout, swap)), + (_('User CPU time'), '%0.3f msec' % stats['utime']), + (_('System CPU time'), '%0.3f msec' % stats['stime']), + (_('Total CPU time'), '%0.3f msec' % (stats['utime'] + stats['stime'])), + (_('Elapsed time'), '%0.3f msec' % stats['total_time']), + (_('Context switches'), '%d voluntary, %d involuntary' % (stats['vcsw'], stats['ivcsw'])), +# ('Memory use', '%d max RSS, %d shared, %d unshared' % (stats['rss'], stats.['srss'], +# stats['urss'] + stats['usrss'])), +# ('Page faults', '%d no i/o, %d requiring i/o' % (stats['minflt'], stats['majflt'])), +# ('Disk operations', '%d in, %d out, %d swapout' % (stats['blkin'], stats['blkout'], stats['swap'])), ) - + context = self.context.copy() - context.update({ - 'rows': rows, - }) - - return render_to_string('debug_toolbar/panels/timer.html', context) + context.update({'rows': rows,}) + return render_to_string(self.template, context) diff --git a/debug_toolbar/panels/version.py b/debug_toolbar/panels/version.py index f0d8fbc..4e58971 100644 --- a/debug_toolbar/panels/version.py +++ b/debug_toolbar/panels/version.py @@ -2,10 +2,8 @@ import sys import django from django.conf import settings -from django.template.loader import render_to_string from django.utils.translation import ugettext_lazy as _ -import debug_toolbar from debug_toolbar.panels import DebugPanel @@ -14,21 +12,22 @@ class VersionDebugPanel(DebugPanel): Panel that displays the Django version. """ name = 'Version' + template = 'debug_toolbar/panels/versions.html' has_content = True - + def nav_title(self): return _('Versions') - + def nav_subtitle(self): return 'Django %s' % django.get_version() - + def url(self): return '' def title(self): return _('Versions') - - def content(self): + + def process_response(self, request, response): versions = {} versions['Python'] = '%d.%d.%d' % sys.version_info[:3] for app in settings.INSTALLED_APPS + ['django']: @@ -50,11 +49,8 @@ class VersionDebugPanel(DebugPanel): if isinstance(version, (list, tuple)): version = '.'.join(str(o) for o in version) versions[name] = version - - context = self.context.copy() - context.update({ + + self.record_stats({ 'versions': versions, 'paths': sys.path, }) - - return render_to_string('debug_toolbar/panels/versions.html', context) |
