web-dev-qa-db-fra.com

Journalisation des demandes vers Django-rest-framework

À des fins de débogage, je voudrais utiliser le mécanisme de journalisation de Django pour consigner chaque requête entrante lorsqu'elle "arrive" au seuil de Django-rest-framework.

Djagno propose la journalisation de ses demandes (uniquement du niveau de journalisation "warning" et supérieur) de la manière suivante (à partir de la section LOGGING dans settings.py):

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

Je cherche à obtenir quelque chose comme ça (remarque: le niveau de journalisation est DEBUG):

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

Est-il possible de faire cela sans incorporer un enregistreur dans le code source de DRF?
Y at-il peut-être une sorte d’option «Logging Backend» dans DRF dont je n’ai pas connaissance?

27
OrPo

J'ai créé une variable RequestLogMiddleware générique qui peut être connectée à toute variable Django View en utilisant 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
):
    ...
24
thnee

Remplacez la méthode APIView.initial() pour ajouter vous-même la journalisation.

Méthodes d'expédition

Les méthodes suivantes sont appelées directement par la méthode .dispatch () de la vue. Celles-ci exécutent toutes les actions devant être effectuées avant ou après l'appel des méthodes de gestionnaire, telles que .get (), .post (), put (), patch () et .delete ().

.initial(self, request, *args, **kwargs)
Effectue toutes les actions nécessaires avant que la méthode du gestionnaire soit appelée. Cette méthode est utilisée pour appliquer des autorisations et des restrictions, ainsi que pour effectuer une négociation de contenu.

9
Glyn Jackson

Voici le code de la réponse de @Glyn Jackson:

dans le middleware/mixin.py

class RequestLogMiddleware(object):

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

dans la vue:

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

J'ai trouvé pour moi que le meilleur moyen et le plus flexible consistait à ajouter la journalisation via un décorateur. J'ajoute simplement le décorateur à chacune des fonctions (post, get) dont je souhaite consigner la demande, au lieu que cela fasse partie de la classe de vue globale. Plus de contrôle sur ce qui est enregistré. Ces décorateurs prennent l'objet de requête transmis (arg [1]), puis enregistrent des parties de l'objet de requête dans un fichier.

Voir https://github.com/slogan621/tscharts/commit/39ed479b04b7077f128774d3a203a86d6f68f03e pour ce qui équivaut à un modèle pour ce faire (commit affiche les modifications apportées à settings.py nécessaire pour alimenter la consignation de fichier que j'ai enregistrée). lieu, ainsi que le décorateur et exemple d'utilisation).

1
slogan621

Voici ma solution actuelle pour obtenir chaque demande/réponse dans le journal. J'ai créé un middleware compatible avec l'ancien middleware (Django <1.10) et le nouveau middleware qui enregistre chaque demande/réponse. Cette solution est la meilleure que j'ai trouvée jusqu'à présent.

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