1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374 |
- import logging
- from django.db.utils import IntegrityError, OperationalError
- from rest_framework import status
- from rest_framework.response import Response
- from rest_framework.views import exception_handler as drf_exception_handler
- from desecapi import metrics
- from desecapi.exceptions import PDNSException
- def exception_handler(exc, context):
- """
- desecapi specific exception handling. If no special treatment is applied,
- we default to restframework's exception handling. See also
- https://www.django-rest-framework.org/api-guide/exceptions/#custom-exception-handling
- """
- class_path = f"{exc.__class__.__module__}.{exc.__class__.__name__}"
- def _log():
- logger = logging.getLogger("django.request")
- logger.error(
- f"{class_path} Supplementary Information",
- exc_info=exc,
- stack_info=False,
- )
- def _409():
- return Response({"detail": f"Conflict: {exc}"}, status=status.HTTP_409_CONFLICT)
- def _500():
- _log()
- return Response(
- {"detail": "Internal Server Error. We're on it!"},
- status=status.HTTP_500_INTERNAL_SERVER_ERROR,
- )
- def _503():
- _log()
- return Response(
- {"detail": "Please try again later."},
- status=status.HTTP_503_SERVICE_UNAVAILABLE,
- )
- # Catch DB OperationalError and log an extra error for additional context
- if (
- isinstance(exc, OperationalError)
- and isinstance(exc.args, (list, dict, tuple))
- and exc.args
- and exc.args[0]
- in (
- 2002, # Connection refused (Socket)
- 2003, # Connection refused (TCP)
- 2005, # Unresolved host name
- 2007, # Server protocol mismatch
- 2009, # Wrong host info
- 2026, # SSL connection error
- )
- ):
- metrics.get("desecapi_database_unavailable").inc()
- return _503()
- handlers = {
- IntegrityError: _409,
- OSError: _500, # OSError happens on system-related errors, like full disk or getaddrinfo() failure.
- PDNSException: _500, # nslord/nsmaster returned an error
- }
- for exception_class, handler in handlers.items():
- if isinstance(exc, exception_class):
- metrics.get("desecapi_exception").labels(class_path).inc()
- return handler()
- return drf_exception_handler(exc, context)
|