# mini-sentry **Repository Path**: zh423328/mini-sentry ## Basic Information - **Project Name**: mini-sentry - **Description**: mini-sentry - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-09-18 - **Last Updated**: 2026-09-30 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Mini Sentry V2 轻量级、生产可用的前端异常监控平台(Redis 增强版)。 产品范围严格限定为前端技术异常反馈:采集、聚合、SourceMap 定位、告警、解决与回归提醒;不提供业务埋点、用户行为分析、会话回放、APM 或日志平台。SDK 默认不采集表单、DOM 文本、请求/响应正文、Cookie 和 URL 查询参数,详见 [SDK 隐私与采集边界](docs/SDK_PRIVACY.md)。 支持 **React 18/19 / Vue 2 / Vue 3 / uni-app / Browser**(JS + TS,Vite/Webpack 均可接入),提供 Runtime Error、Promise Error、框架错误、Network Error 采集,面包屑、用户/设备信息、Release、Issue 聚合(Fingerprint)、SourceMap 源码映射、采样与防洪、数据保留策略与 Dashboard。 > 完整设计见 [docs/DESIGN_V2_COMPACT.md](docs/DESIGN_V2_COMPACT.md)(含 DDL、API 契约、SDK 协议、验收清单)。 > SDK 接入与 npm 发布见 [docs/SDK_USAGE.md](docs/SDK_USAGE.md)。 > 后续优化建议见 [docs/OPTIMIZATION_SUGGESTIONS.md](docs/OPTIMIZATION_SUGGESTIONS.md)(P0–P3 优先级与 Sprint 切片)。 > 生产运维见 [docs/OPERATIONS.md](docs/OPERATIONS.md),SLO 与容量验收见 [docs/SLO.md](docs/SLO.md)。 > V2.4–V2.6 产品与技术方向见 [docs/ROADMAP_V2_4_V2_6.md](docs/ROADMAP_V2_4_V2_6.md)。 > V3.0 异步 Ingest 规划与进入门槛见 [docs/ROADMAP_V3.md](docs/ROADMAP_V3.md)。 ## 架构 ```text SDK (Vue2/Vue3/uni-app/Browser) ← 第一层防洪:sampling / dedupe / maxEventsPerMinute ↓ HTTPS Nginx ← 第二层防洪:IP 限流 / body 大小限制 / TLS ↓ FastAPI ×N ← Ingest + Management API ├─ Redis 限流 / 去重 / 缓存 / 锁 / 实时计数(全部临时 + TTL) ├─ PostgreSQL Project / Release / Issue / Event(唯一事实来源) └─ Filesystem SourceMap(/data/minisentry/sourcemaps) ``` **核心设计原则:Redis 是加速器和保护层,不是数据库。** Redis 挂掉时系统自动降级(限流交给 Nginx、去重交给 DB 唯一约束、缓存直查 PostgreSQL),Ingest 永不中断。 ## 目录结构 ```text backend/ # FastAPI 后端(Python 3.12 + SQLAlchemy 2 Async + asyncpg) README.md # 后端运行 / 迁移 / 测试说明(uv 管理依赖) pyproject.toml # 依赖声明 uv.lock # 锁定版本 app/api/ # ingest / issues / sourcemaps / dashboard / auth / health app/core/ # config / database / redis / security / logging / middleware app/services/ # ingest / rate_limit / dedup / cache / lock / fingerprint / sanitize / sourcemap app/models/ # SQLAlchemy 模型 app/repositories/ # 数据访问(含 Cache Aside) migrations/ # Alembic 迁移 tests/ # 单测 + Redis 故障注入测试 packages/ # SDK(TypeScript) minisentry-core/ # 零依赖核心:捕获引擎 + 防洪 + 脱敏 minisentry-browser/ # Browser:error/promise/fetch/XHR 捕获 minisentry-vue2/ # Vue 2 errorHandler 适配 minisentry-vue3/ # Vue 3 errorHandler 适配 minisentry-uniapp/ # uni-app 适配(uni.request 上报) minisentry-sourcemap/ # SourceMap 自动上传(Vite 插件 / CLI) dashboard/ # 管理前端(Vue 3 + Ant Design Vue + Pinia + ECharts) deploy/ # docker-compose.yml / docker-compose.dev.yml / nginx.conf / .env.example ``` ## 快速开始(开发环境) ### 1. 启动依赖 ```bash cd deploy docker compose -f docker-compose.dev.yml up -d # 启动 PostgreSQL 16 + Redis 7 ``` ### 2. 启动后端 ```bash cd backend uv sync # 安装依赖(需已安装 uv) # 无 uv 时:python3.12 -m venv .venv && source .venv/bin/activate && pip install -r requirements.txt uv run alembic upgrade head # 建表 python run.py --local # 读 .env / .env.development(HOST/PORT/RELOAD) ``` 更完整的后端说明(环境变量、迁移增删、Docker、FAQ)见 **[backend/README.md](backend/README.md)**。 ### 3. 启动 Dashboard(可选) ```bash # 推荐:仓库根目录 workspace npm install npm run dev:dashboard # http://localhost:8080,/api 代理到 8000 # 或仅装 dashboard cd dashboard && npm install && npm run dev ``` Dashboard 构建时可通过 `VITE_API_PUBLIC_URL` 指定公网 API 根地址(与前端不同域时用于拼装 DSN)。 `vite.config.ts` 的 `base`(默认 `/minisentry/`)会同时作用于静态资源与 axios 请求前缀;仓库自带的 `deploy/nginx.conf` / `nginx.ha.conf` 已内置 `/minisentry/` 子路径的静态资源映射与 `/minisentry/api/`(含 ingest)去前缀反代,开箱即用;无前缀 `/api/` 形态同时保留,供 `VITE_API_PUBLIC_URL` 独立域名部署使用。 ### 4. 验证 ```bash curl http://localhost:8000/health # {"status":"ok"} curl http://localhost:8000/ready # {"status":"ready","database":"ok","redis":"ok"} ``` ### 5. 运行测试 ```bash cd backend uv run pytest tests -q ``` ## 接入 SDK > 完整接入、配置项、FAQ 与 **npm 发布** 说明见 **[docs/SDK_USAGE.md](docs/SDK_USAGE.md)**。 > 本地 Vue 3 联调 Demo:[`demo/vue3`](demo/vue3)(`npm install && npm run dev`)。 DSN 格式:`http:///api//ingest/` ### 安装(npm) ```bash npm install @zayn919/minisentry-browser # 原生 JS / TS npm install @zayn919/minisentry-vue3 # Vue 3 npm install @zayn919/minisentry-vue2 # Vue 2 npm install @zayn919/minisentry-uniapp # uni-app ``` ### 维护者发布到 npm ```bash npm login npm run build:packages npm run publish:packages:dry # 预检 npm run publish:packages # 正式发布(core → browser → 其余) ``` ### Browser(原生 JS / TS) ```ts import { init } from '@zayn919/minisentry-browser'; init({ dsn: 'http://localhost:8000/api//ingest/', release: 'web@1.2.0', environment: 'production', sampleRate: 1, // fatal 永不采样 dedupeWindow: 2000, // 客户端相同错误去重窗口 ms maxEventsPerMinute: 30, // 客户端防洪 }); ``` ### Vue 3 ```ts import { createApp } from 'vue'; import { init, vue3Install } from '@zayn919/minisentry-vue3'; init({ dsn: '...', release: 'web@1.2.0' }); const app = createApp(App); vue3Install(app); // 捕获 Vue errorHandler app.mount('#app'); ``` ### Vue 2 ```ts import Vue from 'vue'; import MiniSentryVue2 from '@zayn919/minisentry-vue2'; Vue.use(MiniSentryVue2, { dsn: '...', release: 'app@2.0.1' }); ``` ### uni-app ```ts // main.ts import { init, getClient } from '@zayn919/minisentry-uniapp'; init({ dsn: '...' }); // App.vue onError 中:getClient()?.onError(err) ``` ## 创建 Project 并上报 通过超级管理员账号登录 Dashboard 或获取 JWT 管理令牌(见下方"账号体系"): ```bash # 登录(返回 access_token 与用户信息) curl -X POST http://localhost:8000/api/auth/login \ -H 'Content-Type: application/json' \ -d '{"username":"admin","password":""}' # 上报一个测试事件(project_id=1) curl -X POST 'http://localhost:8000/api//ingest/' \ -H 'Content-Type: application/json' \ -d '{ "event_id": "9b2f5c1a-0000-4000-8000-000000000001", "type": "error", "level": "error", "message": "TypeError: Cannot read properties of undefined", "release": "web@1.2.0", "stack": {"frames": [{"filename": "app.js", "lineno": 125, "colno": 10}]}, "user": {"id": "u123"} }' ``` 查询 Issue 与统计(需 JWT): ```bash curl -H "Authorization: Bearer " \ 'http://localhost:8000/api/projects/1/issues?status=unresolved&page=1' # 返回 { "items": [...], "total": N } curl -H "Authorization: Bearer " \ 'http://localhost:8000/api/projects/1/dashboard' ``` ## SourceMap 上传与堆栈还原 第三方业务项目请用 npm 包 **构建时自动上传**(推荐): ```bash npm install -D @zayn919/minisentry-sourcemap ``` ```ts // vite.config.ts import { minisentrySourcemap } from '@zayn919/minisentry-sourcemap/vite' plugins: [minisentrySourcemap()] // MINISENTRY_URL / PROJECT / RELEASE / AUTH_TOKEN ``` 详见 [docs/SDK_USAGE.md §8](docs/SDK_USAGE.md) 与 [packages/minisentry-sourcemap/README.md](packages/minisentry-sourcemap/README.md)。 **业务站点部署不要公开 `.map`**:构建生成 map → 上传 Mini Sentry → `rsync` 同步静态资源时排除 map: ```bash rsync -avz --delete --exclude '*.map' --exclude '**/*.map' ./dist/ user@server:/var/www/app/ ``` 运维 / 无 Node 场景仍可用: ### CI 脚本(Python) ```bash cd backend uv run python scripts/upload_sourcemaps.py \ --base-url http://localhost:8000 \ --user admin --password admin \ --project 1 --release web@1.2.0 \ --dir ../your-app/dist ``` ### Dashboard / API 应用列表 → SourceMap → **多选** `.map` 或 zip;或: ```bash curl -X POST http://localhost:8000/api/projects/1/sourcemaps \ -H "Authorization: Bearer " \ -F 'release=web@1.2.0' \ -F 'files=@dist/app.abc123.js.map' \ -F 'files=@dist/vendor.def456.js.map' ``` `app.js.map` 会自动归一成 `app.js`。Issue 详情页打开时批量还原堆栈。 SourceMap 为私有数据:本体存文件系统(`SOURCEMAP_STORAGE`),公网 Nginx 直接拒绝访问存储目录。 ## 生产部署 完整步骤、高可用拓扑与**性能调优**见 **[docs/DEPLOYMENT.md](docs/DEPLOYMENT.md)**。摘要: ```bash cd deploy cp .env.example .env # 修改全部密码/密钥、PUBLIC_URL、CORS docker compose up -d --build # nginx + api + postgres + redis ``` `migrate` 一次性服务会在 API 启动前自动执行 `alembic upgrade head`;迁移失败时 API 不会启动。 要点: - 首次部署会构建 Dashboard(`dashboard-build` 一次性任务)并挂入 Nginx;需配置强密码 `.env` - PostgreSQL / Redis 仅在内网,公网不可访问(5432/6379 未映射宿主机端口) - Nginx:Ingest 与 Management 分区分流限流;支持 `X-Forwarded-Proto` / `X-Request-ID`;TLS 示例见 `nginx.conf` 注释;API 仅信任 `TRUSTED_PROXIES` 内对端的 `X-Forwarded-For`(防伪造绕过限流) - api / nginx 含 healthcheck 与 `restart: unless-stopped` - Redis 配置:`maxmemory 256mb` + `allkeys-lru`,无持久化(数据可重建) - **追最高性能**:用 `docker-compose.ha.yml`(双 API + 外部 PG/Redis + 共享 SourceMap),并按 [DEPLOYMENT.md §6](docs/DEPLOYMENT.md) 核算连接池与压测 - 多实例:使用 `nginx.ha.conf` 的双 API upstream;后台任务用 PostgreSQL advisory lock 防惊群 - 备份:只需备份 PostgreSQL 与 SourceMap 目录;Redis 数据丢失会自动重建 - 备份恢复:`deploy/scripts/backup.sh` 与 `restore.sh`;默认目标 RPO 24 小时、RTO 4 小时 - Dashboard 公网 API 地址可通过构建时 `VITE_API_PUBLIC_URL`(compose 由 `PUBLIC_URL` 注入)指定 ## 关键机制说明 ### 三层防洪 | 层 | 机制 | | ----- | ----------------------------------------------------- | | SDK | sampleRate(fatal 不采样)、dedupeWindow、maxEventsPerMinute | | Nginx | IP 限流、body 大小限制 | | Redis | Project 限流(默认 1000 events/min)、Fingerprint 5 秒去重 | ### 发生/采集/丢弃 三值计数 异常风暴时被去重丢弃的事件不会丢失统计:Issue 上同时维护 - `observed_count` —— 真实发生次数 - `stored_event_count` —— 实际落库次数 - `dropped_count` —— 丢弃次数 Dashboard 如实展示 "发生 100,000 / 采集 10,000 / 丢弃 90,000"。 ### 数据保留策略 - 每个 Project 可配置 `retention_days`(默认 30);Dashboard「应用设置」可改 - 后台 Retention 任务按项目分批 `DELETE ... LIMIT 1000` 清理过期 Event(启动约 60s 后首次执行,默认每 6 小时一轮) - 生产环境 `ENVIRONMENT=production` 时拒绝默认 `JWT_SECRET` / `ADMIN_PASSWORD`,防止裸奔上线 ### 数据一致性兜底 - Event 幂等:`UNIQUE(project_id, event_id)`(Redis 去重只是优化) - Issue 唯一:`UNIQUE(project_id, fingerprint)`(Redis 锁只降低竞争) ### Redis Key 一览 | Key | TTL | | ----------------------------------------------------- | ----------------------- | | `minisentry:project:{id}` | 60s | | `minisentry:project:pk:{public_key}` | 60s(Ingest 按 DSN 密钥查项目) | | `minisentry:release:{id}:{hash}` | 300s | | `minisentry:issue:{id}:{fp}` | 300s | | `minisentry:event:{id}:{event_id}` | 600s | | `minisentry:fingerprint:{id}:{fp}` | 5s | | `minisentry:rate:project:{id}:{minute}` | 120s | | `minisentry:lock:issue:{id}:{fp}` | 5s | | `minisentry:issue-counter:{issue_id}` | 3600s(仅首次设 TTL,惰性回写) | | `minisentry:stats:project:{id}:{yyyyMMdd}` | 48h | | `minisentry:alert-throttle:{issue_id}` | 3600s | | `minisentry:lock:reconcile` | 3600s(每日校正防惊群) | | `minisentry:login-fail:u:{username}` / `:ip:{sha256}` | 900s(防爆破失败计数,IP 已哈希) | ## 账号体系与安全 ### 账号体系(超级管理员) - `users` 表存储账号(username + bcrypt 密码哈希 + role),迁移 `0003_users` - **首次启动自动播种**:users 表为空时,用 `ADMIN_PASSWORD`(默认 `admin`)创建 `superadmin` 账号;之后以数据库为准,改环境变量不影响已有账号 - 登录返回 JWT(携带 role claim)+ 用户信息;登录后可在顶栏菜单修改密码(验证旧密码,新密码至少 6 位,改密后强制重新登录) - `GET /api/auth/me` 解析当前用户;`GET /api/audit-logs` 仅 superadmin 可访问 ### 登录防爆破 - 按 **用户名 + IP** 双维度计数(防换 IP 撞库、也防单 IP 爆破多账号) - 连续失败 **5 次** 锁定 **15 分钟**(Redis 计数,key `minisentry:login-fail:`*);登录成功清零 - Redis 不可用时降级为进程内计数(单实例有效;防爆破是安全加固,允许此降级) - 锁定期间登录返回 `429 too_many_failed_attempts` ### 操作审计日志 - `audit_logs` 表(迁移 `0004_audit_logs`)记录关键管理操作:登录 / 登录失败 / 创建空间 / 创建应用 / 修改应用(含变更字段)/ 变更问题状态(含新旧状态)/ 上传 SourceMap - 每条记录:操作者、动作、目标、变更详情、**脱敏 IP**(sha256 前 16 位,不存原文)、状态(ok / denied / failed)、时间 - 审计写入失败不阻断业务(静默 + WARNING 日志);detail 不记录密码/token 等敏感值 - Dashboard 侧边栏"审计日志"页(仅超管可见):按操作者/状态筛选,中文化动作名 ### 其他安全 - Management API:统一 JWT Bearer(`require_user`);Ingest:URL 路径中的 `public_key`(32 位 hex)认证,不暴露数字 project_id - SourceMap CI 可使用项目级只写 Token,不需要在构建流水线保存管理员 JWT;Token 仅在生成时显示一次,可随时轮换或撤销 - 告警密钥(钉钉 secret)只写不读,响应用 `secret_configured` 表示是否已配置 - 双层脱敏(SDK + 服务端):`password / token / authorization / cookie / secret / apikey` 等敏感键递归替换为 `[filtered]`;URL query 敏感参数剥离 - 技术字段白名单:服务端只保留 browser/os/device 上下文、技术面包屑和散列用户 ID;environment 可用于区分 production/staging - 安全响应头(nosniff / DENY / CSP)、`X-Request-ID` 贯通、路径穿越防护(SourceMap 文件名白名单校验) - 日志不记录完整 Event、Token、Password、Cookie - 通知异步化:新 Issue / 风暴告警通过 `asyncio.create_task` + 独立 session 发送,不阻塞 Ingest 返回 ## 通知告警 每个应用可配置三种通知通道(可同时启用):**钉钉机器人**、**邮箱** 或 **通用 Webhook**。 ### 触发时机 | 事件 | 说明 | | ------- | --------------------------------------------------------------------- | | 新问题首次出现 | Issue 首次创建时通知(每通道可用 `notify_on_new_issue` 开关) | | 异常风暴 | Issue 累计发生次数跨过 `storm_threshold`(默认 1000)时通知;同一 Issue 1 小时节流,最多一次,防刷屏 | 通知内容包含:应用名、错误标题、Issue 编号、累计发生次数。发送失败只记录日志,绝不阻断 Ingest 主流程。 ### 配置方式 **钉钉**:Dashboard → 应用概览 → 告警设置 → 填机器人 Webhook(可选加签密钥)→ 保存 → 发送测试消息验证。 **通用 Webhook**: - 仅允许解析到**公网**的 `https://` URL(拒绝 localhost / RFC1918 / link-local;发送前二次 DNS 解析并钉扎已校验 IP,降低重绑定风险) - 请求体为固定技术字段 JSON;签名头: - `X-MiniSentry-Timestamp`:Unix 秒 - `X-MiniSentry-Signature`:`sha256=` + `HMAC-SHA256(secret, timestamp + "." + raw_body)` - 接收方应校验签名,并建议拒绝 `|now - timestamp| > 300s` 的请求以防重放 - `webhook_secret` 只写不回显(`webhook_secret_configured`) **邮箱**:同样在告警设置页填收件邮箱;发件方使用服务端全局 SMTP 环境变量: ```env SMTP_HOST=smtp.example.com SMTP_PORT=465 SMTP_USER=alert@example.com SMTP_PASSWORD=xxx SMTP_FROM=alert@example.com SMTP_SSL=1 # 1=SMTP_SSL(465),0=SMTP+STARTTLS ``` ### API ```bash # 查看/保存通道配置(PUT upsert,dingtalk / email / webhook 各一条) GET /api/projects/{id}/alerts PUT /api/projects/{id}/alerts/{dingtalk|email|webhook} # 测试发送(验证配置正确性) POST /api/projects/{id}/alerts/test {"channel_type": "dingtalk"|"email"|"webhook"} ``` 数据存于 `alert_configs` 表(迁移 `0005_alert_configs`),每项目每通道一条(UNIQUE(project_id, channel_type))。 ## 版本路线 - **V2.0.2**:事件幂等、Release 并发、CORS、自动迁移、就绪探针与故障测试 - **V2.1**:environment、回归提醒、SourceMap CI Token、接入向导、技术筛选与批量处置 - **V2.2**:运行指标、CI、备份恢复、运维手册和双 API 高可用模板 - **V2.3**:SDK 隐私白名单、发送诊断、版本对比和受保护项目删除 - **V2.4**:Fingerprint V2 可解释分组、结构化噪声规则与 SourceMap 诊断中心 - **V2.5**:Release 接入状态、Doctor CLI、HMAC 通用 Webhook 与 React SDK - **V2.6**:Release 异常基线、质量门禁、相似 Issue、人工 Fingerprint Alias 与可选内网摘要 - **V2.x 收口**(建议先于 V3):预发压测、Doctor 存档、10M 分析 / 日聚合、质量规则 UI 等,见 [ROADMAP_V3.md](docs/ROADMAP_V3.md) §2 - **V3.0**(仅达触发条件后):Redis Stream + Worker 异步 Ingest,PostgreSQL 仍为事实源;详见 [docs/ROADMAP_V3.md](docs/ROADMAP_V3.md) - **V3.1+**:分析加速;列存仅在分析仍不够时评估——不要提前引入 Kafka / ClickHouse ## 环境变量 后端通过 `backend/app/core/config.py` 读取多环境 `.env`(详见 [backend/README.md](backend/README.md)#2-配置环境变量多环境): 加载顺序:`.env` → `.env.{environment}` → `.env.local` → `.env.{environment}.local` → 进程环境变量。 用 `ENVIRONMENT` / `APP_ENV` 选择环境(`development` | `staging` | `production`)。 | 变量 | 默认 | 说明 | | --------------------------------------- | --------------------------- | ------------------------------------------ | | `ENVIRONMENT` / `APP_ENV` | development | 当前环境;`production`/`prod` 时启用默认密钥 hardening | | `HOST` | 0.0.0.0 | HTTP 监听地址(`python run.py`) | | `PORT` | 8000 | HTTP 端口 | | `RELOAD` | false | 热更新;开发环境 `.env.development` 默认 true | | `DATABASE_URL` | localhost:5432/minisentry | PostgreSQL 连接串 | | `REDIS_URL` | localhost:6379/0 | Redis 连接串 | | `REDIS_MAX_CONNECTIONS` | 50 | Redis 连接池 | | `DEFAULT_RATE_LIMIT` | 1000 | 项目默认限流(events/min) | | `FINGERPRINT_DEDUP_WINDOW` | 5 | 指纹去重窗口(秒) | | `REQUEST_BODY_LIMIT` / `MAX_EVENT_SIZE` | 102400 | 单事件 body 上限(字节) | | `PUBLIC_URL` | (空) | Dashboard 公网地址,用于 DSN 与告警 Issue 深链 | | `CORS_ALLOWED_ORIGINS` | 本机开发地址 | 允许跨域上报的业务前端来源,生产用逗号分隔且禁止 `*` | | `TRUSTED_PROXIES` | (空) | 受信反向代理 CIDR/IP;仅对端在此列表时才采信 `X-Forwarded-For`(防伪造绕过限流);Docker 部署默认私网段 | | `JWT_SECRET` | change-me-in-production | JWT 密钥(生产必改,长度≥16;生产默认值拒绝启动) | | `ADMIN_USERNAME` | admin | 超级管理员用户名(首次播种用) | | `ADMIN_PASSWORD` | admin | 超级管理员初始密码(首次播种用;生产禁止默认值) | | `SOURCEMAP_STORAGE` | /data/minisentry/sourcemaps | SourceMap 存储目录 | | `AI_SUMMARY_ENABLED` | false | 是否启用 Issue AI 摘要(OpenAI 兼容) | | `AI_SUMMARY_BASE_URL` | 空 | OpenAI 兼容 API 根,如 `https://api.openai.com/v1` | | `AI_SUMMARY_API_KEY` | 空 | Bearer Token / API Key | | `AI_SUMMARY_MODEL` | 空 | 模型 ID,如 `gpt-4o-mini` | | `AI_SUMMARY_TIMEOUT` | 60 | 单次模型请求超时(秒);结果落库后再次打开不再重复请求 | | `SMTP_HOST` 等 | (空) | 邮件告警,见「通知告警」节 |