如何限制Django错误邮件的发送频率

19 投票
5 回答
3620 浏览
提问于 2025-04-15 17:57

我在用Django的错误报告功能,通过邮件来接收错误信息。这个功能通常很有用,但现在我们数据库出现了5分钟的故障,我收到了2000封邮件。有没有什么中间件可以帮我限制Django每分钟发送的邮件数量呢?

5 个回答

2

一个选择是换用像 ErrorStack 这样的工具来报告错误。我写了一个 django 应用,让它变得超级简单,可以轻松地集成到你的项目中。

8

我通过以下方法把每分钟发送的邮件限制在10封。这是用我自己安装的redis连接功能实现的。我建议你根据自己的需求修改incr_counter这个函数。为了安全起见,最好直接使用redis或memcache的连接,而不是用django.cache的封装。

设置文件:settings.py

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'mail_admins': {
            'level': 'ERROR',
            'class': 'error_email_limiter.handler.MyAdminEmailHandler'
        }
    },
    'loggers': {
        'django.request': {
            'handlers': ['mail_admins'],
            'level': 'ERROR',
            'propagate': True,
            },
        }
}

错误邮件限制器的处理文件:error_email_limiter/handlers.py

class MyAdminEmailHandler(AdminEmailHandler):
    def incr_counter(self):
        c = get_redis_connection()
        key = self._redis_key()
        res = c.incr(key)
        c.expire(key, 300)
        return res

    def _redis_key(self):
        return time.strftime('error_email_limiter:%Y-%m-%d_%H:%M',
                             datetime.datetime.now().timetuple())

    def emit(self, record):
        try:
            ctr = self.incr_counter()
        except Exception:
            pass
        else:
            if ctr >= 10:
                return
        super(MyAdminEmailHandler, self).emit(record)
17

以Gattster的精彩回答为例,我写了一个简单的实现,基于django自带的缓存功能。

# -*- coding: utf-8 -*-

from django.utils.log import AdminEmailHandler
from django.core.cache import cache


class ThrottledAdminEmailHandler(AdminEmailHandler):

    PERIOD_LENGTH_IN_SECONDS = 10
    MAX_EMAILS_IN_PERIOD = 1
    COUNTER_CACHE_KEY = "email_admins_counter"

    def increment_counter(self):
        try:
            cache.incr(self.COUNTER_CACHE_KEY)
        except ValueError:
            cache.set(self.COUNTER_CACHE_KEY, 1, self.PERIOD_LENGTH_IN_SECONDS)
        return cache.get(self.COUNTER_CACHE_KEY)

    def emit(self, record):
        try:
            counter = self.increment_counter()
        except Exception:
            pass
        else:
            if counter > self.MAX_EMAILS_IN_PERIOD:
                return
        super(ThrottledAdminEmailHandler, self).emit(record)

另外,从Django 1.9开始,日志配置也有了变化,所以为了让这个处理器正常工作,你需要这样配置日志:

LOGGING = {
    'version': 1,
    'disable_existing_loggers': True,
    'handlers': {
        'mail_admins': {
            'level': 'ERROR',
            'class': 'fully.qualified.path.to.handler.ThrottledAdminEmailHandler'
        }
    },
    'loggers': {
        'django': {
            'handlers': ['mail_admins'],
            'level': 'ERROR',
            'propagate': True,
        },
    }
}

这里的变化只是把日志记录器的名字从django.request改成django。如果你查看日志系统的文档,可能会发现通过实现一个日志过滤器,可以用更简洁的方式来实现这个功能。

撰写回答