Files
zulip/zephyr/middleware.py
Tim Abbott 54b10a292c Monkeypatch Django cursor to log query times even when DEBUG=False.
I don't like monkeypatching in general, but this will give us logging
of how much time is being spent in total on database queries on our
staging and prod instances for each of the views we have -- which
should help a lot with debugging performance problems.

The code being patched is identical in Django 1.5, so I don't expect
any issues upgrading in the near future.

I may work on a patch to upstream Django to support this as well.

(imported from commit 08624c7a30f1fd192a972973e0380dd6a131d84e)
2013-02-12 12:01:02 -05:00

75 lines
2.6 KiB
Python

from django.conf import settings
from decorator import RequestVariableMissingError, RequestVariableConversionError
from zephyr.lib.response import json_error
from django.db import connection
import logging
import time
logger = logging.getLogger('humbug.requests')
class LogRequests(object):
def process_request(self, request):
request._time_started = time.time()
def process_response(self, request, response):
# The reverse proxy might have sent us the real external IP
remote_ip = request.META.get('HTTP_X_REAL_IP')
if remote_ip is None:
remote_ip = request.META['REMOTE_ADDR']
time_delta = -1
# A time duration of -1 means the StartLogRequests middleware
# didn't run for some reason
if hasattr(request, '_time_started'):
time_delta = time.time() - request._time_started
# Get the amount of time spent doing database queries
query_time = sum(float(query.get('time', 0)) for query in connection.queries)
# Get the requestor's email address, if available.
try:
email = request._email
except Exception:
email = "unauth"
logger.info('%-15s %-7s %3d %.3fs (db: %.3fs/%sq) %s (%s)'
% (remote_ip, request.method, response.status_code,
time_delta, query_time, len(connection.queries),
request.get_full_path(), email))
# Log some additional data whenever we return a 40x error
if 400 <= response.status_code < 500:
logger.info('status=%3d, data=%s, uid=%s' % (response.status_code, response.content, email))
return response
class JsonErrorHandler(object):
def process_exception(self, request, exception):
if hasattr(exception, 'to_json_error_msg') and callable(exception.to_json_error_msg):
return json_error(exception.to_json_error_msg())
return None
# Monkeypatch in time tracking to the Django non-debug cursor
# Code comes from CursorDebugWrapper
def wrapper_execute(self, action, sql, params=()):
self.set_dirty()
start = time.time()
try:
return action(sql, params)
finally:
stop = time.time()
duration = stop - start
self.db.queries.append({
'time': "%.3f" % duration,
})
from django.db.backends.util import CursorWrapper
def cursor_execute(self, sql, params=()):
return wrapper_execute(self, self.cursor.execute, sql, params)
CursorWrapper.execute = cursor_execute
def cursor_executemany(self, sql, params=()):
return wrapper_execute(self, self.cursor.executemany, sql, params)
CursorWrapper.executemany = cursor_executemany