Browse Source

feat(api): Generalize desecapi-specific exception handler, add OSError

This is to catch OSError, which includes socket.gaierror (from getaddrinfo()
syscall) and other error conditions, like I/O failures, disk full etc.
Peter Thomassen 6 years ago
parent
commit
33508369b6
2 changed files with 19 additions and 10 deletions
  1. 1 1
      api/api/settings.py
  2. 18 9
      api/desecapi/exception_handlers.py

+ 1 - 1
api/api/settings.py

@@ -88,7 +88,7 @@ REST_FRAMEWORK = {
         'desecapi.authentication.TokenAuthentication',
         'desecapi.authentication.TokenAuthentication',
     ),
     ),
     'TEST_REQUEST_DEFAULT_FORMAT': 'json',
     'TEST_REQUEST_DEFAULT_FORMAT': 'json',
-    'EXCEPTION_HANDLER': 'desecapi.exception_handlers.handle_db_unavailable',
+    'EXCEPTION_HANDLER': 'desecapi.exception_handlers.exception_handler',
     'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.NamespaceVersioning',
     'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.NamespaceVersioning',
     'ALLOWED_VERSIONS': ['v1', 'v2'],
     'ALLOWED_VERSIONS': ['v1', 'v2'],
 }
 }

+ 18 - 9
api/desecapi/exception_handlers.py

@@ -1,17 +1,27 @@
 from django.db.utils import OperationalError
 from django.db.utils import OperationalError
 from rest_framework import status
 from rest_framework import status
 from rest_framework.response import Response
 from rest_framework.response import Response
-from rest_framework.views import exception_handler
+from rest_framework.views import exception_handler as drf_exception_handler
 import logging
 import logging
 
 
 
 
-def handle_db_unavailable(exc, context):
+def exception_handler(exc, context):
     """
     """
     desecapi specific exception handling. If no special treatment is applied,
     desecapi specific exception handling. If no special treatment is applied,
     we default to restframework's exception handling. See also
     we default to restframework's exception handling. See also
     https://www.django-rest-framework.org/api-guide/exceptions/#custom-exception-handling
     https://www.django-rest-framework.org/api-guide/exceptions/#custom-exception-handling
     """
     """
 
 
+    def _perform_handling(name):
+        logger = logging.getLogger('django.request')
+        logger.error('{} Supplementary Information'.format(name),
+                     exc_info=exc, stack_info=False)
+
+        # Gracefully let clients know that we cannot connect to the database
+        return Response({'detail': 'Please try again later.'},
+                        status=status.HTTP_503_SERVICE_UNAVAILABLE)
+
+    # Catch DB exception and log an extra error for additional context
     if isinstance(exc, OperationalError):
     if isinstance(exc, OperationalError):
         if isinstance(exc.args, (list, dict, tuple)) and exc.args and \
         if isinstance(exc.args, (list, dict, tuple)) and exc.args and \
             exc.args[0] in (
             exc.args[0] in (
@@ -22,12 +32,11 @@ def handle_db_unavailable(exc, context):
                 2009,  # Wrong host info
                 2009,  # Wrong host info
                 2026,  # SSL connection error
                 2026,  # SSL connection error
         ):
         ):
-            logging.getLogger('django.request').error('OperationalError Supplementary Information',
-                                                      exc_info=exc, stack_info=False)
-
-            # Gracefully let clients know that we cannot connect to the database
-            data = {'detail': 'Please try again later.'}
+            return _perform_handling('OperationalError')
 
 
-            return Response(data, status=status.HTTP_503_SERVICE_UNAVAILABLE)
+    # OSError happens on system-related errors, like full disk or getaddrinfo() failure.
+    # Catch it and log an extra error for additional context.
+    if isinstance(exc, OSError):
+        return _perform_handling('OSError')
 
 
-    return exception_handler(exc, context)
+    return drf_exception_handler(exc, context)