exception_handlers.py 2.3 KB

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