diff --git a/dmoj/urls.py b/dmoj/urls.py index 72e81cb..01bbbd9 100644 --- a/dmoj/urls.py +++ b/dmoj/urls.py @@ -1069,6 +1069,26 @@ urlpatterns = [ internal.InternalProblem.as_view(), name="internal_problem", ), + url( + r"^request_time$", + internal.InternalRequestTime.as_view(), + name="internal_request_time", + ), + url( + r"^request_time_detail$", + internal.InternalRequestTimeDetail.as_view(), + name="internal_request_time_detail", + ), + url( + r"^internal_slow_request$", + internal.InternalSlowRequest.as_view(), + name="internal_slow_request", + ), + url( + r"^internal_slow_request_detail$", + internal.InternalSlowRequestDetail.as_view(), + name="internal_slow_request_detail", + ), ] ), ), diff --git a/judge/middleware.py b/judge/middleware.py index b86731d..ccd1547 100644 --- a/judge/middleware.py +++ b/judge/middleware.py @@ -1,5 +1,8 @@ import time import logging +import random +import json +from datetime import datetime from django.conf import settings from django.http import HttpResponseRedirect, Http404 @@ -148,17 +151,23 @@ class SlowRequestMiddleware(object): self.get_response = get_response def __call__(self, request): - logger = logging.getLogger("judge.slow_request") + logger = logging.getLogger("judge.request_time") + logger_slow = logging.getLogger("judge.slow_request") start_time = time.time() response = self.get_response(request) response_time = time.time() - start_time + url_name = resolve(request.path).url_name + + message = { + "url_name": url_name, + "response_time": response_time * 1000, + "profile": request.user.username, + "date": datetime.now().strftime("%Y/%m/%d"), + "url": request.build_absolute_uri(), + "method": request.method, + } if response_time > 9: - message = { - "message": "Slow request", - "url": request.build_absolute_uri(), - "response_time": response_time * 1000, - "method": request.method, - "profile": request.profile, - } - logger.info(message) + logger_slow.info(json.dumps(message)) + if random.random() < 0.1: + logger.info(json.dumps(message)) return response diff --git a/judge/views/internal.py b/judge/views/internal.py index 45f9249..7c8e6f2 100644 --- a/judge/views/internal.py +++ b/judge/views/internal.py @@ -1,16 +1,27 @@ +import logging +import json + from django.views.generic import ListView from django.utils.translation import gettext as _, gettext_lazy from django.db.models import Count from django.http import HttpResponseForbidden +from django.urls import reverse from judge.utils.diggpaginator import DiggPaginator from judge.models import VolunteerProblemVote, Problem -class InternalProblem(ListView): +class InternalView(object): + def get(self, request, *args, **kwargs): + if request.user.is_superuser: + return super(InternalView, self).get(request, *args, **kwargs) + return HttpResponseForbidden() + + +class InternalProblem(ListView, InternalView): model = Problem title = _("Internal problems") - template_name = "internal/base.html" + template_name = "internal/problem.html" paginate_by = 100 context_object_name = "problems" @@ -44,7 +55,87 @@ class InternalProblem(ListView): return context - def get(self, request, *args, **kwargs): - if request.user.is_superuser: - return super(InternalProblem, self).get(request, *args, **kwargs) - return HttpResponseForbidden() + +class RequestTimeMixin(object): + def get_requests_data(self): + logger = logging.getLogger(self.log_name) + log_filename = logger.handlers[0].baseFilename + requests = [] + + with open(log_filename, "r") as f: + for line in f: + try: + info = json.loads(line) + requests.append(info) + except: + continue + return requests + + +class InternalRequestTime(ListView, InternalView, RequestTimeMixin): + title = _("Request times") + template_name = "internal/request_time.html" + context_object_name = "pages" + log_name = "judge.request_time" + detail_url_name = "internal_request_time_detail" + page_type = "request_time" + + def get_queryset(self): + requests = self.get_requests_data() + table = {} + for r in requests: + url_name = r["url_name"] + if url_name not in table: + table[url_name] = { + "time": 0, + "count": 0, + "url_name": url_name, + "pattern": reverse(url_name) if url_name else None, + } + old_sum = table[url_name]["time"] * table[url_name]["count"] + table[url_name]["count"] += 1 + table[url_name]["time"] = (old_sum + float(r["response_time"])) / table[ + url_name + ]["count"] + order = self.request.GET.get("order", "time") + return sorted(table.values(), key=lambda x: x[order], reverse=True) + + def get_context_data(self, **kwargs): + context = super(InternalRequestTime, self).get_context_data(**kwargs) + context["page_type"] = self.page_type + context["title"] = self.title + context["current_path"] = self.request.path + context["detail_path"] = reverse(self.detail_url_name) + return context + + +class InternalRequestTimeDetail(InternalRequestTime): + template_name = "internal/request_time_detail.html" + context_object_name = "requests" + + def get_queryset(self): + url_name = self.request.GET.get("url_name", None) + if not url_name: + return HttpResponseForbidden() + if url_name == "None": + url_name = None + self.title = url_name + requests = self.get_requests_data() + filtered_requests = [r for r in requests if r["url_name"] == url_name] + order = self.request.GET.get("order", "response_time") + return sorted(filtered_requests, key=lambda x: x[order], reverse=True)[:200] + + def get_context_data(self, **kwargs): + context = super(InternalRequestTimeDetail, self).get_context_data(**kwargs) + context["url_name"] = self.request.GET.get("url_name", None) + return context + + +class InternalSlowRequest(InternalRequestTime): + log_name = "judge.slow_request" + detail_url_name = "internal_slow_request_detail" + page_type = "slow_request" + + +class InternalSlowRequestDetail(InternalRequestTimeDetail): + log_name = "judge.slow_request" diff --git a/templates/internal/left-sidebar.html b/templates/internal/left-sidebar.html new file mode 100644 index 0000000..64788be --- /dev/null +++ b/templates/internal/left-sidebar.html @@ -0,0 +1,5 @@ + diff --git a/templates/internal/base.html b/templates/internal/problem.html similarity index 94% rename from templates/internal/base.html rename to templates/internal/problem.html index 1df34d8..eebb02c 100644 --- a/templates/internal/base.html +++ b/templates/internal/problem.html @@ -25,9 +25,7 @@ {% endblock %} {% block left_sidebar %} - + {% include "internal/left-sidebar.html" %} {% endblock %} {% block middle_content %} diff --git a/templates/internal/request_time.html b/templates/internal/request_time.html new file mode 100644 index 0000000..ce3118a --- /dev/null +++ b/templates/internal/request_time.html @@ -0,0 +1,28 @@ +{% extends "two-column-content.html" %} + +{% block left_sidebar %} + {% include "internal/left-sidebar.html" %} +{% endblock %} + +{% block middle_content %} + + + + + + + + + + + {% for page in pages %} + + + + + + + {% endfor %} + +
URL NamePatternTime (ms)Count
{{page['url_name']}}{{page['pattern']}}{{page['time'] | floatformat(2)}}{{page['count']}}
+{% endblock %} \ No newline at end of file diff --git a/templates/internal/request_time_detail.html b/templates/internal/request_time_detail.html new file mode 100644 index 0000000..5e3c3bd --- /dev/null +++ b/templates/internal/request_time_detail.html @@ -0,0 +1,36 @@ +{% extends "two-column-content.html" %} + +{% block left_sidebar %} + {% include "internal/left-sidebar.html" %} +{% endblock %} + +{% block middle_content %} + + + + + + + + + + + + {% for request in requests %} + + + + + + + + {% endfor %} + +
ProfileURLMethodTime (ms)Date
+ {% if request['profile'] %} + {{request['profile']}} + {% else %} + None + {% endif %} + {{request['url']}}{{request['method']}}{{request['response_time'] | floatformat(2)}}{{request['date']}}
+{% endblock %} \ No newline at end of file