Подтвердить что ты не робот

Регистрация запросов к django-rest-framework

Для целей отладки я хотел бы использовать механизм ведения журнала Django для регистрации каждого входящего запроса, когда он "прибывает" на django-rest-framework doorstep.

Djagno предлагает протоколировать свои запросы (только "уровень предупреждений" и выше) следующим образом (из раздела LOGGING в settings.py):

'django.request': {
        'handlers': ['mail_admins'],
        'level': 'ERROR',
        'propagate': False,
 },

Я хочу добиться чего-то подобного (обратите внимание: уровень журнала - DEBUG):

'rest_framework.request': {
        'handlers': ['logfile'],
        'level': 'DEBUG',
        'propagate': False,
 },

Есть ли способ сделать это без встраивания регистратора в исходный код DRF?
Возможно ли какая-то опция "Logging Backend" в DRF, о которой я не знаю?

4b9b3361

Ответ 1

Я сделал общий RequestLogMiddleware, который можно подключить к любому Django View, используя decorator_from_middleware.

request_log/middleware.py

import socket
import time


class RequestLogMiddleware(object):
    def process_request(self, request):
        request.start_time = time.time()

    def process_response(self, request, response):

        if response['content-type'] == 'application/json':
            if getattr(response, 'streaming', False):
                response_body = '<<<Streaming>>>'
            else:
                response_body = response.content
        else:
            response_body = '<<<Not JSON>>>'

        log_data = {
            'user': request.user.pk,

            'remote_address': request.META['REMOTE_ADDR'],
            'server_hostname': socket.gethostname(),

            'request_method': request.method,
            'request_path': request.get_full_path(),
            'request_body': request.body,

            'response_status': response.status_code,
            'response_body': response_body,

            'run_time': time.time() - request.start_time,
        }

        # save log_data in some way

        return response

request_log/mixins.py

from django.utils.decorators import decorator_from_middleware

from .middleware import RequestLogMiddleware


class RequestLogViewMixin(object):
    """
    Adds RequestLogMiddleware to any Django View by overriding as_view.
    """

    @classmethod
    def as_view(cls, *args, **kwargs):
        view = super(RequestLogViewMixin, cls).as_view(*args, **kwargs)
        view = decorator_from_middleware(RequestLogMiddleware)(view)
        return view

my_django_rest_api/views.py

from rest_framework import generics

from ...request_log.mixins import RequestLogViewMixin

class SomeListView(
    RequestLogViewMixin,
    generics.ListAPIView
):
    ...

Ответ 2

Переопределите метод APIView.initial() чтобы добавить APIView.initial() самостоятельно.

Методы отправки

Следующие методы вызываются напрямую методом view.dispatch(). Они выполняют любые действия, которые должны произойти до или после вызова методов-обработчиков, таких как .get(),.post(), put(), patch() и .delete().

.initial(self, request, *args, **kwargs)
Выполняет любые действия, которые должны произойти до вызова метода-обработчика. Этот метод используется для обеспечения разрешений и регулирования, а также для согласования содержимого.

Ответ 3

Я обнаружил, что лучшим и наиболее гибким способом является добавление регистрации через декоратор. Я просто добавляю декоратор к каждой из функций (post, get), от которых я хочу зарегистрировать запрос, в отличие от того, что он является частью общего класса представления. Больше контроля над тем, что регистрируется. Эти декораторы принимают объект запроса, переданный в (arg [1]), а затем записывают части объекта запроса в файл.

См. Https://github.com/slogan621/tscharts/commit/39ed479b04b7077f128774d3a203a86d6f68f03e, чтобы узнать, что представляет собой шаблон для этого (фиксация показывает изменения в settings.py, необходимые для подключения к схеме входа в систему, которую я имею на месте, а также как декоратор и пример использования).

Ответ 4

Вот код от @Glyn Jackson Ответ:

в middleware/mixin.py

class RequestLogMiddleware(object):

    def initial(self, request, *args, **kwargs):
         super(RequestLogMiddleware, self).initial(request, *args, **kwargs)
         # store/log the request

в представлении:

class ViewClass(RequestLogMiddleware, generics.RetrieveAPIView):
     ...

Ответ 5

Здесь мое текущее решение, чтобы получить каждый запрос/ответ в журнале. Я создал промежуточное ПО, совместимое со старым промежуточным ПО (Django <1.10) и новым промежуточным ПО, которое регистрирует каждый запрос/ответ. Это лучшее решение, которое я нашел до сих пор.

import logging
from django.utils.deprecation import MiddlewareMixin

_logger = logging.getLogger(__name__)

class LogRestMiddleware(MiddlewareMixin):
    """Middleware to log every request/response.
    Is not triggered when the request/response is managed using the cache
    """

    def _log_request(self, request):
        """Log the request"""
        user = str(getattr(request, 'user', ''))
        method = str(getattr(request, 'method', '')).upper()
        request_path = str(getattr(request, 'path', ''))
        query_params = str(["%s: %s" %(k,v) for k, v in request.GET.items()])
        query_params = query_params if query_params else ''

        _logger.debug("req: (%s) [%s] %s %s", user, method, request_path, query_params)

    def _log_response(self, request, response):
        """Log the response using values from the request"""
        user = str(getattr(request, 'user', ''))
        method = str(getattr(request, 'method', '')).upper()
        status_code = str(getattr(response, 'status_code', ''))
        status_text = str(getattr(response, 'status_text', ''))
        request_path = str(getattr(request, 'path', ''))
        size = str(len(response.content))

        _logger.debug("res: (%s) [%s] %s - %s (%s / %s)", user, method, request_path, status_code, status_text, size)

    def process_response(self, request, response):
        """Method call when the middleware is used in the 'MIDDLEWARE_CLASSES' option in the settings. Django < 1.10"""
        self._log_request(request)
        self._log_response(request, response)
        return response

    def __call__(self, request):
        """Method call when the middleware is used in the 'MIDDLEWARE' option in the settings (Django >= 1.10)"""
        self._log_request(request)
        response = self.get_response(request)
        self._log_response(request, response)
        return response

Ответ 6

В новом Django 2+ лучше использовать Middleware в качестве вызываемого объекта, подобного этому, просто подключите его к своему проекту в разделе Middlewares файла settings.py (также Middleware может быть функцией, а не только классом, потому что это вызываемый объект старое MiddlewareMixin теперь в устаревшем модуле Django):

Больше информации в документах:
https://docs.djangoproject.com/en/2.2/topics/http/middleware/#writing-your-own-middleware

class UserActivityLogMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        print(request.method)    # In this string we catch request object.
        response = self.get_response(request)
        return response