优化任务调度说明
This commit is contained in:
@@ -0,0 +1,237 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
报警通知模块
|
||||
功能:
|
||||
1. 多通道报警通知(邮件/企业微信/飞书)
|
||||
2. 分级报警策略
|
||||
3. 失败重试机制
|
||||
"""
|
||||
|
||||
import smtplib
|
||||
import json
|
||||
import requests
|
||||
from email.mime.text import MIMEText
|
||||
from typing import Optional, Dict, List
|
||||
import logging
|
||||
from dataclasses import dataclass
|
||||
from tenacity import retry, stop_after_attempt, wait_exponential
|
||||
|
||||
# 日志配置
|
||||
logger = logging.getLogger('alert')
|
||||
logger.setLevel(logging.INFO)
|
||||
|
||||
@dataclass
|
||||
class AlertConfig:
|
||||
"""报警配置数据类"""
|
||||
email: Dict[str, str] = None # SMTP配置
|
||||
wecom: Dict[str, str] = None # 企业微信机器人配置
|
||||
feishu: Dict[str, str] = None # 飞书机器人配置
|
||||
min_level: str = 'WARNING' # 默认最低报警级别
|
||||
|
||||
class AlertService:
|
||||
def __init__(self, config: AlertConfig):
|
||||
"""
|
||||
初始化报警服务
|
||||
参数:
|
||||
config: AlertConfig对象
|
||||
"""
|
||||
self.config = config
|
||||
self.levels = {
|
||||
'DEBUG': 0,
|
||||
'INFO': 1,
|
||||
'WARNING': 2,
|
||||
'ERROR': 3,
|
||||
'CRITICAL': 4
|
||||
}
|
||||
|
||||
@retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=4, max=10))
|
||||
def send_email(self, subject: str, content: str, to_addrs: List[str]) -> bool:
|
||||
"""
|
||||
发送邮件报警
|
||||
参数:
|
||||
subject: 邮件主题
|
||||
content: 邮件内容(支持HTML)
|
||||
to_addrs: 收件人列表
|
||||
返回:
|
||||
发送是否成功
|
||||
"""
|
||||
if not self.config.email:
|
||||
logger.warning("邮件配置未启用")
|
||||
return False
|
||||
|
||||
try:
|
||||
msg = MIMEText(content, 'html', 'utf-8')
|
||||
msg['From'] = self.config.email['from_addr']
|
||||
msg['To'] = ','.join(to_addrs)
|
||||
msg['Subject'] = subject
|
||||
|
||||
with smtplib.SMTP_SSL(
|
||||
host=self.config.email['smtp_server'],
|
||||
port=self.config.email['smtp_port']
|
||||
) as server:
|
||||
server.login(
|
||||
user=self.config.email['username'],
|
||||
password=self.config.email['password']
|
||||
)
|
||||
server.send_message(msg)
|
||||
|
||||
logger.info(f"邮件报警发送成功 -> {to_addrs}")
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"邮件发送失败: {str(e)}")
|
||||
raise
|
||||
|
||||
@retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=4, max=10))
|
||||
def send_wecom(self, content: str, mentioned_list: List[str] = None) -> bool:
|
||||
"""
|
||||
发送企业微信机器人通知
|
||||
参数:
|
||||
content: 消息内容(支持Markdown)
|
||||
mentioned_list: 要@的成员手机号列表
|
||||
返回:
|
||||
发送是否成功
|
||||
"""
|
||||
if not self.config.wecom:
|
||||
logger.warning("企业微信配置未启用")
|
||||
return False
|
||||
|
||||
try:
|
||||
payload = {
|
||||
"msgtype": "markdown",
|
||||
"markdown": {
|
||||
"content": content,
|
||||
}
|
||||
}
|
||||
if mentioned_list:
|
||||
payload["mentioned_mobile_list"] = mentioned_list
|
||||
|
||||
resp = requests.post(
|
||||
url=self.config.wecom['webhook_url'],
|
||||
headers={'Content-Type': 'application/json'},
|
||||
data=json.dumps(payload),
|
||||
timeout=5
|
||||
)
|
||||
resp.raise_for_status()
|
||||
|
||||
logger.info("企业微信报警发送成功")
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"企业微信发送失败: {str(e)}")
|
||||
raise
|
||||
|
||||
@retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=4, max=10))
|
||||
def send_feishu(self, title: str, content: str) -> bool:
|
||||
"""
|
||||
发送飞书机器人通知
|
||||
参数:
|
||||
title: 消息标题
|
||||
content: 消息内容(支持Markdown)
|
||||
返回:
|
||||
发送是否成功
|
||||
"""
|
||||
if not self.config.feishu:
|
||||
logger.warning("飞书配置未启用")
|
||||
return False
|
||||
|
||||
try:
|
||||
payload = {
|
||||
"msg_type": "interactive",
|
||||
"card": {
|
||||
"header": {
|
||||
"title": {
|
||||
"tag": "plain_text",
|
||||
"content": title
|
||||
},
|
||||
"template": "red" # 红色标题表示报警
|
||||
},
|
||||
"elements": [{
|
||||
"tag": "markdown",
|
||||
"content": content
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
resp = requests.post(
|
||||
url=self.config.feishu['webhook_url'],
|
||||
headers={'Content-Type': 'application/json'},
|
||||
data=json.dumps(payload),
|
||||
timeout=5
|
||||
)
|
||||
resp.raise_for_status()
|
||||
|
||||
logger.info("飞书报警发送成功")
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"飞书发送失败: {str(e)}")
|
||||
raise
|
||||
|
||||
def send_alert(self, level: str, title: str, content: str) -> bool:
|
||||
"""
|
||||
分级发送报警通知
|
||||
参数:
|
||||
level: 报警级别(DEBUG/INFO/WARNING/ERROR/CRITICAL)
|
||||
title: 报警标题
|
||||
content: 报警详情
|
||||
返回:
|
||||
是否发送成功(至少一个通道成功)
|
||||
"""
|
||||
if self.levels[level] < self.levels[self.config.min_level]:
|
||||
logger.debug(f"忽略低于阈值的报警: {level} < {self.config.min_level}")
|
||||
return False
|
||||
|
||||
results = []
|
||||
if self.config.email:
|
||||
email_content = f"""
|
||||
<h2>{title}</h2>
|
||||
<p>报警级别: <strong>{level}</strong></p>
|
||||
<pre>{content}</pre>
|
||||
"""
|
||||
results.append(
|
||||
self.send_email(
|
||||
subject=f"[{level}] {title}",
|
||||
content=email_content,
|
||||
to_addrs=self.config.email['receivers']
|
||||
)
|
||||
)
|
||||
|
||||
if self.config.wecom:
|
||||
wecom_content = f"## {title}\n**级别**: {level}\n```\n{content}\n```"
|
||||
results.append(self.send_wecom(wecom_content))
|
||||
|
||||
if self.config.feishu:
|
||||
feishu_content = f"**级别**: {level}\n\n{content}"
|
||||
results.append(self.send_feishu(title, feishu_content))
|
||||
|
||||
return any(results)
|
||||
|
||||
# 配置示例
|
||||
if __name__ == "__main__":
|
||||
# 初始化配置
|
||||
config = AlertConfig(
|
||||
email={
|
||||
'smtp_server': 'smtp.example.com',
|
||||
'smtp_port': 465,
|
||||
'username': 'alert@example.com',
|
||||
'password': 'your_password',
|
||||
'from_addr': 'alert@example.com',
|
||||
'receivers': ['admin@example.com']
|
||||
},
|
||||
wecom={
|
||||
'webhook_url': 'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=your_key'
|
||||
},
|
||||
feishu={
|
||||
'webhook_url': 'https://open.feishu.cn/open-apis/bot/v2/hook/your_token'
|
||||
},
|
||||
min_level='ERROR'
|
||||
)
|
||||
|
||||
# 使用示例
|
||||
alert = AlertService(config)
|
||||
|
||||
# 发送测试报警
|
||||
alert.send_alert(
|
||||
level='ERROR',
|
||||
title='数据库连接失败',
|
||||
content='无法连接到MySQL服务器,已重试3次\n主机: 192.168.1.100:3306'
|
||||
)
|
||||
Reference in New Issue
Block a user