通过自定义logging.Handler实现异常实时通知,结合限流、异步发送与上下文丰富等策略,可高效捕获并推送Python应用中的错误信息至Slack、钉钉等平台,提升生产环境问题响应速度。

在Python项目中,捕获日志中的异常并及时发送通知,本质上就是利用Python强大的
logging
ERROR
CRITICAL
要实现这一点,我们需要几个核心步骤。首先,配置好
logging
logging.error()
logging.exception()
exc_info=True
logging.exception()
接下来,关键在于创建一个或多个自定义的日志处理器。Python标准库自带了
logging.handlers.SMTPHandler
logging.Handler
基础日志配置与异常捕获示例:
立即学习“Python免费学习笔记(深入)”;
import logging
import traceback
import requests # 假设用于发送HTTP请求到通知服务
import json
import os # 用于获取环境变量,避免硬编码敏感信息
# 配置基础日志
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
# 定义一个简单的自定义通知Handler
class CustomNotificationHandler(logging.Handler):
def __init__(self, webhook_url, level=logging.ERROR):
super().__init__(level)
self.webhook_url = webhook_url
self.session = requests.Session() # 使用session保持连接,提高效率
def emit(self, record):
# 过滤掉非异常的日志,或者根据需求只处理特定级别的日志
if not record.exc_info and record.levelno < logging.ERROR:
return
# 格式化日志信息,包括异常堆栈
message = self.format(record)
# 准备发送到通知服务的payload
# 这里以一个简单的Webhook为例,实际可能需要根据服务调整
payload = {
"text": f"? **生产环境异常告警** ?\n\n**应用:** MyAwesomeApp\n**级别:** {record.levelname}\n**消息:** {record.message}\n**时间:** {self.formatTime(record, '%Y-%m-%d %H:%M:%S')}\n\n**详细信息:**\n```\n{message}\n```"
}
try:
# 异步发送通知是更好的实践,这里为简化直接发送
# 生产环境建议使用线程池、Celery等异步任务队列
response = self.session.post(self.webhook_url, json=payload, timeout=5)
response.raise_for_status() # 检查HTTP请求是否成功
except requests.exceptions.RequestException as e:
# 如果通知发送失败,我们应该记录下来,但不能再次触发通知循环
print(f"Failed to send notification: {e}")
# 这里可以考虑将失败的通知信息记录到另一个更稳定的地方,如数据库或专门的告警系统
except Exception as e:
print(f"Unexpected error in CustomNotificationHandler: {e}")
# 获取Webhook URL,通常从环境变量或配置文件读取
# SLACK_WEBHOOK_URL = os.getenv("SLACK_WEBHOOK_URL")
# 示例用一个虚拟的URL
SLACK_WEBHOOK_URL = "https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX"
if SLACK_WEBHOOK_URL:
notification_handler = CustomNotificationHandler(SLACK_WEBHOOK_URL, level=logging.ERROR)
# 给通知处理器设置一个更简洁的Formatter,因为我们已经在payload中格式化了大部分信息
notification_handler.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s', datefmt='%Y-%m-%d %H:%M:%S'))
# 将自定义的Handler添加到root logger
logging.getLogger().addHandler(notification_handler)
else:
logging.warning("SLACK_WEBHOOK_URL not set, notification handler will not be active.")
# 模拟一个会抛出异常的函数
def problematic_function(a, b):
return a / b
# 捕获并记录异常
try:
result = problematic_function(10, 0)
print(result)
except ZeroDivisionError as e:
logging.error("An error occurred during division.", exc_info=True) # exc_info=True是关键
# 或者直接使用 logging.exception()
# logging.exception("Another error occurred during division.")
print("\n--- 模拟其他日志 ---")
logging.info("This is an informational message.")
logging.warning("This is a warning, but won't trigger notification by default.")这段代码展示了一个基本的框架。
CustomNotificationHandler
emit
emit
对我来说,实时通知的价值体现在“主动”二字。传统的做法是部署服务,然后定期或在用户抱怨时去查看日志文件。这种方式效率低下且反应滞后。想象一下,如果一个核心功能在凌晨两点因为某个边缘条件崩溃了,没有实时通知,你可能要等到第二天上班,甚至更晚,才能发现问题。这期间,用户体验受损,业务损失也在悄悄累积。
有了实时通知,一旦异常发生,相关负责人(比如我,或者我的团队成员)就能立刻收到消息。这就像给系统装了一个“健康警报器”。它不仅能帮助我们更快地定位问题、减少停机时间,还能让我们对系统的健康状况有一个更直观、更及时的感知。有时候,一些看似不严重的错误,如果频繁出现,可能预示着潜在的系统瓶颈或设计缺陷,实时通知能让我们尽早注意到这些“小信号”,避免它们演变成“大事故”。它把被动救火变成了主动预防,这在任何生产环境中都是至关重要的。
邮件通知虽然经典,但在即时性、团队协作和信息聚合方面,它已经显得有些力不从心了。我个人更倾向于使用那些能直接集成到团队日常工作流中的工具。
requests
CustomNotificationHandler
以Slack为例,一个简单的
CustomNotificationHandler
emit
# ... (CustomNotificationHandler的init方法) ...
def emit(self, record):
# ... (过滤逻辑) ...
# 格式化堆栈信息
exc_text = ""
if record.exc_info:
exc_text = "".join(traceback.format_exception(*record.exc_info))
# 针对Slack的Payload
slack_message_blocks = [
{
"type": "header",
"text": {
"type": "plain_text",
"text": f"? 异常告警: {record.levelname} ?"
}
},
{
"type": "section",
"fields": [
{ "type": "mrkdwn", "text": f"*应用:* MyAwesomeApp" },
{ "type": "mrkdwn", "text": f"*时间:* {self.formatTime(record, '%Y-%m-%d %H:%M:%S')}" },
{ "type": "mrkdwn", "text": f"*级别:* {record.levelname}" },
{ "type": "mrkdwn", "text": f"*消息:* {record.message}" }
]
}
]
if exc_text:
slack_message_blocks.append({
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*堆栈信息:*\n```\n" + exc_text + "\n```"
}
})
payload = {
"blocks": slack_message_blocks
}
try:
response = self.session.post(self.webhook_url, json=payload, timeout=5)
response.raise_for_status()
except requests.exceptions.RequestException as e:
print(f"Failed to send Slack notification: {e}")这样,消息在Slack中会以更美观、结构化的形式展现,便于阅读和理解。
在生产环境里,异常通知的实现远不止“能发出去”那么简单,还需要考虑很多细节,否则可能适得其反,比如造成“告警疲劳”。
functools.lru_cache
threading
multiprocessing
WARNING
ERROR
CRITICAL
WARNING
ERROR
CRITICAL
总的来说,构建一个健壮的异常通知系统,需要对日志模块有深入理解,对通知渠道有清晰的认识,并且在生产环境的复杂性下,不断迭代和优化其稳定性和效率。
以上就是Python 如何捕获日志中的异常并发送通知的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号