diff options
| author | David Cramer | 2011-03-31 11:42:23 -0700 |
|---|---|---|
| committer | David Cramer | 2011-03-31 11:42:23 -0700 |
| commit | 3f578cf684b7e3b9a20d9c777950c28c44db074a (patch) | |
| tree | d7573707ccdfbd0627d85c9a2b6f90d62f7537fe /debug_toolbar/utils/tracking/db.py | |
| parent | f492b56c8200eebb77b8023ab386c9ef412cc06b (diff) | |
| download | django-debug-toolbar-3f578cf684b7e3b9a20d9c777950c28c44db074a.tar.bz2 | |
Add utilities to inject and monitor functions. Change DB tracking to use new injection method on BaseDatabaseWrapper.cursor
Diffstat (limited to 'debug_toolbar/utils/tracking/db.py')
| -rw-r--r-- | debug_toolbar/utils/tracking/db.py | 85 |
1 files changed, 85 insertions, 0 deletions
diff --git a/debug_toolbar/utils/tracking/db.py b/debug_toolbar/utils/tracking/db.py new file mode 100644 index 0000000..4c9ee53 --- /dev/null +++ b/debug_toolbar/utils/tracking/db.py @@ -0,0 +1,85 @@ +import sys +import traceback + +from datetime import datetime + +from django.conf import settings +from django.template import Node +from django.utils import simplejson +from django.utils.encoding import force_unicode +from django.utils.hashcompat import sha_constructor + +from debug_toolbar.utils import ms_from_timedelta, tidy_stacktrace, get_template_info + +# TODO:This should be set in the toolbar loader as a default and panels should +# get a copy of the toolbar object with access to its config dictionary +SQL_WARNING_THRESHOLD = getattr(settings, 'DEBUG_TOOLBAR_CONFIG', {}) \ + .get('SQL_WARNING_THRESHOLD', 500) + +class CursorWrapper(object): + """ + Wraps a cursor and logs queries. + """ + + def __init__(self, cursor, db, logger): + self.cursor = cursor + # Instance of a BaseDatabaseWrapper subclass + self.db = db + # logger must implement a ``record`` method + self.logger = logger + + def execute(self, sql, params=()): + start = datetime.now() + try: + return self.cursor.execute(sql, params) + finally: + stop = datetime.now() + duration = ms_from_timedelta(stop - start) + stacktrace = tidy_stacktrace(traceback.extract_stack()) + _params = '' + try: + _params = simplejson.dumps([force_unicode(x, strings_only=True) for x in params]) + except TypeError: + pass # object not JSON serializable + + template_info = None + cur_frame = sys._getframe().f_back + try: + while cur_frame is not None: + if cur_frame.f_code.co_name == 'render': + node = cur_frame.f_locals['self'] + if isinstance(node, Node): + template_info = get_template_info(node.source) + break + cur_frame = cur_frame.f_back + except: + pass + del cur_frame + + # We keep `sql` to maintain backwards compatibility + self.logger.record(**{ + 'alias': getattr(self, 'alias', 'default'), + 'sql': self.db.ops.last_executed_query(self.cursor, sql, params), + 'duration': duration, + 'raw_sql': sql, + 'params': _params, + 'hash': sha_constructor(settings.SECRET_KEY + sql + _params).hexdigest(), + 'stacktrace': stacktrace, + 'start_time': start, + 'stop_time': stop, + 'is_slow': (duration > SQL_WARNING_THRESHOLD), + 'is_select': sql.lower().strip().startswith('select'), + 'template_info': template_info, + }) + + def executemany(self, sql, param_list): + return self.cursor.executemany(sql, param_list) + + def __getattr__(self, attr): + if attr in self.__dict__: + return self.__dict__[attr] + else: + return getattr(self.cursor, attr) + + def __iter__(self): + return iter(self.cursor)
\ No newline at end of file |
