123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155 |
- """
- Debug Toolbar middleware
- """
-
- from __future__ import absolute_import, unicode_literals
-
- import re
- import threading
-
- from django.conf import settings
- from django.utils import six
- from django.utils.deprecation import MiddlewareMixin
- from django.utils.encoding import force_text
- from django.utils.lru_cache import lru_cache
- from django.utils.module_loading import import_string
-
- from debug_toolbar import settings as dt_settings
- from debug_toolbar.toolbar import DebugToolbar
-
- _HTML_TYPES = ('text/html', 'application/xhtml+xml')
-
-
- def show_toolbar(request):
- """
- Default function to determine whether to show the toolbar on a given page.
- """
- if request.META.get('REMOTE_ADDR', None) not in settings.INTERNAL_IPS:
- return False
-
- return bool(settings.DEBUG)
-
-
- @lru_cache()
- def get_show_toolbar():
- # If SHOW_TOOLBAR_CALLBACK is a string, which is the recommended
- # setup, resolve it to the corresponding callable.
- func_or_path = dt_settings.get_config()['SHOW_TOOLBAR_CALLBACK']
- if isinstance(func_or_path, six.string_types):
- return import_string(func_or_path)
- else:
- return func_or_path
-
-
- class DebugToolbarMiddleware(MiddlewareMixin):
- """
- Middleware to set up Debug Toolbar on incoming request and render toolbar
- on outgoing response.
- """
- debug_toolbars = {}
-
- def process_request(self, request):
- # Decide whether the toolbar is active for this request.
- show_toolbar = get_show_toolbar()
- if not show_toolbar(request):
- return
-
- # Don't render the toolbar during AJAX requests.
- if request.is_ajax():
- return
-
- toolbar = DebugToolbar(request)
- self.__class__.debug_toolbars[threading.current_thread().ident] = toolbar
-
- # Activate instrumentation ie. monkey-patch.
- for panel in toolbar.enabled_panels:
- panel.enable_instrumentation()
-
- # Run process_request methods of panels like Django middleware.
- response = None
- for panel in toolbar.enabled_panels:
- response = panel.process_request(request)
- if response:
- break
- return response
-
- def process_view(self, request, view_func, view_args, view_kwargs):
- toolbar = self.__class__.debug_toolbars.get(threading.current_thread().ident)
- if not toolbar:
- return
-
- # Run process_view methods of panels like Django middleware.
- response = None
- for panel in toolbar.enabled_panels:
- response = panel.process_view(request, view_func, view_args, view_kwargs)
- if response:
- break
- return response
-
- def process_response(self, request, response):
- toolbar = self.__class__.debug_toolbars.pop(threading.current_thread().ident, None)
- if not toolbar:
- return response
-
- # Run process_response methods of panels like Django middleware.
- for panel in reversed(toolbar.enabled_panels):
- new_response = panel.process_response(request, response)
- if new_response:
- response = new_response
-
- # Deactivate instrumentation ie. monkey-unpatch. This must run
- # regardless of the response. Keep 'return' clauses below.
- # (NB: Django's model for middleware doesn't guarantee anything.)
- for panel in reversed(toolbar.enabled_panels):
- panel.disable_instrumentation()
-
- # Check for responses where the toolbar can't be inserted.
- content_encoding = response.get('Content-Encoding', '')
- content_type = response.get('Content-Type', '').split(';')[0]
- if any((getattr(response, 'streaming', False),
- 'gzip' in content_encoding,
- content_type not in _HTML_TYPES)):
- return response
-
- # Collapse the toolbar by default if SHOW_COLLAPSED is set.
- if toolbar.config['SHOW_COLLAPSED'] and 'djdt' not in request.COOKIES:
- response.set_cookie('djdt', 'hide', 864000)
-
- # Insert the toolbar in the response.
- content = force_text(response.content, encoding=response.charset)
- insert_before = dt_settings.get_config()['INSERT_BEFORE']
- pattern = re.escape(insert_before)
- bits = re.split(pattern, content, flags=re.IGNORECASE)
- if len(bits) > 1:
- # When the toolbar will be inserted for sure, generate the stats.
- for panel in reversed(toolbar.enabled_panels):
- panel.generate_stats(request, response)
- panel.generate_server_timing(request, response)
-
- response = self.generate_server_timing_header(response, toolbar.enabled_panels)
-
- bits[-2] += toolbar.render_toolbar()
- response.content = insert_before.join(bits)
- if response.get('Content-Length', None):
- response['Content-Length'] = len(response.content)
- return response
-
- @staticmethod
- def generate_server_timing_header(response, panels):
- data = []
-
- for panel in panels:
- stats = panel.get_server_timing_stats()
- if not stats:
- continue
-
- for key, record in stats.items():
- # example: `SQLPanel_sql_time=0; "SQL 0 queries"`
- data.append('{}_{}={}; "{}"'.format(panel.panel_id,
- key,
- record.get('value'),
- record.get('title')))
-
- if data:
- response['Server-Timing'] = ', '.join(data)
- return response
|