Phase 19 UX 監控 - 善用 Sentry Session Replay: - SentryService: 新增 list_replays, get_ux_audit_summary - 偵測: 憤怒點擊 (Rage Clicks) + 死亡點擊 (Dead Clicks) - 偵測: 有錯誤的 Session Replay - 偵測: UI 相關錯誤 (TypeError/render) - API: GET /api/v1/errors/ux-audit 端點 - 腳本: audit_ux_sentry.py CLI 工具 統帥回饋: "AI都要全自動化!" ✅ Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
123 lines
3.6 KiB
Python
123 lines
3.6 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
Sentry UX Audit Script
|
||
======================
|
||
使用 Sentry Session Replay + Error Data 自動審計 UI/UX 問題
|
||
|
||
執行方式:
|
||
cd apps/api
|
||
python scripts/audit_ux_sentry.py
|
||
|
||
輸出:
|
||
- 有錯誤的 Session Replay
|
||
- 憤怒點擊 (Rage Clicks) 統計
|
||
- 死亡點擊 (Dead Clicks) 統計
|
||
- UI 相關錯誤列表
|
||
|
||
版本: v1.0
|
||
建立: 2026-03-29 (台北時區)
|
||
建立者: Claude Code (Phase 19 UX 監控)
|
||
"""
|
||
|
||
import asyncio
|
||
import json
|
||
import sys
|
||
from pathlib import Path
|
||
|
||
# 添加專案路徑
|
||
sys.path.insert(0, str(Path(__file__).parent.parent))
|
||
|
||
from src.services.sentry_service import SentryService
|
||
|
||
|
||
async def main():
|
||
"""執行 UX 審計"""
|
||
print("=" * 60)
|
||
print("AWOOOI UX Audit via Sentry Session Replay")
|
||
print("=" * 60)
|
||
print()
|
||
|
||
service = SentryService()
|
||
|
||
# 檢查連線
|
||
projects = await service.list_projects()
|
||
if not projects:
|
||
print("❌ 無法連線 Sentry API,請檢查:")
|
||
print(" - SENTRY_SELF_HOSTED_URL")
|
||
print(" - SENTRY_AUTH_TOKEN")
|
||
print(" - SENTRY_ORG")
|
||
return
|
||
|
||
print(f"✅ Sentry 連線成功,找到 {len(projects)} 個專案")
|
||
print()
|
||
|
||
# 取得 UX 審計摘要
|
||
print("📊 執行 UX 審計...")
|
||
print("-" * 60)
|
||
summary = await service.get_ux_audit_summary()
|
||
|
||
# 輸出摘要
|
||
print()
|
||
print("🔍 UX 問題摘要:")
|
||
print(f" 有錯誤的 Replay 數: {summary['replays_with_errors']}")
|
||
print(f" 憤怒點擊 (Rage Clicks): {summary['rage_clicks']}")
|
||
print(f" 死亡點擊 (Dead Clicks): {summary['dead_clicks']}")
|
||
print(f" UI 錯誤數: {summary['ui_errors']}")
|
||
print()
|
||
|
||
# 輸出詳情
|
||
if summary["details"]:
|
||
print("📝 問題詳情:")
|
||
print("-" * 60)
|
||
for i, detail in enumerate(summary["details"][:15], 1): # 最多 15 筆
|
||
detail_type = detail.get("type", "unknown")
|
||
|
||
if detail_type == "replay_with_errors":
|
||
print(f"{i}. 🎥 Replay 有 {detail.get('error_count', 0)} 個錯誤")
|
||
print(f" URL: {detail.get('url', 'N/A')}")
|
||
urls = detail.get("urls", [])
|
||
if urls:
|
||
print(f" 訪問頁面: {', '.join(urls[:2])}")
|
||
print()
|
||
|
||
elif detail_type == "ui_error":
|
||
print(f"{i}. 🐛 UI Error: {detail.get('title', 'N/A')[:60]}...")
|
||
print(f" 發生次數: {detail.get('count', 0)}")
|
||
print(f" URL: {detail.get('url', 'N/A')}")
|
||
print()
|
||
else:
|
||
print("✨ 太棒了!目前沒有檢測到明顯的 UX 問題")
|
||
print()
|
||
|
||
# 總結與建議
|
||
print("=" * 60)
|
||
total_issues = (
|
||
summary["replays_with_errors"] +
|
||
summary["ui_errors"]
|
||
)
|
||
|
||
if total_issues > 10:
|
||
print("🔴 UX 健康度: 差 - 需要立即處理")
|
||
print(" 建議: 優先修復 UI Error,然後檢視 Rage Click Replay")
|
||
elif total_issues > 3:
|
||
print("🟡 UX 健康度: 普通 - 有改善空間")
|
||
print(" 建議: 逐一檢視問題 Replay 找出根因")
|
||
else:
|
||
print("🟢 UX 健康度: 良好")
|
||
print(" 建議: 持續監控,設置 Alert 自動通知")
|
||
|
||
print()
|
||
print("💡 查看完整 Session Replay:")
|
||
print(f" {service.base_url}/organizations/{service.org}/replays/")
|
||
print()
|
||
|
||
# 輸出 JSON (供 CI 使用)
|
||
json_output = Path(__file__).parent / "ux_audit_result.json"
|
||
with open(json_output, "w") as f:
|
||
json.dump(summary, f, indent=2, ensure_ascii=False)
|
||
print(f"📄 JSON 結果已保存: {json_output}")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
asyncio.run(main())
|