Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# Imports 

2 

3from django.conf import settings 

4from django.core.cache import cache 

5from django import http 

6from django.http import HttpResponse 

7from django.views import debug 

8from hashlib import sha1 

9from importlib import import_module 

10import sys 

11 

12# Exports 

13 

14___all__ = ( 

15 "ErrorCaptureMiddleware", 

16) 

17 

18# Constants 

19 

20ERROR_CAPTURE_DEBUG = getattr(settings, "SUPERDJANGO_ERROR_CAPTURE_DEBUG", False) 

21ERROR_CAPTURE_REPEAT_SECONDS = getattr(settings, "SUPERDJANGO_ERROR_CAPTURE_REPEAT_SECONDS", 3600) 

22ERROR_CAPTURE_HANDLERS = getattr(settings, "SUPERDJANGO_ERROR_CAPTURE_HANDLERS", list()) 

23 

24# Classes 

25 

26 

27class ErrorCaptureMiddleware(object): 

28 """Middleware for capturing exceptions.""" 

29 

30 traceback = __import__('traceback') 

31 

32 def __init__(self, get_response): 

33 self.get_response = get_response 

34 

35 def __call__(self, request): 

36 return self.get_response(request) 

37 

38 def capture_exception(self, request, exception): 

39 """Runs the exception capture process. 

40 

41 :param request: The current HTTP request. 

42 

43 :param exception: The exception being raised. 

44 

45 :rtype: BaseType[HttpResponse] 

46 

47 """ 

48 # Just raise if it's a 404. 

49 if isinstance(exception, http.Http404): 

50 raise exception 

51 

52 # Get content and info. 

53 content = self.traceback.format_exc() 

54 info = sys.exc_info() 

55 

56 # Don't handle in DEBUG mode unless specifically requested. 

57 if settings.DEBUG and not ERROR_CAPTURE_DEBUG: 

58 return debug.technical_500_response(request, *info) 

59 

60 # Hash is used to identify the exception in cache. 

61 _hash = sha1() 

62 _hash.update(content.encode("utf-8")) 

63 digest = _hash.hexdigest() 

64 

65 # Don't do anything if the exception has been handled previously. 

66 if cache.get(digest) is not None: 

67 return 

68 

69 # Cached to avoid repetition. 

70 cache.set(digest, content, ERROR_CAPTURE_REPEAT_SECONDS) 

71 

72 # Initialize the report to be passed to the handlers. 

73 report = debug.ExceptionReporter(request, *info) 

74 

75 # Iterate through handlers. The last result is returned if it's a response. 

76 context = dict() 

77 result = None 

78 for handler in ERROR_CAPTURE_HANDLERS: 

79 callback_class = self._get_handler_instance(handler) 

80 if callback_class is None: 

81 continue 

82 

83 if result and type(result) is dict: 

84 context.update(result) 

85 

86 instance = callback_class() 

87 result = instance(report, context=context) 

88 

89 if result and isinstance(result, HttpResponse): 

90 return result 

91 

92 return debug.technical_500_response(request, *info) 

93 

94 def process_exception(self, request, exception): 

95 """Process the exception.""" 

96 return self.capture_exception(request, exception) 

97 

98 # noinspection PyMethodMayBeStatic 

99 def _get_handler_instance(self, dotted): 

100 """Get the handler instance from the given dotted path. 

101 

102 :param dotted: The dotted path to the handler. 

103 :type dotted: str 

104 

105 :returns: The instance or ``None`` if it could not be loaded. 

106 

107 """ 

108 class_name = dotted.split(".")[-1] 

109 dotted = dotted.replace(".%s" % class_name, "") 

110 try: 

111 module = import_module(dotted) 

112 try: 

113 return getattr(module, class_name) 

114 except AttributeError: 

115 return None 

116 except ImportError: 

117 return None