Files
ai_wht_wechat/backend/error_screenshot.py
2026-01-07 22:55:12 +08:00

194 lines
6.5 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
错误截图保存工具
当发生错误时自动截图并保存,便于问题排查
"""
import os
import sys
from datetime import datetime
from pathlib import Path
from typing import Optional
from playwright.async_api import Page
# 截图保存目录
SCREENSHOT_DIR = Path("error_screenshots")
SCREENSHOT_DIR.mkdir(exist_ok=True)
async def save_error_screenshot(
page: Optional[Page],
error_type: str,
error_message: str = "",
prefix: str = ""
) -> Optional[str]:
"""
保存错误截图
Args:
page: Playwright 页面对象
error_type: 错误类型login_failed, send_code_failed, publish_failed等
error_message: 错误信息(可选,会添加到日志)
prefix: 文件名前缀(可选)
Returns:
截图文件路径失败返回None
"""
if not page:
print("[错误截图] 页面对象为空,无法截图", file=sys.stderr)
return None
try:
# 检查页面状态
try:
current_url = page.url
print(f"[错误截图] 当前URL: {current_url}", file=sys.stderr)
# 检查是否是空白页
if current_url in ['about:blank', '', 'data:,']:
print(f"[错误截图] 警告: 当前页面为空白页,截图可能没有内容", file=sys.stderr)
# 等待页面稳定
await page.wait_for_load_state('domcontentloaded', timeout=3000)
except Exception as state_error:
print(f"[错误截图] 检查页面状态失败: {str(state_error)}", file=sys.stderr)
# 生成文件名年月日时分秒_错误类型.png
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
# 清理错误类型字符串(移除特殊字符)
safe_error_type = "".join(c for c in error_type if c.isalnum() or c in ('_', '-'))
# 组合文件名
if prefix:
filename = f"{prefix}_{timestamp}_{safe_error_type}.png"
else:
filename = f"{timestamp}_{safe_error_type}.png"
filepath = SCREENSHOT_DIR / filename
# 截图(添加超时和全页截图)
await page.screenshot(
path=str(filepath),
full_page=True,
timeout=10000 # 10秒超时
)
# 检查截图文件大小
file_size = filepath.stat().st_size
print(f"[错误截图] 已保存: {filepath} (大小: {file_size} bytes)", file=sys.stderr)
# 如果文件太小小于5KB可能是空白截图
if file_size < 5120:
print(f"[错误截图] 警告: 截图文件过小 ({file_size} bytes),可能为空白页面", file=sys.stderr)
if error_message:
print(f"[错误截图] 错误信息: {error_message}", file=sys.stderr)
# 返回文件路径
return str(filepath)
except Exception as e:
print(f"[错误截图] 截图失败: {str(e)}", file=sys.stderr)
return None
def cleanup_old_screenshots(days: int = 7):
"""
清理旧的错误截图
Args:
days: 保留最近几天的截图默认7天
"""
try:
import time
current_time = time.time()
cutoff_time = current_time - (days * 24 * 60 * 60)
deleted_count = 0
for file in SCREENSHOT_DIR.glob("*.png"):
if file.stat().st_mtime < cutoff_time:
file.unlink()
deleted_count += 1
if deleted_count > 0:
print(f"[错误截图] 已清理 {deleted_count} 个超过 {days} 天的旧截图", file=sys.stderr)
except Exception as e:
print(f"[错误截图] 清理旧截图失败: {str(e)}", file=sys.stderr)
async def save_screenshot_with_html(
page: Optional[Page],
error_type: str,
error_message: str = "",
prefix: str = ""
) -> tuple[Optional[str], Optional[str]]:
"""
保存错误截图和HTML源码用于深度调试
Args:
page: Playwright 页面对象
error_type: 错误类型
error_message: 错误信息(可选)
prefix: 文件名前缀(可选)
Returns:
(截图路径, HTML路径),失败返回(None, None)
"""
if not page:
return None, None
try:
# 检查页面状态
try:
current_url = page.url
print(f"[错误截图] 当前URL: {current_url}", file=sys.stderr)
if current_url in ['about:blank', '', 'data:,']:
print(f"[错误截图] 警告: 当前页面为空白页", file=sys.stderr)
# 等待页面稳定
await page.wait_for_load_state('domcontentloaded', timeout=3000)
except Exception as state_error:
print(f"[错误截图] 检查页面状态失败: {str(state_error)}", file=sys.stderr)
# 生成文件名
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
safe_error_type = "".join(c for c in error_type if c.isalnum() or c in ('_', '-'))
if prefix:
base_filename = f"{prefix}_{timestamp}_{safe_error_type}"
else:
base_filename = f"{timestamp}_{safe_error_type}"
# 保存截图
screenshot_path = SCREENSHOT_DIR / f"{base_filename}.png"
await page.screenshot(
path=str(screenshot_path),
full_page=True,
timeout=10000
)
# 检查截图文件大小
screenshot_size = screenshot_path.stat().st_size
if screenshot_size < 5120:
print(f"[错误截图] 警告: 截图文件过小 ({screenshot_size} bytes)", file=sys.stderr)
# 保存HTML
html_path = SCREENSHOT_DIR / f"{base_filename}.html"
html_content = await page.content()
with open(html_path, 'w', encoding='utf-8') as f:
f.write(html_content)
html_size = html_path.stat().st_size
print(f"[错误截图] 已保存截图: {screenshot_path} ({screenshot_size} bytes)", file=sys.stderr)
print(f"[错误截图] 已保存HTML: {html_path} ({html_size} bytes)", file=sys.stderr)
if error_message:
print(f"[错误截图] 错误信息: {error_message}", file=sys.stderr)
return str(screenshot_path), str(html_path)
except Exception as e:
print(f"[错误截图] 保存截图和HTML失败: {str(e)}", file=sys.stderr)
return None, None