Log APIExceptions on the server

This commit is contained in:
FinnStutzenstein 2020-05-25 09:16:37 +02:00
parent 4ac7b1eb4b
commit fba043fedf
No known key found for this signature in database
GPG Key ID: 9042F605C6324654
2 changed files with 28 additions and 3 deletions

View File

@ -6,6 +6,7 @@ from django.db.models import Model
from django.http import Http404 from django.http import Http404
from rest_framework import status from rest_framework import status
from rest_framework.decorators import detail_route, list_route from rest_framework.decorators import detail_route, list_route
from rest_framework.exceptions import APIException
from rest_framework.metadata import SimpleMetadata from rest_framework.metadata import SimpleMetadata
from rest_framework.mixins import ( from rest_framework.mixins import (
CreateModelMixin as _CreateModelMixin, CreateModelMixin as _CreateModelMixin,
@ -44,6 +45,7 @@ from rest_framework.viewsets import (
ModelViewSet as _ModelViewSet, ModelViewSet as _ModelViewSet,
) )
from . import logging
from .access_permissions import BaseAccessPermissions from .access_permissions import BaseAccessPermissions
from .cache import element_cache from .cache import element_cache
@ -70,6 +72,7 @@ __all__ = [
router = DefaultRouter() router = DefaultRouter()
error_logger = logging.getLogger("openslides.requests.errors")
class IdManyRelatedField(ManyRelatedField): class IdManyRelatedField(ManyRelatedField):
@ -129,6 +132,27 @@ class IdPrimaryKeyRelatedField(PrimaryKeyRelatedField):
return IdManyRelatedField(**list_kwargs) return IdManyRelatedField(**list_kwargs)
class ErrorLoggingMixin:
def handle_exception(self, exc: Any) -> Response:
user_id = self.request.user.pk or 0 # type: ignore
path = self.request._request.get_full_path() # type: ignore
prefix = f"{path} {user_id}"
if isinstance(exc, APIException):
detail = self._detail_to_string(exc.detail)
error_logger.warn(f"{prefix} {str(detail)}")
else:
error_logger.warn(f"{prefix} unknown exception: {exc}")
return super().handle_exception(exc) # type: ignore
def _detail_to_string(self, detail: Any) -> Any:
if isinstance(detail, list):
return [self._detail_to_string(item) for item in detail]
elif isinstance(detail, dict):
return {key: self._detail_to_string(value) for key, value in detail.items()}
else:
return str(detail)
class PermissionMixin: class PermissionMixin:
""" """
Mixin for subclasses of APIView like GenericViewSet and ModelViewSet. Mixin for subclasses of APIView like GenericViewSet and ModelViewSet.
@ -324,11 +348,12 @@ class UpdateModelMixin(_UpdateModelMixin):
return response return response
class GenericViewSet(PermissionMixin, _GenericViewSet): class GenericViewSet(ErrorLoggingMixin, PermissionMixin, _GenericViewSet):
pass pass
class ModelViewSet( class ModelViewSet(
ErrorLoggingMixin,
PermissionMixin, PermissionMixin,
ListModelMixin, ListModelMixin,
RetrieveModelMixin, RetrieveModelMixin,

View File

@ -3,10 +3,10 @@ from typing import Any, Dict, List, Set
from django.db import models, transaction from django.db import models, transaction
from rest_framework.views import APIView as _APIView from rest_framework.views import APIView as _APIView
from .rest_api import Response, ValidationError from .rest_api import ErrorLoggingMixin, Response, ValidationError
class APIView(_APIView): class APIView(ErrorLoggingMixin, _APIView):
""" """
The Django Rest framework APIView with improvements for OpenSlides. The Django Rest framework APIView with improvements for OpenSlides.
""" """