À 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?
J'ai créé une variable RequestLogMiddleware
générique qui peut être connectée à toute variable Django View
en utilisant decorator_from_middleware
.
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
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
from rest_framework import generics
from ...request_log.mixins import RequestLogViewMixin
class SomeListView(
RequestLogViewMixin,
generics.ListAPIView
):
...
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.
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):
...
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).
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