【问题标题】:Manually trigger Django email error report手动触发 Django 邮件错误报告
【发布时间】:2015-06-06 04:30:41
【问题描述】:

Django error reporting 通过发送电子邮件来处理未捕获的异常,并且(可选)向用户显示一个漂亮的 500 错误页面。

这很好用,但在某些情况下,我希望允许用户不间断地继续他们的业务,但仍然让 Django 向我发送有关异常的电子邮件错误报告。

所以基本上:即使我发现异常,我也可以手动发送电子邮件错误报告吗?

当然,我想避免手动生成错误报告电子邮件。

【问题讨论】:

    标签: python django exception-handling django-email django-errors


    【解决方案1】:

    是的,即使您发现异常,您也可以手动发送电子邮件错误报告。

    有几种方法可以解决这个问题。

    1. 您可以为 django.request 使用现有的默认记录器配置(及其关联的处理程序配置,记录在 here),它将所有错误消息发送到 mail_admins 处理程序,该处理程序从 django.request 发送使用 log.error 记录的任何内容当使用AdminEmailHandler 的电子邮件调试为错误时,其现有调用点位于handle_uncaught_exception
    2. 您可以添加使用相同处理程序的其他记录器配置,以便在 django.request 之前捕获您的异常并更早地调用 log.error。
    3. 您可以继承 django.request,特别是 handle_uncaught_exception。
    4. 您可以使用自定义中间件 (for example StandardExceptionMiddleware) 或 ExceptionMiddleware
    5. 您可以在AdminEmailHandler或mail.mail_admins中手动调用emit的内容。

    在这些选项中,选项 4 似乎是最常用的。

    根据您评论中的附加信息,下面是 2 的代码示例。

    首先是要添加到视图中的代码

    from django.http import HttpResponse
    import logging
    logger = logging.getLogger(__name__)
    
    def my_view(request):
    
        try:
            result = do_something()
            return HttpResponse('<h1>Page was found' + result + '</h1>')
        except Exception: 
             # Can have whatever status_code and title you like, but I was just matching the existing call.
             logger.error('Internal Server Error: %s', request.path,
                exc_info=sys.exc_info(),
                extra={
                'status_code': 500,
                'request': request
                }
             )
             return HttpResponse('<h1>Page was found, and exception was mailed to admins.</h1>')
    

    这是基于Django documentation for view writingand introduction to Django logging,但尚未经过测试。

    然后将额外的记录器配置添加到记录器条目中(根据here

    'nameofdjangoapplicationgoeshere': {
                'handlers': ['mail_admins'],
                'level': 'ERROR',
                'propagate': False,
            },
    

    【讨论】:

    • 我希望从 View 触发错误报告,然后像往常一样继续操作(即允许 View 返回 HttpResponse),所以我认为中间件不会。还是我错过了,@Brandon 的评论也表明中间件?选项 1 和 2 似乎是答案。想添加一个代码示例吗?
    【解决方案2】:

    只需在您的设置中设置一个简单的日志处理程序即可。

    LOGGING = {
        'version': 1, 
        'disable_existing_loggers': False,
        'filters': {
            'require_debug_false': {
                '()': 'django.utils.log.RequireDebugFalse'
            }
        },
        'handlers': {
            'mail_admins': {
                'level': 'ERROR',
                'filters': ['require_debug_false'],
                'class': 'django.utils.log.AdminEmailHandler'
            },
            'app': {
                'level': 'ERROR',
                'filters': ['require_debug_false'],
                'class': 'django.utils.log.AdminEmailHandler'
            },
        },
        'loggers': {
            'django.request': {
                'handlers': ['mail_admins'],
                'level': 'ERROR',
                'propagate': True,
            },
        }
    }
    

    然后在你看来,你可以做任何事

     import logging
     logger = logging.getLogger('app')
    
     def some_view(request):
         try:
             # something
             if something_wnet_wrong:
                 logger.error('Something went wrong!')
             return some_http_response
         except:
             #something else
             logger.error(sys.exc_info(), request)        
             return some_other_response
    

    如果你想要详细的错误报告,可以试试something like this

    你还需要照顾sensitive information

    【讨论】:

      【解决方案3】:

      您可以使用以下代码手动发送有关request 和异常e 的电子邮件:

      import sys
      import traceback
      from django.core import mail
      from django.views.debug import ExceptionReporter
      
      def send_manually_exception_email(request, e):
          exc_info = sys.exc_info()
          reporter = ExceptionReporter(request, is_email=True, *exc_info)
          subject = e.message.replace('\n', '\\n').replace('\r', '\\r')[:989]
          message = "%s\n\n%s" % (
              '\n'.join(traceback.format_exception(*exc_info)),
              reporter.filter.get_request_repr(request)
          )
          mail.mail_admins(
              subject, message, fail_silently=True,
              html_message=reporter.get_traceback_html()
          )
      

      您可以在这样的视图中对其进行测试:

      def test_view(request):
          try:
              raise Exception
          except Exception as e:
              send_manually_exception_email(request, e)
      

      【讨论】:

      • 最甜蜜!虽然没有答案产生完全自动生成的错误报告,但这种方法提供了大部分信息。它类似于 Django 显示的错误页面 DEBUG = True
      • 我在第 9 行遇到错误。它说:'Exception' object has no attribute 'message'
      • 这也像电子邮件报告一样过滤敏感数据,除非 DEFAULT_EXCEPTION_REPORTER_FILTER 设置为不过滤的过滤器。 (至少在 Django 1.9.6 上)
      • get_request_repr 已在 Django 1.10 中删除。我现在按照django-zh-cn.readthedocs.io/en/latest/releases/1.9.html 的建议使用message = reporter.get_traceback_text,但这看起来更像debug=True 错误页面,而不是Django 发送的自动电子邮件。
      【解决方案4】:

      我主要将此模式与标准错误报告一起使用。

      import logging    
      logger = logging.getLogger('django.request')
      
      #code block in view
      try:
          #code that can raise exception
      except:
          logger.exception('Information')
      #continue as nothing happend
      

      它将触发内置的错误报告和 logger.exception 将捕获堆栈帧。 https://docs.djangoproject.com/en/1.8/topics/logging/#making-logging-calls

      编辑:

      我注意到电子邮件中缺少一些信息,因此可以使用以下内置内容来获得准确的回溯:

      logger.exception('Internal Server Error: %s', request.path,
                       extra={'status_code': 500, 'request': request})
      

      在此处找到更多信息: How to send django exception log manually?

      【讨论】:

        【解决方案5】:

        基于@JuniorCompressor 的回答,这是我使用的代码:

        import sys
        from django.core import mail
        from django.views.debug import ExceptionReporter
        
        def send_exception_email(request, exception, subject_prefix=''):
        
            exc_info = sys.exc_info()
            reporter = ExceptionReporter(request, *exc_info, is_email=True)
        
            def exception_name():
                if exc_info[0]:
                    return exc_info[0].__name__
                return 'Exception'
        
            def subject_suffix():
                if request:
                    return '{} at {}'.format(
                        exception_name(),
                        request.path_info
                    )
                return exception_name()
        
            def subject():
                return '{}{}'.format(
                    subject_prefix,
                    subject_suffix()
                )
        
            mail.mail_admins(
                subject=subject(),
                message=reporter.get_traceback_text(),
                fail_silently=True,
                html_message=reporter.get_traceback_html()
            )
        

        【讨论】:

          【解决方案6】:

          这是@gitaarik 解决方案的精简版,适用于 Python 3:

          import sys
          
          from django.core import mail
          from django.views.debug import ExceptionReporter
          
          def send_exception_email(request, exception, subject_prefix=''):
              exc_info = sys.exc_info()
          
              exception_name = exc_info[0].__name__ if exc_info[0] else 'Exception'
              request_path = f" at {request.path_info}" if request else ''
          
              reporter = ExceptionReporter(request, *exc_info, is_email=True)
          
              mail.mail_admins(
                  subject=f"{subject_prefix}{exception_name}{request_path}",
                  message=reporter.get_traceback_text(),
                  fail_silently=True,
                  html_message=reporter.get_traceback_html(),
              )
          

          【讨论】:

            猜你喜欢
            • 2011-01-29
            • 1970-01-01
            • 2015-07-20
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2017-11-29
            相关资源
            最近更新 更多