增加异常处理
This commit is contained in:
parent
dd7eb8d3a1
commit
5a56076858
@ -10,6 +10,7 @@ from contextlib import asynccontextmanager
|
||||
from app.config import get_settings
|
||||
from app.utils.logger import logger
|
||||
from app.api import chat, stock, skills, llm, auth, admin, paper_trading, stocks, signals
|
||||
from app.utils.error_handler import setup_global_exception_handler, init_error_notifier
|
||||
import os
|
||||
|
||||
|
||||
@ -289,6 +290,23 @@ async def lifespan(app: FastAPI):
|
||||
# 启动时执行
|
||||
logger.info("应用启动")
|
||||
|
||||
# 初始化全局异常处理器
|
||||
setup_global_exception_handler()
|
||||
logger.info("全局异常处理器已安装")
|
||||
|
||||
# 初始化飞书错误通知
|
||||
try:
|
||||
from app.services.feishu_service import get_feishu_service
|
||||
feishu_service = get_feishu_service()
|
||||
init_error_notifier(
|
||||
feishu_service=feishu_service,
|
||||
enabled=True, # 启用异常通知
|
||||
cooldown=300 # 5分钟冷却时间
|
||||
)
|
||||
logger.info("✅ 系统异常通知已启用(异常将发送到飞书)")
|
||||
except Exception as e:
|
||||
logger.warning(f"飞书异常通知初始化失败: {e}")
|
||||
|
||||
# 启动后台任务
|
||||
settings = get_settings()
|
||||
if getattr(settings, 'paper_trading_enabled', True):
|
||||
@ -438,9 +456,19 @@ async def signals_page():
|
||||
|
||||
if __name__ == "__main__":
|
||||
import uvicorn
|
||||
|
||||
# 设置全局异常处理器(防止主线程异常退出)
|
||||
setup_global_exception_handler()
|
||||
|
||||
try:
|
||||
uvicorn.run(
|
||||
"app.main:app",
|
||||
host=settings.api_host,
|
||||
port=settings.api_port,
|
||||
reload=settings.debug
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"应用启动失败: {e}")
|
||||
import traceback
|
||||
logger.error(traceback.format_exc())
|
||||
raise
|
||||
|
||||
217
backend/app/utils/error_handler.py
Normal file
217
backend/app/utils/error_handler.py
Normal file
@ -0,0 +1,217 @@
|
||||
"""
|
||||
全局异常处理器
|
||||
|
||||
捕获系统中所有未处理的异常,并发送飞书通知
|
||||
"""
|
||||
import sys
|
||||
import traceback
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
from threading import Lock
|
||||
|
||||
from app.utils.logger import logger
|
||||
|
||||
|
||||
class GlobalExceptionHandler:
|
||||
"""全局异常处理器"""
|
||||
|
||||
_instance = None
|
||||
_lock = Lock()
|
||||
_initialized = False
|
||||
|
||||
def __new__(cls):
|
||||
"""单例模式"""
|
||||
if cls._instance is None:
|
||||
with cls._lock:
|
||||
if cls._instance is None:
|
||||
cls._instance = super().__new__(cls)
|
||||
return cls._instance
|
||||
|
||||
def __init__(self):
|
||||
"""初始化异常处理器"""
|
||||
if GlobalExceptionHandler._initialized:
|
||||
return
|
||||
|
||||
GlobalExceptionHandler._initialized = True
|
||||
self.feishu_service = None
|
||||
self.enabled = True
|
||||
self.last_error_time = None
|
||||
self.error_cooldown = 300 # 错误通知冷却时间(秒),避免重复通知
|
||||
|
||||
logger.info("全局异常处理器初始化完成")
|
||||
|
||||
def set_feishu_service(self, feishu_service):
|
||||
"""设置飞书服务"""
|
||||
self.feishu_service = feishu_service
|
||||
logger.info("异常处理器已连接飞书服务")
|
||||
|
||||
def set_enabled(self, enabled: bool):
|
||||
"""启用或禁用异常通知"""
|
||||
self.enabled = enabled
|
||||
logger.info(f"异常通知已{'启用' if enabled else '禁用'}")
|
||||
|
||||
def set_cooldown(self, seconds: int):
|
||||
"""设置错误通知冷却时间"""
|
||||
self.error_cooldown = seconds
|
||||
logger.info(f"错误通知冷却时间已设置为 {seconds} 秒")
|
||||
|
||||
def handle_exception(self, exc_type, exc_value, exc_traceback):
|
||||
"""
|
||||
处理异常
|
||||
|
||||
Args:
|
||||
exc_type: 异常类型
|
||||
exc_value: 异常值
|
||||
exc_traceback: 异常堆栈
|
||||
"""
|
||||
# 检查是否是键盘中断(用户主动退出)
|
||||
if exc_type == KeyboardInterrupt:
|
||||
logger.info("用户主动中断程序")
|
||||
return
|
||||
|
||||
# 检查是否启用
|
||||
if not self.enabled:
|
||||
logger.warning("异常通知已禁用,仅记录日志")
|
||||
self._log_exception(exc_type, exc_value, exc_traceback)
|
||||
return
|
||||
|
||||
# 检查冷却时间
|
||||
if self.last_error_time:
|
||||
time_since_last = (datetime.now() - self.last_error_time).total_seconds()
|
||||
if time_since_last < self.error_cooldown:
|
||||
logger.warning(f"错误通知冷却中(剩余 {int(self.error_cooldown - time_since_last)} 秒)")
|
||||
self._log_exception(exc_type, exc_value, exc_traceback)
|
||||
return
|
||||
|
||||
# 记录异常
|
||||
self._log_exception(exc_type, exc_value, exc_traceback)
|
||||
|
||||
# 发送飞书通知
|
||||
self._send_error_notification(exc_type, exc_value, exc_traceback)
|
||||
|
||||
# 更新最后错误时间
|
||||
self.last_error_time = datetime.now()
|
||||
|
||||
def _log_exception(self, exc_type, exc_value, exc_traceback):
|
||||
"""记录异常到日志"""
|
||||
logger.error("=" * 60)
|
||||
logger.error("❌ 未捕获的异常")
|
||||
logger.error("=" * 60)
|
||||
logger.error(f"异常类型: {exc_type.__name__}")
|
||||
logger.error(f"异常信息: {str(exc_value)}")
|
||||
logger.error(f"发生时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
||||
|
||||
# 打印完整的堆栈跟踪
|
||||
tb_lines = traceback.format_exception(exc_type, exc_value, exc_traceback)
|
||||
logger.error("堆栈跟踪:\n" + "".join(tb_lines))
|
||||
logger.error("=" * 60)
|
||||
|
||||
def _send_error_notification(self, exc_type, exc_value, exc_traceback):
|
||||
"""发送错误通知到飞书"""
|
||||
if not self.feishu_service:
|
||||
logger.warning("飞书服务未设置,无法发送错误通知")
|
||||
return
|
||||
|
||||
try:
|
||||
# 获取异常信息
|
||||
exc_name = exc_type.__name__
|
||||
exc_msg = str(exc_value)
|
||||
exc_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||
|
||||
# 获取堆栈跟踪(限制长度)
|
||||
tb_list = traceback.format_exception(exc_type, exc_value, exc_traceback)
|
||||
|
||||
# 构建堆栈信息(限制长度,避免超出飞书限制)
|
||||
stack_trace = "".join(tb_list)
|
||||
|
||||
# 限制堆栈信息长度(飞书有长度限制)
|
||||
max_length = 3000
|
||||
if len(stack_trace) > max_length:
|
||||
stack_trace = stack_trace[:max_length] + "\n... (堆栈信息过长,已截断)"
|
||||
|
||||
# 格式化堆栈信息,使用代码块
|
||||
formatted_stack = "```\n" + stack_trace + "\n```"
|
||||
|
||||
# 构建飞书消息
|
||||
message = f"""🚨 **系统异常报警**
|
||||
|
||||
**异常类型**: {exc_name}
|
||||
**异常信息**: {exc_msg}
|
||||
**发生时间**: {exc_time}
|
||||
|
||||
**堆栈跟踪**:
|
||||
{formatted_stack}
|
||||
|
||||
⚠️ 请及时处理系统异常"""
|
||||
|
||||
# 发送飞书通知
|
||||
import asyncio
|
||||
try:
|
||||
# 获取当前事件循环
|
||||
loop = asyncio.get_event_loop()
|
||||
if loop.is_running():
|
||||
# 如果事件循环正在运行,使用 run_coroutine_threadsafe
|
||||
asyncio.run_coroutine_threadsafe(
|
||||
self.feishu_service.send_text(message),
|
||||
loop
|
||||
)
|
||||
else:
|
||||
# 如果事件循环未运行,直接运行
|
||||
asyncio.run(self.feishu_service.send_text(message))
|
||||
|
||||
logger.info("✅ 已发送异常通知到飞书")
|
||||
|
||||
except RuntimeError:
|
||||
# 没有事件循环,创建新的
|
||||
asyncio.run(self.feishu_service.send_text(message))
|
||||
logger.info("✅ 已发送异常通知到飞书")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"发送异常通知失败: {e}")
|
||||
|
||||
|
||||
# 创建全局异常处理器实例
|
||||
_exception_handler: Optional[GlobalExceptionHandler] = None
|
||||
|
||||
|
||||
def get_exception_handler() -> GlobalExceptionHandler:
|
||||
"""获取全局异常处理器实例"""
|
||||
global _exception_handler
|
||||
if _exception_handler is None:
|
||||
_exception_handler = GlobalExceptionHandler()
|
||||
return _exception_handler
|
||||
|
||||
|
||||
def setup_global_exception_handler():
|
||||
"""
|
||||
设置全局异常处理器
|
||||
|
||||
捕获所有未处理的异常,并发送飞书通知
|
||||
"""
|
||||
handler = get_exception_handler()
|
||||
|
||||
def handle_exception(exc_type, exc_value, exc_traceback):
|
||||
"""异常处理回调函数"""
|
||||
handler.handle_exception(exc_type, exc_value, exc_traceback)
|
||||
|
||||
# 设置全局异常钩子
|
||||
sys.excepthook = handle_exception
|
||||
|
||||
logger.info("✅ 全局异常处理器已安装")
|
||||
|
||||
|
||||
def init_error_notifier(feishu_service, enabled: bool = True, cooldown: int = 300):
|
||||
"""
|
||||
初始化错误通知器
|
||||
|
||||
Args:
|
||||
feishu_service: 飞书服务实例
|
||||
enabled: 是否启用异常通知
|
||||
cooldown: 错误通知冷却时间(秒)
|
||||
"""
|
||||
handler = get_exception_handler()
|
||||
handler.set_feishu_service(feishu_service)
|
||||
handler.set_enabled(enabled)
|
||||
handler.set_cooldown(cooldown)
|
||||
|
||||
logger.info("错误通知器初始化完成")
|
||||
210
docs/ERROR_NOTIFICATION.md
Normal file
210
docs/ERROR_NOTIFICATION.md
Normal file
@ -0,0 +1,210 @@
|
||||
# 系统异常通知功能使用说明
|
||||
|
||||
## 功能概述
|
||||
|
||||
系统异常通知功能会自动捕获所有未处理的异常,并发送通知到飞书,让你及时了解系统错误。
|
||||
|
||||
## 主要特性
|
||||
|
||||
✅ **自动捕获异常** - 捕获系统中所有未处理的异常
|
||||
✅ **飞书通知** - 自动发送异常详情到飞书
|
||||
✅ **完整堆栈信息** - 包含异常类型、错误信息和完整堆栈跟踪
|
||||
✅ **冷却机制** - 避免重复通知(默认5分钟冷却时间)
|
||||
✅ **可配置** - 可以启用/禁用通知,调整冷却时间
|
||||
|
||||
## 工作原理
|
||||
|
||||
### 1. 异常捕获流程
|
||||
|
||||
```
|
||||
系统异常 → 全局异常处理器 → 记录日志 → 检查冷却时间 → 发送飞书通知
|
||||
```
|
||||
|
||||
### 2. 通知内容
|
||||
|
||||
飞书通知包含以下信息:
|
||||
- 🚨 异常类型(如 `ValueError`, `RuntimeError`)
|
||||
- 📝 异常信息(错误描述)
|
||||
- ⏰ 发生时间
|
||||
- 📚 完整堆栈跟踪(代码格式化显示)
|
||||
|
||||
### 3. 冷却机制
|
||||
|
||||
为了避免同一错误重复通知,系统实现了冷却机制:
|
||||
- 默认冷却时间:300秒(5分钟)
|
||||
- 在冷却期内的异常只记录日志,不发送飞书通知
|
||||
- 冷却时间过后再次出现异常才会发送通知
|
||||
|
||||
## 配置说明
|
||||
|
||||
### 代码配置
|
||||
|
||||
异常通知已在 `main.py` 中自动初始化:
|
||||
|
||||
```python
|
||||
# 初始化飞书错误通知
|
||||
from app.services.feishu_service import get_feishu_service
|
||||
feishu_service = get_feishu_service()
|
||||
init_error_notifier(
|
||||
feishu_service=feishu_service,
|
||||
enabled=True, # 启用异常通知
|
||||
cooldown=300 # 5分钟冷却时间
|
||||
)
|
||||
```
|
||||
|
||||
### 动态调整配置
|
||||
|
||||
你可以在运行时动态调整异常通知配置:
|
||||
|
||||
```python
|
||||
from app.utils.error_handler import get_exception_handler
|
||||
|
||||
handler = get_exception_handler()
|
||||
|
||||
# 启用/禁用异常通知
|
||||
handler.set_enabled(True) # 启用
|
||||
handler.set_enabled(False) # 禁用
|
||||
|
||||
# 调整冷却时间(秒)
|
||||
handler.set_cooldown(300) # 5分钟
|
||||
handler.set_cooldown(60) # 1分钟
|
||||
handler.set_cooldown(0) # 禁用冷却(每次都通知)
|
||||
```
|
||||
|
||||
## 使用示例
|
||||
|
||||
### 示例1: 手动触发异常通知
|
||||
|
||||
```python
|
||||
from app.utils.error_handler import get_exception_handler
|
||||
|
||||
handler = get_exception_handler()
|
||||
|
||||
# 手动报告异常
|
||||
try:
|
||||
# 你的代码
|
||||
result = 1 / 0
|
||||
except Exception as e:
|
||||
import sys
|
||||
exc_type, exc_value, exc_traceback = sys.exc_info()
|
||||
handler.handle_exception(exc_type, exc_value, exc_traceback)
|
||||
```
|
||||
|
||||
### 示例2: 在函数中装饰器使用
|
||||
|
||||
```python
|
||||
from functools import wraps
|
||||
from app.utils.error_handler import get_exception_handler
|
||||
|
||||
def notify_on_error(func):
|
||||
"""异常时发送通知的装饰器"""
|
||||
@wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
try:
|
||||
return func(*args, **kwargs)
|
||||
except Exception as e:
|
||||
handler = get_exception_handler()
|
||||
import sys
|
||||
exc_type, exc_value, exc_traceback = sys.exc_info()
|
||||
handler.handle_exception(exc_type, exc_value, exc_traceback)
|
||||
raise
|
||||
return wrapper
|
||||
|
||||
@notify_on_error
|
||||
def risky_function():
|
||||
# 可能出错的代码
|
||||
pass
|
||||
```
|
||||
|
||||
## 测试
|
||||
|
||||
运行测试脚本验证功能:
|
||||
|
||||
```bash
|
||||
source backend/venv/bin/activate
|
||||
python scripts/test_error_notification.py
|
||||
```
|
||||
|
||||
测试脚本会:
|
||||
1. 触发各种类型的异常
|
||||
2. 发送飞书通知
|
||||
3. 测试冷却机制
|
||||
4. 验证通知格式
|
||||
|
||||
## 飞书通知示例
|
||||
|
||||
```
|
||||
🚨 **系统异常报警**
|
||||
|
||||
**异常类型**: ZeroDivisionError
|
||||
**异常信息**: division by zero
|
||||
**发生时间**: 2026-02-22 11:50:59
|
||||
|
||||
**堆栈跟踪**:
|
||||
```
|
||||
Traceback (most recent call last):
|
||||
File "/app/main.py", line 100, in process_data
|
||||
result = calculate_ratio(x, y)
|
||||
File "/app/utils.py", line 50, in calculate_ratio
|
||||
return x / y
|
||||
ZeroDivisionError: division by zero
|
||||
```
|
||||
|
||||
⚠️ 请及时处理系统异常
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **敏感信息**: 异常堆栈可能包含敏感信息(如API密钥、密码等),请注意飞书安全性
|
||||
2. **网络依赖**: 发送飞书通知需要网络连接,如果网络异常会只记录日志
|
||||
3. **性能影响**: 异常处理本身对性能影响很小,但频繁异常可能表示系统有问题
|
||||
4. **日志级别**: 异常信息会记录到 ERROR 级别日志,可通过日志系统查询
|
||||
|
||||
## 故障排查
|
||||
|
||||
### 问题1: 没有收到飞书通知
|
||||
|
||||
检查项:
|
||||
- ✅ 飞书 webhook URL 是否正确配置(`.env` 文件中的 `FEISHU_WEBHOOK_URL`)
|
||||
- ✅ `FEISHU_ENABLED=true` 是否设置
|
||||
- ✅ 网络连接是否正常
|
||||
- ✅ 是否在冷却期内
|
||||
- ✅ 查看日志中是否有 "飞书消息发送成功" 或相关错误信息
|
||||
|
||||
### 问题2: 通知太频繁
|
||||
|
||||
解决方案:
|
||||
- 增加冷却时间:`handler.set_cooldown(600)` # 10分钟
|
||||
- 或者临时禁用通知:`handler.set_enabled(False)`
|
||||
|
||||
### 问题3: 想临时禁用通知
|
||||
|
||||
```python
|
||||
from app.utils.error_handler import get_exception_handler
|
||||
handler = get_exception_handler()
|
||||
handler.set_enabled(False)
|
||||
```
|
||||
|
||||
## 技术实现
|
||||
|
||||
异常处理器使用 Python 的 `sys.excepthook` 机制,可以捕获所有未处理的异常:
|
||||
|
||||
```python
|
||||
import sys
|
||||
|
||||
def exception_hook(exc_type, exc_value, exc_traceback):
|
||||
# 处理异常
|
||||
handler.handle_exception(exc_type, exc_value, exc_traceback)
|
||||
|
||||
sys.excepthook = exception_hook
|
||||
```
|
||||
|
||||
这种方式可以捕获:
|
||||
- 主线程中的所有未处理异常
|
||||
- 异步任务中的异常(如果未正确捕获)
|
||||
- 脚本运行时的异常
|
||||
|
||||
但不能捕获:
|
||||
- 已被 try-except 捕获的异常(这是设计行为)
|
||||
- 子线程中的异常(需要在子线程中单独处理)
|
||||
- 系统级别的严重错误(如 Segmentation Fault)
|
||||
218
scripts/test_error_notification.py
Executable file
218
scripts/test_error_notification.py
Executable file
@ -0,0 +1,218 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
测试系统异常通知功能
|
||||
|
||||
测试场景:
|
||||
1. 测试普通异常通知
|
||||
2. 测试带堆栈信息的异常通知
|
||||
3. 测试冷却时间(避免重复通知)
|
||||
"""
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# 添加项目路径
|
||||
backend_dir = Path(__file__).parent.parent / "backend"
|
||||
sys.path.insert(0, str(backend_dir))
|
||||
|
||||
|
||||
def test_normal_exception():
|
||||
"""测试普通异常通知"""
|
||||
print("\n" + "=" * 60)
|
||||
print("🧪 测试1: 普通异常通知")
|
||||
print("=" * 60)
|
||||
|
||||
from app.utils.error_handler import get_exception_handler
|
||||
|
||||
handler = get_exception_handler()
|
||||
|
||||
# 模拟一个异常
|
||||
try:
|
||||
result = 1 / 0 # ZeroDivisionError
|
||||
except Exception as e:
|
||||
import traceback
|
||||
exc_type, exc_value, exc_traceback = sys.exc_info()
|
||||
handler.handle_exception(exc_type, exc_value, exc_traceback)
|
||||
print("✅ 已触发异常处理(请检查飞书是否收到通知)")
|
||||
|
||||
|
||||
def test_custom_exception():
|
||||
"""测试自定义异常通知"""
|
||||
print("\n" + "=" * 60)
|
||||
print("🧪 测试2: 自定义异常通知")
|
||||
print("=" * 60)
|
||||
|
||||
from app.utils.error_handler import get_exception_handler
|
||||
|
||||
handler = get_exception_handler()
|
||||
|
||||
# 模拟一个自定义异常
|
||||
try:
|
||||
raise ValueError("这是一个测试异常,用于验证飞书通知功能")
|
||||
except Exception as e:
|
||||
import traceback
|
||||
exc_type, exc_value, exc_traceback = sys.exc_info()
|
||||
handler.handle_exception(exc_type, exc_value, exc_traceback)
|
||||
print("✅ 已触发自定义异常处理(请检查飞书是否收到通知)")
|
||||
|
||||
|
||||
def test_nested_exception():
|
||||
"""测试嵌套调用异常"""
|
||||
print("\n" + "=" * 60)
|
||||
print("🧪 测试3: 嵌套调用异常(带完整堆栈)")
|
||||
print("=" * 60)
|
||||
|
||||
from app.utils.error_handler import get_exception_handler
|
||||
|
||||
handler = get_exception_handler()
|
||||
|
||||
def level_3():
|
||||
"""第三层调用"""
|
||||
raise RuntimeError("深层错误:数据库连接失败")
|
||||
|
||||
def level_2():
|
||||
"""第二层调用"""
|
||||
level_3()
|
||||
|
||||
def level_1():
|
||||
"""第一层调用"""
|
||||
level_2()
|
||||
|
||||
# 模拟嵌套调用异常
|
||||
try:
|
||||
level_1()
|
||||
except Exception as e:
|
||||
import traceback
|
||||
exc_type, exc_value, exc_traceback = sys.exc_info()
|
||||
handler.handle_exception(exc_type, exc_value, exc_traceback)
|
||||
print("✅ 已触发嵌套异常处理(请检查飞书是否收到完整堆栈信息)")
|
||||
|
||||
|
||||
def test_cooldown():
|
||||
"""测试冷却时间"""
|
||||
print("\n" + "=" * 60)
|
||||
print("🧪 测试4: 冷却时间(连续触发异常)")
|
||||
print("=" * 60)
|
||||
|
||||
from app.utils.error_handler import get_exception_handler
|
||||
import time
|
||||
|
||||
handler = get_exception_handler()
|
||||
handler.set_cooldown(10) # 设置10秒冷却时间
|
||||
|
||||
print(f"⏰ 冷却时间设置为 10 秒")
|
||||
|
||||
# 第一次触发
|
||||
try:
|
||||
raise RuntimeError("第一次异常")
|
||||
except Exception:
|
||||
exc_type, exc_value, exc_traceback = sys.exc_info()
|
||||
handler.handle_exception(exc_type, exc_value, exc_traceback)
|
||||
print("✅ 第一次异常已处理")
|
||||
|
||||
# 立即第二次触发(应该在冷却期内)
|
||||
print("\n⏳ 立即触发第二次异常(应该在冷却期内)...")
|
||||
try:
|
||||
raise RuntimeError("第二次异常")
|
||||
except Exception:
|
||||
exc_type, exc_value, exc_traceback = sys.exc_info()
|
||||
handler.handle_exception(exc_type, exc_value, exc_traceback)
|
||||
print("✅ 第二次异常已处理(应该被冷却,不发送飞书通知)")
|
||||
|
||||
print("\n⏳ 等待 11 秒后再次触发...")
|
||||
time.sleep(11)
|
||||
|
||||
# 第三次触发(冷却期已过)
|
||||
try:
|
||||
raise RuntimeError("第三次异常")
|
||||
except Exception:
|
||||
exc_type, exc_value, exc_traceback = sys.exc_info()
|
||||
handler.handle_exception(exc_type, exc_value, exc_traceback)
|
||||
print("✅ 第三次异常已处理(冷却期已过,应该发送飞书通知)")
|
||||
|
||||
|
||||
def test_with_feishu_integration():
|
||||
"""测试与飞书集成的完整流程"""
|
||||
print("\n" + "=" * 60)
|
||||
print("🧪 测试5: 完整集成测试(带飞书服务)")
|
||||
print("=" * 60)
|
||||
|
||||
from app.utils.error_handler import setup_global_exception_handler, get_exception_handler
|
||||
from app.services.feishu_service import get_feishu_service
|
||||
|
||||
# 设置全局异常处理器
|
||||
setup_global_exception_handler()
|
||||
print("✅ 全局异常处理器已安装")
|
||||
|
||||
# 初始化飞书通知
|
||||
feishu_service = get_feishu_service()
|
||||
handler = get_exception_handler()
|
||||
handler.set_feishu_service(feishu_service)
|
||||
handler.set_enabled(True)
|
||||
print("✅ 飞书通知已启用")
|
||||
|
||||
# 触发一个测试异常
|
||||
print("\n🔔 触发测试异常...")
|
||||
try:
|
||||
raise Exception("【测试】这是一个集成测试异常,用于验证飞书通知功能是否正常工作")
|
||||
except Exception:
|
||||
exc_type, exc_value, exc_traceback = sys.exc_info()
|
||||
handler.handle_exception(exc_type, exc_value, exc_traceback)
|
||||
print("✅ 测试异常已处理,请检查飞书是否收到通知")
|
||||
|
||||
|
||||
def main():
|
||||
"""主测试函数"""
|
||||
print("\n" + "🚀" * 30)
|
||||
print("系统异常通知功能测试")
|
||||
print("🚀" * 30)
|
||||
|
||||
print("\n⚠️ 注意:以下测试会触发真实的异常,并尝试发送飞书通知")
|
||||
print("⚠️ 请确保飞书 webhook 已正确配置\n")
|
||||
|
||||
try:
|
||||
# 测试1: 普通异常
|
||||
test_normal_exception()
|
||||
|
||||
# 等待用户确认
|
||||
input("\n按 Enter 键继续下一个测试...")
|
||||
|
||||
# 测试2: 自定义异常
|
||||
test_custom_exception()
|
||||
|
||||
# 等待用户确认
|
||||
input("\n按 Enter 键继续下一个测试...")
|
||||
|
||||
# 测试3: 嵌套异常
|
||||
test_nested_exception()
|
||||
|
||||
# 等待用户确认
|
||||
input("\n按 Enter 键继续下一个测试...")
|
||||
|
||||
# 测试4: 冷却时间
|
||||
test_cooldown()
|
||||
|
||||
# 等待用户确认
|
||||
input("\n按 Enter 键继续最后一个测试...")
|
||||
|
||||
# 测试5: 完整集成
|
||||
test_with_feishu_integration()
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("✅ 所有测试完成!")
|
||||
print("=" * 60)
|
||||
print("\n📋 请检查飞书聊天窗口,确认是否收到异常通知")
|
||||
print("💡 提示:如果未收到通知,请检查:")
|
||||
print(" 1. 飞书 webhook URL 是否正确")
|
||||
print(" 2. 网络连接是否正常")
|
||||
print(" 3. .env 文件中的 FEISHU_ENABLED 是否为 true\n")
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print("\n\n⚠️ 测试被用户中断")
|
||||
except Exception as e:
|
||||
print(f"\n\n❌ 测试失败: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
Reference in New Issue
Block a user