webentwicklung-frage-antwort-db.com.de

Protokollierung von Anforderungen an das Django-Rest-Framework

Zu Debugging-Zwecken möchte ich den Protokollierungsmechanismus von Django verwenden, um jede eingehende Anforderung zu protokollieren, wenn sie bei Django-Rest-Framework "ankommt".

Djagno bietet die Protokollierung seiner Anforderungen (nur Protokollstufe "Warnung" und höher) auf folgende Weise an (aus dem LOGGING-Abschnitt in settings.py):

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

Ich möchte etwas erreichen (Hinweis: Log-Level ist DEBUG):

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

Gibt es eine Möglichkeit, dass ich ohne einen Logger in den DRF-Quellcode einbetten kann?
Gibt es in DRF möglicherweise eine "Logging Backend" -Option, von der ich keine Kenntnis habe?

27
OrPo

Ich habe ein generisches RequestLogMiddleware erstellt, das mit decorator_from_middleware in jedes Django View eingebunden werden kann.

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
):
    ...
24
thnee

Überschreiben Sie die Methode APIView.initial() , um die Protokollierung selbst hinzuzufügen.

Versandmethoden

Die folgenden Methoden werden direkt von der .dispatch () - Methode der Ansicht aufgerufen. Diese führen alle Aktionen aus, die vor oder nach dem Aufruf der Handler-Methoden wie .get (), .post (), put (), patch () und .delete () ausgeführt werden müssen.

.initial(self, request, *args, **kwargs)
Führt alle Aktionen aus, die ausgeführt werden müssen, bevor die Handler-Methode aufgerufen wird. Diese Methode wird zum Erzwingen von Berechtigungen und zur Drosselung und zum Durchführen von Inhaltsverhandlungen verwendet.

9
Glyn Jackson

Hier ist der Code von @Glyn Jacksons Antwort:

in Middleware/mixin.py

class RequestLogMiddleware(object):

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

in der Ansicht:

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

Ich fand für mich die beste und flexibelste Möglichkeit, die Protokollierung über einen Dekorateur hinzuzufügen. Ich füge den Decorator einfach zu jeder der Funktionen hinzu (post, get), von denen ich die Anforderung protokollieren möchte, im Gegensatz dazu, dass sie nicht Teil der Gesamtansicht ist. Mehr Kontrolle darüber, was protokolliert wird. Diese Dekorierer nehmen das übergebene Anforderungsobjekt (arg [1]) und protokollieren dann Teile des Anforderungsobjekts in einer Datei.

Siehe https://github.com/slogan621/tscharts/commit/39ed479b04b7077f128774d3a203a86d6f68f03e , um zu erfahren, was eine Vorlage dafür darstellt (das Festschreiben zeigt Änderungen an settings.py an, die für das Threading erforderlich sind Platz sowie den Dekorateur und die Beispielnutzung).

1
slogan621

Hier meine aktuelle Lösung, um jede REQUEST/RESPONSE in das Protokoll aufzunehmen. Ich habe eine Middleware erstellt, die mit der alten Middleware (Django <1.10) und der neuen Middleware kompatibel ist, die jede Anfrage/Antwort protokolliert. Diese Lösung ist die beste, die ich bisher gefunden habe.

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
0
Karim N Gorjux

In neu Django 2+ besser Middleware als aufrufbares Objekt verwenden, verbinden Sie es einfach mit Ihrem Projekt in Ihrem Middlewares-Abschnitt der settings.py-Datei (Middleware kann auch eine Funktion sein, nicht nur eine Klasse, da es sich um ein aufrufbares Objekt handelt. alt MiddlewareMixin jetzt im veraltet Modul von Django):

Weitere Informationen in der Dokumentation:
https://docs.djangoproject.com/de/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
0
om2c0de