exception_handlers.py 2.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465
  1. import logging
  2. from django.db.utils import IntegrityError, OperationalError
  3. from psl_dns.exceptions import UnsupportedRule
  4. from rest_framework import status
  5. from rest_framework.response import Response
  6. from rest_framework.views import exception_handler as drf_exception_handler
  7. from desecapi import metrics
  8. from desecapi.exceptions import PDNSException
  9. def exception_handler(exc, context):
  10. """
  11. desecapi specific exception handling. If no special treatment is applied,
  12. we default to restframework's exception handling. See also
  13. https://www.django-rest-framework.org/api-guide/exceptions/#custom-exception-handling
  14. """
  15. def _log():
  16. logger = logging.getLogger('django.request')
  17. logger.error('{} Supplementary Information'.format(exc.__class__),
  18. exc_info=exc, stack_info=False)
  19. def _409():
  20. return Response({'detail': f'Conflict: {exc}'}, status=status.HTTP_409_CONFLICT)
  21. def _500():
  22. return Response({'detail': "Internal Server Error. We're on it!"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
  23. def _503():
  24. return Response({'detail': 'Please try again later.'}, status=status.HTTP_503_SERVICE_UNAVAILABLE)
  25. # Catch DB OperationalError and log an extra error for additional context
  26. if (
  27. isinstance(exc, OperationalError) and
  28. isinstance(exc.args, (list, dict, tuple)) and
  29. exc.args and
  30. exc.args[0] in (
  31. 2002, # Connection refused (Socket)
  32. 2003, # Connection refused (TCP)
  33. 2005, # Unresolved host name
  34. 2007, # Server protocol mismatch
  35. 2009, # Wrong host info
  36. 2026, # SSL connection error
  37. )
  38. ):
  39. _log()
  40. metrics.get('desecapi_database_unavailable').inc()
  41. return _503()
  42. handlers = {
  43. IntegrityError: _409,
  44. OSError: _500, # OSError happens on system-related errors, like full disk or getaddrinfo() failure.
  45. UnsupportedRule: _500, # The PSL encountered an unsupported rule
  46. PDNSException: _500, # nslord/nsmaster returned an error
  47. }
  48. for exception_class, handler in handlers.items():
  49. if isinstance(exc, exception_class):
  50. _log()
  51. # TODO add metrics
  52. return handler()
  53. return drf_exception_handler(exc, context)