【问题标题】:Python -- Send Email When Exception Is Raised?Python——引发异常时发送电子邮件?
【发布时间】:2011-05-31 03:20:22
【问题描述】:

我有一个python 类,其中包含许多方法():

Method1()

Method2()

...........

...........

MethodN()

所有方法——在执行不同任务时——具有相同的方案:

do something
do something else
has anything gone wrong?
    raise an exception

我希望在课堂任何地方出现异常时都能收到电子邮件。

是否有一些简单的方法可以将此逻辑组合到类中,而不是在每个 raise Exception 语句之前调用 SendEmail()?处理这种情况的正确的pythonic方式是什么?可以使用“通用”异常处理程序作为解决方案吗?如果您有任何想法,我会很高兴。

【问题讨论】:

  • 有些异常不需要捕获,即使在全局处理程序中也是如此。

标签: python exception-handling


【解决方案1】:

就像@User 之前所说的那样,Python 有 logging.handlers.SMTPHandler 发送记录的错误消息。使用日志模块!重写异常类来发送电子邮件是个坏主意。

快速示例:

import logging
import logging.handlers

smtp_handler = logging.handlers.SMTPHandler(mailhost=("smtp.example.com", 25),
                                            fromaddr="from@example.com", 
                                            toaddrs="to@example.com",
                                            subject=u"AppName error!")


logger = logging.getLogger()
logger.addHandler(smtp_handler)

try:
  break
except Exception as e:
  logger.exception('Unhandled Exception')

【讨论】:

  • 我认为这是最好的答案,但我会将最后一行更改为:logger.exception ('Unhandled Exception')
  • 这对我不起作用。我使用 gmail,所以我将 mailhost 更改为“smtp.gmail.com”,并将 fromaddr 和 toaddrs 更改为两个 gmail 地址。我没有收到任何电子邮件。
  • @horaceT 对于 gmail,将端口从 25 更改为 587 并添加参数 credentials=(EMAIL, PASSWORD)secure=()。请参阅@guneysus 的以下答案
【解决方案2】:

Gist Link

如果没有传递安全参数,最重要的trick就在这里,默认值为None,如果您尝试使用TLS/SSL启用的STMP进行身份验证,则会引发异常服务器如 Gmail、Yahoo、Yandex、STMP 服务器。

我们传递了一个空元组来触发 smtp.ehlo() 以正确使用 SSL 进行身份验证。

...
if self.secure is not None:
    smtp.ehlo()
    smtp.starttls(*self.secure)
    smtp.ehlo()
...


import logging
import logging.handlers

__author__ = 'Ahmed Şeref GÜNEYSU'


def foo():
    raise Exception("Foo Bar")


def main():
    logger = logging.getLogger()
    logger.addHandler(logging.handlers.SMTPHandler(
        mailhost=("smtp.mail.yahoo.com", 587),
        fromaddr="boss@example.com",
        toaddrs="me@example.com",
        subject="EXCEPTION",
        credentials=('smtpuser@example.com', 'MY SECRET PASSWORD'),
        secure=()))
    try:
        foo()
    except Exception, e:
        logging.exception(e)


if __name__ == '__main__':
    main()

【讨论】:

    【解决方案3】:

    到 2019 年,最简单最好的选择似乎是sentry

    你只需要两行代码:

    import sentry_sdk
    sentry_sdk.init("https://xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx@sentry.io/xxxxxxx")
    

    它会向您发送一封详细的电子邮件,其中包含任何出现的错误。

    在此之后,您可以在 Sentry 网站上进一步检查错误,那里有令人印象深刻的调试信息 - 异常回溯、日志、传递给函数的数据、包版本等...

    【讨论】:

    • 这是商业套餐的广告吗?
    【解决方案4】:

    注意:虽然这是解决上述问题的简单、明显的方法,但在大多数情况下,below answer 可能更好。

    如果是这样的话:

    if problem_test():
        SendEmail()
        raise Exception
    

    那你为什么不直接定义一个自定义的raise_email 方法呢?

    def raise_email(self, e):
        SendEmail()
        raise e
    

    【讨论】:

      【解决方案5】:

      我只需subclass Exception 并在自定义异常中发送电子邮件。

      【讨论】:

        【解决方案6】:

        Python stdlib 有专门的类来做你想做的事。见logging.handlers.SMTPHandler

        【讨论】:

          【解决方案7】:

          大概是这样的吧?

          def mailexception(ex):
            # Be creative.
            print 'Mailing... NOW!'
          
          def pokemontrainer(cls):
            class Rye(cls):
              def __getattribute__(self, name):
                def catcher(func):
                  def caller(*args, **kwargs):
                    try:
                      func(*args, **kwargs)
                    except Exception, e:
                      mailexception(e)
                      raise
                  return caller
                ref = cls.__getattribute__(self, name)
                if hasattr(cls, name) and hasattr(getattr(cls, name), '__call__'):
                  return catcher(ref)
            return Rye
          
          @pokemontrainer
          class Exceptor(object):
            def toss(self, e):
              raise e('Incoming salad!')
          
          ex = Exceptor()
          ex.toss(ValueError)
          

          【讨论】:

          • 我在 class 内的 3 个 defs 之间迷路了,这也在 def 内。可能您的解决方案是完美的,但是,有这样的复杂性是最重要的吗?
          • 它将类包装在另一个类中,该类检查属性访问是否是方法,如果是,则将方法包装在捕获任何异常并依次发送电子邮件的函数中。它动态包装它们,以便以后可以添加方法,而不是在构建类时包装它们并丢失之后添加的任何方法。
          • 顺便说一句,当它不是函数时,您忘记返回属性。就在“不可读代码的危险”
          【解决方案8】:

          当心the wizard's apprentice

          最好记录这些错误,然后检查最后一封电子邮件的发送时间,如果时间跨度太短,请不要发送另一条消息,因为人们已经在查看日志文件。对于很多事情,每天一封邮件就足够了,但即使对于系统关键的事情,如果您已经有一个故障,如果您等待两个小时发送下一封电子邮件会出现什么问题?

          如果您每两小时发送一封电子邮件,那么每天的最大电子邮件数量是 12失败事件。

          大多数大型网络公司提供 4 小时的 SLA 来修复故障,从故障首次发生(因为级联故障往往会重复)到客户对故障修复感到满意为止。如果您的 SLA 比这更严格,那么除非是某些金融行业服务,否则您可能提供的服务水平太高。

          但是,如果您确实有 4 小时的 SLA,那么我会确保在最后一封电子邮件的 2 到 4 小时内发送的任何电子邮件都应该使用您可以使用的任何花里胡哨的方式来确定它的优先级、突出显示它等等。对于例如使用 X-Priority 标头并将 URGENT 一词放在主题中,以便您的邮件客户端可以用大号粗体红色字母显示它。

          【讨论】:

            【解决方案9】:

            这个怎么样:

            class MyException(Exception):
                def __init__(self):
                    SendEmail()
            

            【讨论】:

              【解决方案10】:

              您可以使用 except 挂钩在未捕获到异常时发送电子邮件。

              sys.excepthook

              【讨论】:

                【解决方案11】:

                您可以使用 python 警报库。它支持电子邮件(通过 mailgun 和 sendgrid)、电报和用于发送警报的松弛通知。

                https://github.com/sinarezaei/alerting

                示例代码:

                from alerting import Alerting
                from alerting.clients import AlertingMailGunClient, AlertingSlackClient, AlertingTelegramClient
                
                alerts = Alerting(
                  clients=[
                    AlertingMailGunClient(your_mailgun_api_key, your_domain, from_email, target_email),
                    AlertingSlackClient(your_bot_user_oauth, target_channel),
                    AlertingTelegramClient(bot_token, chat_id)
                  ]
                )
                
                
                try:
                  # something
                except Exception as ex:
                  alerting.send_alert(title='some bad error happened', message=str(ex))
                
                

                【讨论】:

                  【解决方案12】:

                  我喜欢使用 logging 模块的答案,但我为此使用了 smtp 库 (smtplib)。要发送错误消息,我在 try/except 块的异常分支中执行以下操作:

                  import smtplib as smtp
                  
                  s = smtplib.SMTP('smtp.gmail.com', 587)
                  s.starttls()  
                  from_user = r"foo@gmail.com"
                  to_user = r"bar@gmail.com"
                  password = "xxxxxxxx"  
                  s.login(from_user, password)
                  subject = "Uh oh"
                  text = "XYZ error message you blew it!"
                  message = f"Subject: {subject}\n\n{text}"
                  s.sendmail(from_user, to_user, message);
                  

                  这很好用,但不是世界上最安全的选择。你实际上必须告诉谷歌你想让不太安全的应用程序连接(你可以在这里更改这个设置:https://myaccount.google.com/lesssecureapps)。

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2018-10-17
                    • 2017-05-03
                    • 1970-01-01
                    • 2020-03-03
                    相关资源
                    最近更新 更多