2013-02-11 23:33:47 +01:00
|
|
|
from django.conf import settings
|
2012-12-19 20:19:46 +01:00
|
|
|
from decorator import RequestVariableMissingError, RequestVariableConversionError
|
|
|
|
from zephyr.lib.response import json_error
|
2013-02-11 23:33:47 +01:00
|
|
|
from django.db import connection
|
2012-12-19 20:19:46 +01:00
|
|
|
|
2012-10-16 23:52:10 +02:00
|
|
|
import logging
|
2012-11-14 21:00:26 +01:00
|
|
|
import time
|
2012-10-16 23:52:10 +02:00
|
|
|
|
|
|
|
logger = logging.getLogger('humbug.requests')
|
|
|
|
|
|
|
|
class LogRequests(object):
|
2012-11-14 21:00:26 +01:00
|
|
|
def process_request(self, request):
|
|
|
|
request._time_started = time.time()
|
|
|
|
|
2012-10-16 23:52:10 +02:00
|
|
|
def process_response(self, request, response):
|
2013-03-26 17:00:30 +01:00
|
|
|
def format_timedelta(timedelta):
|
|
|
|
if (timedelta >= 1):
|
|
|
|
return "%.1fs" % (timedelta)
|
|
|
|
return "%.0fms" % (timedelta * 1000,)
|
2012-10-16 23:52:10 +02:00
|
|
|
|
|
|
|
# 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']
|
|
|
|
|
2012-11-14 21:00:26 +01:00
|
|
|
time_delta = -1
|
|
|
|
# A time duration of -1 means the StartLogRequests middleware
|
|
|
|
# didn't run for some reason
|
2013-03-26 16:22:52 +01:00
|
|
|
optional_orig_delta = ""
|
2012-11-14 21:00:26 +01:00
|
|
|
if hasattr(request, '_time_started'):
|
|
|
|
time_delta = time.time() - request._time_started
|
2013-03-26 16:22:52 +01:00
|
|
|
if hasattr(request, "_time_stopped"):
|
|
|
|
orig_time_delta = time_delta
|
|
|
|
time_delta = ((request._time_stopped - request._time_started) +
|
|
|
|
(time.time() - request._time_restarted))
|
|
|
|
optional_orig_delta = " (lp: %s)" % (format_timedelta(orig_time_delta),)
|
2013-02-11 23:15:34 +01:00
|
|
|
|
2013-02-11 23:33:47 +01:00
|
|
|
# Get the amount of time spent doing database queries
|
2013-03-26 16:51:10 +01:00
|
|
|
db_time_output = ""
|
|
|
|
if len(connection.queries) > 0:
|
|
|
|
query_time = sum(float(query.get('time', 0)) for query in connection.queries)
|
|
|
|
db_time_output = " (db: %s/%sq)" % (format_timedelta(query_time),
|
|
|
|
len(connection.queries))
|
2013-02-11 23:33:47 +01:00
|
|
|
|
2013-03-15 18:57:58 +01:00
|
|
|
# Get the requestor's email address and client, if available.
|
2013-02-11 23:15:34 +01:00
|
|
|
try:
|
|
|
|
email = request._email
|
|
|
|
except Exception:
|
|
|
|
email = "unauth"
|
2013-03-15 18:57:58 +01:00
|
|
|
try:
|
2013-03-26 19:40:28 +01:00
|
|
|
client = request.client.name
|
2013-03-15 18:57:58 +01:00
|
|
|
except Exception:
|
|
|
|
client = "?"
|
2013-02-11 23:15:34 +01:00
|
|
|
|
2013-03-26 16:51:10 +01:00
|
|
|
logger.info('%-15s %-7s %3d %5s%s%s %s (%s via %s)' %
|
2013-03-26 17:00:30 +01:00
|
|
|
(remote_ip, request.method, response.status_code,
|
2013-03-26 16:22:52 +01:00
|
|
|
format_timedelta(time_delta), optional_orig_delta,
|
2013-03-26 16:51:10 +01:00
|
|
|
db_time_output, request.get_full_path(), email, client))
|
2013-02-12 17:26:12 +01:00
|
|
|
|
2013-02-12 22:04:37 +01:00
|
|
|
# Log some additional data whenever we return certain 40x errors
|
|
|
|
if 400 <= response.status_code < 500 and response.status_code not in [401, 404, 405]:
|
|
|
|
content = response.content
|
|
|
|
if len(content) > 100:
|
|
|
|
content = "[content more than 100 characters]"
|
|
|
|
logger.info('status=%3d, data=%s, uid=%s' % (response.status_code, content, email))
|
2012-10-16 23:52:10 +02:00
|
|
|
return response
|
2012-12-19 20:19:46 +01:00
|
|
|
|
|
|
|
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
|
2013-02-12 17:26:12 +01:00
|
|
|
|
|
|
|
# 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
|