# BugCollect API v1 规范 > **版本**:1.1.0 > **日期**:2026-06-17 > **状态**:Draft > **设计参考**:[Sentry Event Payloads](https://develop.sentry.dev/sdk/event-payloads/) > **服务端模块**:`xuqm-bugcollect-service` > **基础路径**:`/bugcollect/v1` --- ## 目录 1. [概述](#1-概述) 2. [术语表](#2-术语表) 3. [认证与安全](#3-认证与安全) 4. [数据上报端点](#4-数据上报端点) - 4.1 [Issue 批量上报](#41-issue-批量上报) - 4.2 [Event 批量上报](#42-event-批量上报) - 4.3 [SourceMap / 符号文件上传](#43-sourcemap--符号文件上传) - 4.4 [Session 上报](#44-session-上报) 5. [数据模型](#5-数据模型) - 5.1 [批量信封 Envelope](#51-批量信封-envelope) - 5.2 [IssueEvent](#52-issueevent) - 5.3 [LogEvent](#53-logevent) - 5.4 [Breadcrumb](#54-breadcrumb) - 5.5 [枚举定义](#55-枚举定义) 6. [查询端点](#6-查询端点) - 6.1 [Issue 列表](#61-issue-列表) - 6.2 [Issue 详情](#62-issue-详情) - 6.3 [Issue 事件列表](#63-issue-事件列表) - 6.4 [Issue 趋势](#64-issue-趋势) - 6.5 [Event 列表](#65-event-列表) - 6.6 [概览统计](#66-概览统计) - 6.7 [频率排行](#67-频率排行) - 6.8 [风险排行](#68-风险排行) - 6.9 [漏斗分析](#69-漏斗分析) 7. [Issue 管理端点](#7-issue-管理端点) - 7.1 [解决 Issue](#71-解决-issue) - 7.2 [忽略 Issue](#72-忽略-issue) - 7.3 [分配责任人](#73-分配责任人) - 7.4 [批量操作](#74-批量操作) 8. [Webhook](#8-webhook) 9. [错误码](#9-错误码) 10. [Fingerprint 去重机制](#10-fingerprint-去重机制) 11. [SDK 集成指南](#11-sdk-集成指南) 12. [数据库 Schema](#12-数据库-schema) 13. [变更日志](#13-变更日志) --- ## 1. 概述 BugCollect 是一个轻量级错误收集与分析服务,设计目标对齐 [Sentry](https://sentry.io) 核心能力,为 XuqmGroup 全平台 SDK(Android、iOS、HarmonyOS、React Native、Web)提供统一的错误上报、聚合、查询和告警能力。 ### 核心能力 | 能力 | 说明 | |------|------| | **Issue 聚合** | 按 fingerprint 自动去重,将同类错误聚合为 Issue | | **Event 追踪** | 每次错误发生记录独立 Event,保留完整上下文和面包屑 | | **Analytics 事件** | 自定义业务事件上报,支持漏斗分析 | | **Session 追踪** | 会话生命周期上报,支持 crash-free session rate 计算 | | **符号化** | 上传 SourceMap / ProGuard mapping / dSYM,支持多平台堆栈还原 | | **Webhook 告警** | 新 Issue、fatal 级别、阈值超限等事件触发 Webhook 通知 | | **Issue 管理** | 解决、忽略、分配、批量操作 | | **多平台** | 统一 API,平台差异由 `platform` 字段区分 | ### 设计原则 1. **Sentry 对齐**:字段命名和结构参考 Sentry,降低学习成本 2. **批量优先**:上报端点均为批量接口,减少网络请求 3. **信封封装**:请求体使用信封(Envelope)结构,支持携带 SDK 元信息 4. **幂等安全**:每条 Event 携带 `eventId`,服务端按此去重 5. **向前兼容**:未知字段忽略不报错,新字段可选添加 --- ## 2. 术语表 | 术语 | 说明 | |------|------| | **Issue** | 一类错误的聚合,由 fingerprint 唯一标识。一个 Issue 对应多次 Event | | **Event** | 一次具体的错误发生记录,包含完整的堆栈、面包屑、设备、用户等上下文 | | **Analytics Event** | 自定义业务事件(如页面访问、按钮点击),用于漏斗分析 | | **Session** | 一次应用使用过程,从前台打开到退出/崩溃,用于计算 crash-free rate | | **Breadcrumb** | 崩溃前的操作轨迹,如导航、网络请求、用户操作,自动附带在 Event 中 | | **Fingerprint** | 错误指纹,SHA-256 哈希值,用于 Issue 去重分组 | | **Level** | 错误严重级别:`fatal` > `error` > `warning` > `info` > `debug` | | **Status** | Issue 状态:`open`(未处理)/ `resolved`(已解决)/ `ignored`(已忽略) | | **Release** | 应用版本号,对应 `appVersion` | | **Environment** | 运行环境:`production` / `staging` / `development` / `integration` | | **Envelope** | 批量请求的信封结构,包含 `sentAt`、`sdk`、`events` | | **AppKey** | 应用唯一标识,由平台分配 | --- ## 3. 认证与安全 ### 上报端点(SDK → 服务端) SDK 通过 `config.xuqm` 完成初始化认证,`appKey` 由 SDK 自动从配置文件读取并附带在请求体中,**开发者无需手动处理认证逻辑**。 ``` appKey 来源:assets/xuqm/config.xuqm(加密文件,由租户平台下发) ``` 认证链路: 1. 开发者从租户平台下载 `config.xuqm`,内含 `appKey` 和可选的 `serverUrl` 2. `config.xuqm` 已加密存储,且绑定包名(SDK 启动时校验包名与文件中记录的一致性) 3. SDK 初始化时从平台拉取配置,验证 BugCollect 服务已开通(`t_feature_service.enabled = true`);未开通时 `captureError` 为空操作,不发送任何请求 4. 服务端收到上报后,验证 `appKey` 有效且对应 BugCollect 服务已激活,否则拒绝写入 > **服务激活即权限边界**:BugCollect 服务开通状态由租户在平台管理,未开通的 `appKey` 的上报请求会被服务端拒绝,无需额外 Token。 建议 SDK 端通过 **HTTPS** 传输,防止 appKey 被中间人截获。 ### 查询端点 / 管理端点(Web → 服务端) - 通过 API 网关统一鉴权(租户 Token) - 查询参数中的 `appKey` 必须与鉴权租户匹配 ### 速率限制 | 端点类型 | 限制 | 超限行为 | |---------|------|---------| | Issue / Event 上报 | 1000 次/分钟/appKey | HTTP 429,SDK 将事件重新入队,下次重试 | | Session 上报 | 5000 次/分钟/appKey | HTTP 429,SDK 丢弃(会话数据可接受损失) | | SourceMap 上传 | 20 次/小时/appKey | HTTP 429 | | 查询端点 | 100 次/分钟/租户 | HTTP 429 | --- ## 4. 数据上报端点 ### 4.1 Issue 批量上报 捕获的错误、未捕获的崩溃统一通过此端点上报。 ``` POST /bugcollect/v1/issues/batch Content-Type: application/json ``` #### 请求体 ```json { "sentAt": "2026-06-17T10:30:00Z", "sdk": { "name": "bugcollect.android", "version": "1.0.0" }, "events": [ { "eventId": "550e8400-e29b-41d4-a716-446655440000", "appKey": "2000111111110002", "level": "error", "platform": "android", "release": "7.2.13", "environment": "production", "timestamp": 1718620200000, "fingerprint": "a1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456", "exception": { "type": "IllegalStateException", "value": "更新服务未开通 (code=40404)", "stacktrace": "java.lang.IllegalStateException: 更新服务未开通\n\tat com.szyx.app.MainActivity.onCreate(MainActivity.kt:178)\n\tat android.app.Activity.performCreate(Activity.java:8000)" }, "breadcrumbs": [ { "timestamp": 1718620195000, "category": "navigation", "message": "进入首页", "level": "info" }, { "timestamp": 1718620199000, "category": "network", "message": "GET /api/update → 404", "level": "warning", "data": { "url": "https://update.dev.xuqinmin.com/", "statusCode": 404 } } ], "user": { "id": "user-123" }, "device": { "name": "Pixel 7", "model": "Pixel 7", "manufacturer": "Google", "osName": "Android", "osVersion": "14", "locale": "zh_CN", "timezone": "Asia/Shanghai", "network": "wifi", "isEmulator": false, "freeMemoryMb": 512, "buildType": "release" }, "tags": { "context": "checkAppUpdate" } } ] } ``` #### 响应 ```json { "code": 200, "message": "success", "data": null } ``` > **幂等说明**:服务端按 `eventId` 去重。相同 `appKey` + `eventId` 的二次提交直接返回 200,不重复写入。 #### cURL 示例 ```bash curl -X POST https://bugcollect.xuqinmin.com/bugcollect/v1/issues/batch \ -H "Content-Type: application/json" \ -d '{ "sentAt": "2026-06-17T10:30:00Z", "sdk": { "name": "bugcollect.android", "version": "1.0.0" }, "events": [{ "eventId": "550e8400-e29b-41d4-a716-446655440000", "appKey": "2000111111110002", "level": "error", "platform": "android", "release": "7.2.13", "environment": "production", "timestamp": 1718620200000, "fingerprint": "abc123...", "exception": { "type": "NullPointerException", "value": "Attempt to invoke virtual method on a null reference", "stacktrace": "java.lang.NullPointerException: ...\n\tat ..." } }] }' ``` --- ### 4.2 Event 批量上报 自定义业务事件(页面访问、功能使用、漏斗步骤等)通过此端点上报。 ``` POST /bugcollect/v1/events/batch Content-Type: application/json ``` #### 请求体 ```json { "sentAt": "2026-06-17T10:30:00Z", "sdk": { "name": "bugcollect.android", "version": "1.0.0" }, "events": [ { "eventId": "660e8400-e29b-41d4-a716-446655440001", "appKey": "2000111111110002", "name": "page_view", "properties": { "page": "/home", "referrer": "/login" }, "platform": "android", "release": "7.2.13", "environment": "production", "timestamp": 1718620200000, "user": { "id": "user-123" }, "device": { "osName": "Android", "osVersion": "14", "isEmulator": false } } ] } ``` #### 响应 ```json { "code": 200, "message": "success", "data": null } ``` --- ### 4.3 SourceMap / 符号文件上传 上传符号文件,用于堆栈还原。各平台文件格式不同: | 平台 | 文件类型 | 说明 | |------|----------|------| | `web` / `react-native` | `.map` | JavaScript SourceMap | | `android` | `.txt` | ProGuard / R8 mapping 文件 | | `ios` | `.zip` | dSYM 压缩包(含 DWARF 调试信息) | | `harmonyos` | `.map` | 类 SourceMap 格式 | ``` POST /bugcollect/v1/sourcemaps/upload Content-Type: multipart/form-data ``` #### 请求参数 | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | `appKey` | string | ✅ | 应用标识 | | `platform` | string | ✅ | 平台:`web` / `react-native` / `android` / `ios` / `harmonyos` | | `appVersion` | string | ✅ | 应用版本,对应 `release` 字段 | | `bundleName` | string | | JS Bundle 名称,默认 `index`(web/rn 专用) | | `file` | file | ✅ | 符号文件 | #### cURL 示例 ```bash # React Native SourceMap curl -X POST https://bugcollect.xuqinmin.com/bugcollect/v1/sourcemaps/upload \ -F "appKey=2000111111110002" \ -F "platform=react-native" \ -F "appVersion=7.2.13" \ -F "bundleName=index" \ -F "file=@./index.bundle.map" # Android ProGuard mapping curl -X POST https://bugcollect.xuqinmin.com/bugcollect/v1/sourcemaps/upload \ -F "appKey=2000111111110002" \ -F "platform=android" \ -F "appVersion=7.2.13" \ -F "file=@./mapping.txt" # iOS dSYM curl -X POST https://bugcollect.xuqinmin.com/bugcollect/v1/sourcemaps/upload \ -F "appKey=2000111111110002" \ -F "platform=ios" \ -F "appVersion=7.2.13" \ -F "file=@./YwxApp.app.dSYM.zip" ``` #### 响应 ```json { "code": 200, "message": "success", "data": { "id": 1, "appKey": "2000111111110002", "platform": "android", "appVersion": "7.2.13", "uploadedAt": "2026-06-17T10:30:00" } } ``` --- ### 4.4 Session 上报 上报会话生命周期,用于计算 crash-free session rate。 ``` POST /bugcollect/v1/sessions/batch Content-Type: application/json ``` #### 请求体 ```json { "sentAt": "2026-06-17T10:30:00Z", "sdk": { "name": "bugcollect.android", "version": "1.0.0" }, "sessions": [ { "sessionId": "sess-abc-123", "appKey": "2000111111110002", "status": "exited", "platform": "android", "release": "7.2.13", "environment": "production", "startedAt": 1718620100000, "duration": 95000, "user": { "id": "user-123" }, "errors": 0 } ] } ``` #### Session 状态 | `status` | 说明 | |---------|------| | `ok` | 进行中(心跳上报) | | `exited` | 正常退出(用户主动离开或 App 进入后台超时) | | `crashed` | 因未捕获异常终止 | | `abnormal` | 进程被系统杀死(ANR、OOM、后台清理) | #### 字段说明 | 字段 | 类型 | 必填 | 说明 | |------|------|------|------| | `sessionId` | string | ✅ | 会话唯一 ID(UUID) | | `appKey` | string | ✅ | 应用标识 | | `status` | string | ✅ | 会话状态,见上表 | | `platform` | string | ✅ | 平台 | | `release` | string | | 应用版本 | | `environment` | string | | 运行环境 | | `startedAt` | long | ✅ | 会话开始时间戳(毫秒) | | `duration` | long | | 会话时长(毫秒) | | `user.id` | string | | 用户 ID | | `errors` | int | | 会话内捕获的 error 数量 | --- ## 5. 数据模型 ### 5.1 批量信封 Envelope 所有批量上报端点共享统一的信封结构。信封层的 `sdk` 字段作为默认值,事件层不再重复携带。 | 字段 | 类型 | 必填 | 说明 | |------|------|------|------| | `sentAt` | string (ISO 8601) | | 客户端发送时间,用于时钟偏差校正 | | `sdk` | object | | SDK 信息 | | `sdk.name` | string | | SDK 标识,如 `bugcollect.android`、`bugcollect.ios`、`bugcollect.harmonyos`、`bugcollect.rn`、`bugcollect.web` | | `sdk.version` | string | | SDK 版本号 | | `events` | array | ✅ | 事件数组,不能为空 | --- ### 5.2 IssueEvent 错误事件数据模型,用于 `captureError()` 和 `captureCrash()`。 | 字段 | 类型 | 必填 | 说明 | |------|------|------|------| | `eventId` | string (UUID) | ✅ | 客户端生成的唯一事件 ID,服务端按此去重 | | `appKey` | string | ✅ | 应用标识 | | `level` | string | ✅ | 严重级别,见 [Level 枚举](#551-level) | | `platform` | string | ✅ | 平台,见 [Platform 枚举](#552-platform) | | `fingerprint` | string | ✅ | 错误指纹,64 字符 SHA-256 哈希 | | `timestamp` | long | ✅ | 事件时间戳(毫秒) | | `exception` | object | | 异常信息 | | `exception.type` | string | | 异常类名,如 `NullPointerException` | | `exception.value` | string | | 异常消息 | | `exception.stacktrace` | string | | 完整堆栈文本 | | `breadcrumbs` | array | | 崩溃前操作轨迹,见 [Breadcrumb](#54-breadcrumb),最多 50 条 | | `release` | string | | 应用版本号 | | `environment` | string | | 运行环境,默认 `production` | | `user` | object | | 用户信息 | | `user.id` | string | | 用户 ID | | `device` | object | | 设备信息,见下表 | | `tags` | object | | 自定义标签,key-value 均为 string | #### device 字段 | 字段 | 类型 | 说明 | |------|------|------| | `name` | string | 设备名称,如 `Pixel 7` | | `model` | string | 设备型号 | | `manufacturer` | string | 制造商 | | `osName` | string | 操作系统名称 | | `osVersion` | string | 操作系统版本 | | `locale` | string | 语言区域,如 `zh_CN` | | `timezone` | string | 时区,如 `Asia/Shanghai` | | `network` | string | 网络类型:`wifi` / `cellular` / `none` / `unknown` | | `isEmulator` | boolean | 是否模拟器(过滤开发噪声) | | `freeMemoryMb` | int | 可用内存(MB) | | `buildType` | string | 构建类型:`debug` / `release` | #### 最小上报示例 ```json { "eventId": "550e8400-e29b-41d4-a716-446655440000", "appKey": "2000111111110002", "level": "error", "platform": "android", "fingerprint": "abc123...", "timestamp": 1718620200000, "exception": { "type": "Exception", "value": "something went wrong", "stacktrace": "..." } } ``` --- ### 5.3 LogEvent 自定义业务事件数据模型,用于 `event()` 方法。 | 字段 | 类型 | 必填 | 说明 | |------|------|------|------| | `eventId` | string (UUID) | ✅ | 客户端生成的唯一事件 ID,服务端按此去重 | | `appKey` | string | ✅ | 应用标识 | | `name` | string | ✅ | 事件名称,如 `page_view`、`button_click` | | `timestamp` | long | ✅ | 事件时间戳(毫秒) | | `properties` | object | | 事件属性,任意 key-value | | `platform` | string | | 平台 | | `release` | string | | 应用版本号 | | `environment` | string | | 运行环境 | | `user` | object | | 用户信息 | | `user.id` | string | | 用户 ID | | `device` | object | | 设备信息(同 IssueEvent.device) | --- ### 5.4 Breadcrumb 崩溃前的操作轨迹条目,自动附带在 IssueEvent 的 `breadcrumbs` 数组中,最多保留最近 50 条。 | 字段 | 类型 | 必填 | 说明 | |------|------|------|------| | `timestamp` | long | ✅ | 轨迹时间戳(毫秒) | | `category` | string | ✅ | 分类,见下表 | | `message` | string | ✅ | 描述文本 | | `level` | string | | `info` / `warning` / `error`,默认 `info` | | `data` | object | | 额外键值对,如 `url`、`statusCode`、`method` | #### 标准 Category | category | 说明 | 典型 data 字段 | |----------|------|---------------| | `navigation` | 页面/路由切换 | `from`, `to` | | `network` | HTTP 请求 | `url`, `method`, `statusCode`, `durationMs` | | `ui` | 用户交互 | `action`(tap/swipe),`target` | | `lifecycle` | App / Activity 生命周期 | `event`(foreground/background) | | `console` | 手动打点日志 | — | | `error` | SDK 内部捕获的非致命错误 | — | --- ### 5.5 枚举定义 #### 5.5.1 Level 错误严重级别,从高到低排列。 | 值 | 含义 | 对应 SDK 方法 | Web 展示颜色 | |----|------|--------------|-------------| | `fatal` | 未捕获崩溃,应用即将终止 | `captureCrash()` | 🔴 深红 | | `error` | 捕获的错误,不影响应用存活 | `captureError()` | 🔴 红 | | `warning` | 警告,潜在问题 | `warn()` | 🟡 黄 | | `info` | 信息性记录 | `info()` | 🔵 蓝 | | `debug` | 调试信息 | `debug()` | ⚪ 灰 | **服务端存储策略**: - `fatal` / `error` → 写入 `log_issues` 表参与 Issue 聚合,同时写入 `log_issue_events` 表记录明细 - `warning` / `info` / `debug` → **仅写入** `log_issue_events` 表,不创建或更新 Issue,不触发告警 #### 5.5.2 Platform | 值 | 说明 | |----|------| | `android` | Android 原生 | | `ios` | iOS 原生 | | `harmonyos` | HarmonyOS 原生 | | `react-native` | React Native 跨平台 | | `web` | Web 浏览器 | | `java-server` | Java 服务端 | | `python-server` | Python 服务端 | | `go-server` | Go 服务端 | #### 5.5.3 Environment | 值 | 说明 | |----|------| | `production` | 生产环境(默认) | | `staging` | 预发布环境 | | `integration` | 集成测试环境 | | `development` | 开发环境 | #### 5.5.4 Issue Status | 值 | 说明 | |----|------| | `open` | 未处理(默认) | | `resolved` | 已解决 | | `ignored` | 已忽略,不再告警 | --- ## 6. 查询端点 > 以下端点均为 `GET` 请求,返回数据格式统一为 `ApiResponse`。 ### 通用响应格式 ```json { "code": 200, "message": "success", "data": { ... } } ``` ### 通用分页格式 ```json { "items": [], "page": 1, "size": 20, "total": 100 } ``` --- ### 6.1 Issue 列表 ``` GET /bugcollect/v1/issues ``` #### 查询参数 | 参数 | 类型 | 必填 | 默认值 | 说明 | |------|------|------|--------|------| | `appKey` | string | ✅ | | 应用标识 | | `level` | string | | | 级别筛选:`fatal` / `error` / `warning` | | `status` | string | | `open` | 状态筛选:`open` / `resolved` / `ignored` | | `platform` | string | | | 平台筛选 | | `release` | string | | | 版本筛选 | | `q` | string | | | 按 Issue 标题全文搜索 | | `from` | string | | | 起始日期 `YYYY-MM-DD` | | `to` | string | | | 结束日期 `YYYY-MM-DD` | | `page` | int | | 1 | 页码(从 1 开始) | | `size` | int | | 20 | 每页条数 | #### 响应 ```json { "code": 200, "message": "success", "data": { "items": [ { "id": 1, "appKey": "2000111111110002", "fingerprint": "abc123...", "level": "error", "status": "open", "title": "IllegalStateException: 更新服务未开通", "firstSeenAt": "2026-06-17T10:00:00", "lastSeenAt": "2026-06-17T12:00:00", "count": 15, "affectedUsers": 8, "platform": "android", "release": "7.2.13" } ], "page": 1, "size": 20, "total": 1 } } ``` --- ### 6.2 Issue 详情 仅返回 Issue 元数据。事件明细通过 [6.3 Issue 事件列表](#63-issue-事件列表) 分页获取。 ``` GET /bugcollect/v1/issues/{id} ``` #### 路径参数 | 参数 | 类型 | 说明 | |------|------|------| | `id` | long | Issue ID | #### 响应 ```json { "code": 200, "message": "success", "data": { "id": 1, "appKey": "2000111111110002", "fingerprint": "abc123...", "level": "error", "status": "open", "title": "IllegalStateException: 更新服务未开通", "firstSeenAt": "2026-06-17T10:00:00", "lastSeenAt": "2026-06-17T12:00:00", "count": 15, "affectedUsers": 8, "platform": "android", "release": "7.2.13", "assignee": null } } ``` --- ### 6.3 Issue 事件列表 分页获取某个 Issue 下的所有事件明细,含完整堆栈、面包屑、设备信息。 ``` GET /bugcollect/v1/issues/{id}/events ``` #### 查询参数 | 参数 | 类型 | 必填 | 默认值 | 说明 | |------|------|------|--------|------| | `id` | long | ✅(路径) | | Issue ID | | `from` | string | | | 起始日期 | | `to` | string | | | 结束日期 | | `page` | int | | 1 | 页码 | | `size` | int | | 20 | 每页条数 | #### 响应 ```json { "code": 200, "message": "success", "data": { "items": [ { "id": 101, "issueId": 1, "eventId": "550e8400-e29b-41d4-a716-446655440000", "appKey": "2000111111110002", "level": "error", "userId": "user-123", "sessionId": "sess-abc", "exception": { "type": "IllegalStateException", "value": "更新服务未开通", "stacktrace": "java.lang.IllegalStateException: ...", "stackSymbolicated": null }, "breadcrumbs": [ { "timestamp": 1718620195000, "category": "navigation", "message": "进入首页", "level": "info" } ], "tags": { "context": "checkAppUpdate" }, "device": { "osName": "Android", "osVersion": "14", "isEmulator": false, "network": "wifi" }, "platform": "android", "release": "7.2.13", "environment": "production", "sdkName": "bugcollect.android", "sdkVersion": "1.0.0", "createdAt": "2026-06-17T10:30:00" } ], "page": 1, "size": 20, "total": 15 } } ``` --- ### 6.4 Issue 趋势 获取某个 Issue 在指定时间范围内的每日发生次数趋势,用于判断问题是在收敛还是扩散。 ``` GET /bugcollect/v1/issues/{id}/trend ``` #### 查询参数 | 参数 | 类型 | 必填 | 默认值 | 说明 | |------|------|------|--------|------| | `from` | string | | 近 14 天 | 起始日期 `YYYY-MM-DD` | | `to` | string | | 今天 | 结束日期 `YYYY-MM-DD` | #### 响应 ```json { "code": 200, "message": "success", "data": { "issueId": 1, "points": [ { "date": "2026-06-11", "count": 2, "affectedUsers": 1 }, { "date": "2026-06-12", "count": 5, "affectedUsers": 3 }, { "date": "2026-06-17", "count": 8, "affectedUsers": 5 } ] } } ``` --- ### 6.5 Event 列表 ``` GET /bugcollect/v1/events ``` #### 查询参数 | 参数 | 类型 | 必填 | 默认值 | 说明 | |------|------|------|--------|------| | `appKey` | string | ✅ | | 应用标识 | | `name` | string | | | 事件名称筛选 | | `userId` | string | | | 用户 ID 筛选 | | `from` | string | | | 起始日期 | | `to` | string | | | 结束日期 | | `page` | int | | 1 | 页码 | | `size` | int | | 20 | 每页条数 | --- ### 6.6 概览统计 ``` GET /bugcollect/v1/overview?appKey={appKey} ``` #### 查询参数 | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | `appKey` | string | ✅ | 应用标识 | | `from` | string | | 起始日期 | | `to` | string | | 结束日期 | #### 响应 ```json { "code": 200, "message": "success", "data": { "totalIssues": 42, "openIssues": 35, "todayNewIssues": 3, "affectedUsers": 128, "crashFreeSessionRate": 0.9971, "crashRate": 0.05, "crashRateTrend": [ { "date": "2026-06-11", "count": 5, "rate": 0.03 }, { "date": "2026-06-12", "count": 8, "rate": 0.05 } ], "topIssues": [ { "id": 1, "title": "NullPointerException: ...", "level": "error", "count": 15, "affectedUsers": 8 } ] } } ``` > **crashFreeSessionRate**:`1 - (crashed_sessions / total_sessions)`,行业标准指标,需 [Session 上报](#44-session-上报) 数据支撑。 --- ### 6.7 频率排行 ``` GET /bugcollect/v1/issues/rankings/frequency ``` #### 查询参数 | 参数 | 类型 | 必填 | 默认值 | 说明 | |------|------|------|--------|------| | `appKey` | string | ✅ | | 应用标识 | | `from` | string | | | 起始日期 | | `to` | string | | | 结束日期 | | `limit` | int | | 20 | 返回条数 | --- ### 6.8 风险排行 ``` GET /bugcollect/v1/issues/rankings/risk ``` 风险分 = `count × level_weight × recency_weight`,`fatal` 权重最高。 #### 查询参数 同 [频率排行](#67-频率排行)。 --- ### 6.9 漏斗分析 ``` GET /bugcollect/v1/events/funnel ``` #### 查询参数 | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | `appKey` | string | ✅ | 应用标识 | | `steps` | string | ✅ | 逗号分隔的步骤名,如 `page_view,click_submit,order_created` | | `from` | string | | 起始日期 | | `to` | string | | 结束日期 | #### 响应 ```json { "code": 200, "message": "success", "data": { "steps": ["page_view", "click_submit", "order_created"], "counts": [1000, 600, 300], "rates": [100.0, 60.0, 30.0] } } ``` --- ## 7. Issue 管理端点 > 以下端点均需租户 Token 鉴权(Web 端调用)。 ### 7.1 解决 Issue ``` PUT /bugcollect/v1/issues/{id}/resolve ``` 将 Issue 状态置为 `resolved`。若该 Issue 后续有新 Event 上报,状态自动回滚到 `open` 并触发 `issue.regressed` Webhook。 #### 响应 ```json { "code": 200, "message": "success", "data": null } ``` --- ### 7.2 忽略 Issue ``` PUT /bugcollect/v1/issues/{id}/ignore ``` 将 Issue 状态置为 `ignored`。忽略后不再触发告警,不影响数据记录。 --- ### 7.3 分配责任人 ``` PUT /bugcollect/v1/issues/{id}/assign Content-Type: application/json ``` #### 请求体 ```json { "assignee": "user@example.com" } ``` 传 `null` 取消分配。 --- ### 7.4 批量操作 ``` POST /bugcollect/v1/issues/bulk Content-Type: application/json ``` #### 请求体 ```json { "ids": [1, 2, 3], "action": "resolve" } ``` #### action 枚举 | 值 | 说明 | |----|------| | `resolve` | 批量解决 | | `ignore` | 批量忽略 | | `reopen` | 批量重新打开 | --- ## 8. Webhook ### 8.1 查询 Webhook 列表 ``` GET /bugcollect/v1/webhooks?appKey={appKey} ``` ### 8.2 创建 Webhook ``` POST /bugcollect/v1/webhooks Content-Type: application/json ``` #### 请求体 ```json { "appKey": "2000111111110002", "url": "https://hooks.example.com/notify", "events": ["issue.created", "issue.fatal"], "secret": "webhook-secret-key" } ``` ### 8.3 更新 Webhook ``` PUT /bugcollect/v1/webhooks/{id} ``` ### 8.4 删除 Webhook ``` DELETE /bugcollect/v1/webhooks/{id} ``` ### Webhook 事件类型 | 事件 | 触发时机 | |------|---------| | `issue.created` | 新 Issue 首次出现 | | `issue.fatal` | `fatal` 级别事件上报时立即触发(不等去重) | | `issue.threshold` | Issue 发生次数超过配置阈值(默认 100 次) | | `issue.regressed` | 已解决的 Issue 重新出现 | | `issue.resolved` | Issue 被标记为已解决(用于 CI/CD 集成) | ### Webhook 回调体 ```json { "event": "issue.created", "timestamp": "2026-06-17T10:30:00Z", "issue": { "id": 1, "appKey": "2000111111110002", "level": "error", "status": "open", "title": "IllegalStateException: 更新服务未开通", "platform": "android", "count": 1, "affectedUsers": 1, "firstSeenAt": "2026-06-17T10:30:00" } } ``` > **签名验证**:Webhook 请求携带 `X-BugCollect-Signature: sha256=` 请求头,接收方用 `secret` 验证请求合法性。 --- ## 9. 错误码 | HTTP 状态码 | code | 说明 | |------------|------|------| | 200 | 200 | 成功 | | 400 | 400 | 请求参数校验失败 | | 400 | 40001 | `appKey` 不能为空 | | 400 | 40002 | `events` / `sessions` 数组为空 | | 400 | 40003 | `fingerprint` 格式非法(非 64 字符十六进制) | | 400 | 40004 | `level` 不在枚举范围内 | | 400 | 40005 | `platform` 不在枚举范围内 | | 403 | 40301 | `appKey` 对应的 BugCollect 服务未开通 | | 404 | 40401 | Issue 不存在 | | 429 | 42901 | 请求频率超限 | | 500 | 500 | 服务端内部错误 | --- ## 10. Fingerprint 去重机制 ### 默认算法 SDK 端默认使用以下公式生成 fingerprint(注意:不含 `level`,同一错误不同严重程度归为同一 Issue): ``` SHA-256( normalize(exception.type + ":" + exception.value) + ":" + top_3_stack_lines ) ``` - `normalize` — 移除数字字面量、UUID、URL、内存地址等易变内容 - `top_3_stack_lines` — 取堆栈中前 3 行(跳过 SDK 内部框架层) ### 自定义 Fingerprint SDK 端可传入自定义 fingerprint,覆盖默认算法: ```kotlin BugCollect.captureError(exception, tags = mapOf("context" to "checkAppUpdate"), fingerprint = "custom-group-key") ``` ### 分组优先级 1. **自定义 fingerprint** — 如果 SDK 传入,完全按此分组 2. **堆栈分组** — 按 `exception.type` + 归一化堆栈前 3 行 3. **消息分组** — 无堆栈时按 `exception.type` + `exception.value` 分组 ### 混淆代码处理 Android Release 包经 ProGuard / R8 混淆后,堆栈行类名如 `a.b.c.d` 无法直接分组。建议: 1. 上传 [ProGuard mapping 文件](#43-sourcemap--符号文件上传) 2. 服务端符号化后将还原堆栈写入 `stack_symbolicated` 3. 对已符号化的堆栈重新计算 fingerprint(服务端异步任务) --- ## 11. SDK 集成指南 ### 11.1 Android SDK #### 初始化 SDK 通过 `config.xuqm` 自动初始化,无需手动调用初始化代码。DSN Token 已内含于配置文件。 ```kotlin // 无需手动初始化,ContentProvider 自动完成 // 若需手动初始化: XuqmSDK.initialize(context, appKey = "2000111111110002") ``` #### 设置用户 通过 `XuqmSDK` 统一管理用户信息,BugCollect 自动跟随: ```kotlin XuqmSDK.setUserInfo(XuqmUserInfo(userId = "user-123")) ``` #### 捕获错误 ```kotlin try { // 业务代码 } catch (e: Exception) { BugCollect.captureError(e, tags = mapOf("context" to "checkAppUpdate")) } ``` #### 记录面包屑 ```kotlin BugCollect.addBreadcrumb(category = "navigation", message = "进入首页") BugCollect.addBreadcrumb( category = "network", message = "GET /api/update → 404", level = "warning", data = mapOf("url" to url, "statusCode" to 404) ) ``` #### 记录事件 ```kotlin BugCollect.event("page_view", properties = mapOf("page" to "/home")) ``` #### 设置环境 ```kotlin BugCollect.setEnvironment("staging") ``` ### 11.2 React Native SDK ```typescript import { BugCollect } from '@xuqm/rn-bugcollect'; // 捕获错误 try { ... } catch (e) { BugCollect.captureError(e, { tags: { context: 'checkAppUpdate' } }); } // 记录面包屑 BugCollect.addBreadcrumb({ category: 'navigation', message: '进入首页' }); // 记录事件 BugCollect.event('page_view', { page: '/home' }); ``` ### 11.3 Web SDK ```javascript import { BugCollect } from '@xuqm/bugcollect-web'; BugCollect.init({ appKey: '2000111111110002' }); // 自动捕获未处理异常(window.onerror / unhandledrejection) // 手动捕获 try { ... } catch (e) { BugCollect.captureError(e); } ``` ### 11.4 批量上报策略 SDK 内部自动管理队列,开发者无需关心。 | 参数 | 值 | 说明 | |------|------|------| | 批量大小 | 30 条 | 积攒 30 条后立即发送 | | 定时刷新 | 10 秒 | 未满 30 条时每 10 秒发送一次 | | 最大队列 | 500 条 | 超出后丢弃最旧的 | | 持久化范围 | `fatal` / `error` | 发送失败写入磁盘,下次启动补发;`warning` 及以下仅内存队列 | | 重试 | 1 次 | 发送失败重试 1 次,失败后重新入队 | | 幂等保障 | `eventId` | 每条 Event 携带 UUID,服务端按此去重,重试不会产生重复数据 | --- ## 12. 数据库 Schema ### log_issues — Issue 聚合表 ```sql CREATE TABLE log_issues ( id BIGINT AUTO_INCREMENT PRIMARY KEY, app_key VARCHAR(64) NOT NULL, fingerprint VARCHAR(128) NOT NULL, level VARCHAR(20) NOT NULL DEFAULT 'error', status VARCHAR(20) NOT NULL DEFAULT 'open', -- open | resolved | ignored assignee VARCHAR(200), title VARCHAR(500), first_seen_at DATETIME NOT NULL, last_seen_at DATETIME NOT NULL, count INT NOT NULL DEFAULT 1, affected_users INT NOT NULL DEFAULT 0, platform VARCHAR(30), release VARCHAR(50), UNIQUE KEY uk_app_fingerprint (app_key, fingerprint), INDEX idx_app_key_last_seen (app_key, status, last_seen_at), INDEX idx_app_key_level (app_key, level, last_seen_at) ); ``` ### log_issue_events — Issue 事件明细表 ```sql CREATE TABLE log_issue_events ( id BIGINT AUTO_INCREMENT PRIMARY KEY, issue_id BIGINT NOT NULL, event_id VARCHAR(64) NOT NULL, -- 客户端幂等 ID app_key VARCHAR(64) NOT NULL, level VARCHAR(20) NOT NULL DEFAULT 'error', user_id VARCHAR(128), session_id VARCHAR(128), exception_type VARCHAR(200), exception_value TEXT, stack LONGTEXT, stack_symbolicated LONGTEXT, breadcrumbs JSON, tags JSON, device JSON, platform VARCHAR(30), release VARCHAR(50), environment VARCHAR(50) DEFAULT 'production', sdk_name VARCHAR(100), sdk_version VARCHAR(20), created_at DATETIME NOT NULL, UNIQUE KEY uk_event_id (app_key, event_id), INDEX idx_issue_created (issue_id, created_at), INDEX idx_app_key (app_key) ); ``` ### log_events — 自定义事件表 ```sql CREATE TABLE log_events ( id BIGINT AUTO_INCREMENT PRIMARY KEY, event_id VARCHAR(64) NOT NULL, -- 客户端幂等 ID app_key VARCHAR(64) NOT NULL, name VARCHAR(200) NOT NULL, user_id VARCHAR(128), session_id VARCHAR(128), properties JSON, platform VARCHAR(30), release VARCHAR(50), environment VARCHAR(50) DEFAULT 'production', device JSON, sdk_name VARCHAR(100), sdk_version VARCHAR(20), created_at DATETIME NOT NULL, UNIQUE KEY uk_event_id (app_key, event_id), INDEX idx_app_key_name (app_key, name), INDEX idx_app_key_user (app_key, user_id), INDEX idx_app_key_created (app_key, created_at) ); ``` ### log_sessions — Session 表 ```sql CREATE TABLE log_sessions ( id BIGINT AUTO_INCREMENT PRIMARY KEY, session_id VARCHAR(64) NOT NULL, app_key VARCHAR(64) NOT NULL, status VARCHAR(20) NOT NULL, -- ok | exited | crashed | abnormal user_id VARCHAR(128), platform VARCHAR(30), release VARCHAR(50), environment VARCHAR(50) DEFAULT 'production', started_at DATETIME NOT NULL, duration_ms BIGINT, errors INT NOT NULL DEFAULT 0, created_at DATETIME NOT NULL, UNIQUE KEY uk_session_id (app_key, session_id), INDEX idx_app_key_started (app_key, started_at), INDEX idx_app_key_status (app_key, status, started_at) ); ``` ### sourcemaps — 符号文件表 ```sql CREATE TABLE sourcemaps ( id BIGINT AUTO_INCREMENT PRIMARY KEY, app_key VARCHAR(64) NOT NULL, platform VARCHAR(30) NOT NULL, app_version VARCHAR(50) NOT NULL, bundle_name VARCHAR(100) DEFAULT 'index', file_path VARCHAR(500) NOT NULL, uploaded_at DATETIME NOT NULL, UNIQUE KEY uk_app_version_bundle (app_key, platform, app_version, bundle_name) ); ``` --- ## 13. 变更日志 ### v1.1.0(2026-06-17) **New Features:** - 上报认证:明确认证链路,`appKey` 由 SDK 从 `config.xuqm` 自动读取;服务端新增 BugCollect 服务激活校验(`t_feature_service.enabled`),未开通的 appKey 请求返回 `403 / 40301` - 幂等保障:IssueEvent / LogEvent 新增必填 `eventId`(UUID),服务端按此去重 - Breadcrumbs:IssueEvent 新增 `breadcrumbs` 数组,携带崩溃前最近 50 条操作轨迹 - device 字段扩充:新增 `locale`、`timezone`、`network`、`isEmulator`、`freeMemoryMb`、`buildType` - Session 上报:新增 `POST /bugcollect/v1/sessions/batch`,支持 crash-free session rate 计算 - 符号化扩展:SourceMap 上传新增 `android`(ProGuard mapping)、`ios`(dSYM)平台支持 - Issue 管理端点:新增 resolve / ignore / assign / bulk 操作 - Issue 状态:`isResolved: boolean` 替换为 `status: open | resolved | ignored`,新增 `affectedUsers` 字段 - Issue 详情拆分:`GET /issues/{id}` 不再内嵌 Event 列表,新增 `GET /issues/{id}/events` 分页接口 - Issue 趋势:新增 `GET /issues/{id}/trend` - Webhook 事件扩充:新增 `issue.fatal`、`issue.threshold`、`issue.resolved`;回调体新增签名校验头 - 概览统计:新增 `crashFreeSessionRate`、`openIssues`、`affectedUsers` - 全文搜索:Issue 列表新增 `q` 参数 - 版本筛选:Issue 列表新增 `release` 参数 - Environment:新增 `integration` 枚举值 - 数据库:新增 `log_sessions` 表;`log_issues` 新增 `status`/`affected_users`/`assignee` 列;`log_issue_events` 新增 `event_id`/`breadcrumbs` 列;补全缺失索引 **Breaking Changes:** - `isResolved` 字段已移除,改用 `status`(`isResolved: false` → `status: "open"`,`isResolved: true` → `status: "resolved"`) - Issue 详情响应不再包含 `events` 数组,需改用 `GET /issues/{id}/events` - 每条 Event 现在必须携带 `eventId`;缺失时返回 `400 / 40006` - BugCollect 服务未激活的 appKey 上报请求返回 `403 / 40301` **Bug Fixes:** - 修复 Fingerprint 算法中包含 `level` 字段的问题:相同堆栈的 `error` 和 `warning` 现在归为同一 Issue - 修复 Issue 列表查询参数使用废弃的 `type` 字段问题,统一改为 `level` - 修复 SDK 集成指南中 `BugCollect.setUser()` 错误引用,统一改为 `XuqmSDK.setUserInfo()` - 修复 Issue 详情示例响应中 `metadata` 字段未同步改名为 `tags` 的问题 ### v1.0.0(2026-06-17) **Breaking Changes:** - 上报端点路径从 `/log/v1/` 统一为 `/bugcollect/v1/` - Issue 上报请求体从裸 JSON 数组改为信封格式 `{ "sentAt", "sdk", "events" }` - Issue 事件中 `type` 字段(`android_error` / `native_crash`)替换为 `level` 字段(`fatal` / `error` / `warning` / `info` / `debug`) - Issue 事件中 `message` + `stack` 平铺字段归入 `exception` 对象(`exception.type` / `exception.value` / `exception.stacktrace`) - Issue 事件中 `metadata` 重命名为 `tags` - Issue 事件中 `appVersion` 重命名为 `release` - Analytics 事件请求体同步改为信封格式 **New Features:** - Issue 事件新增 `user`(用户信息)、`device`(设备信息)、`sdk`(SDK 自标识)字段 - Issue 事件新增 `environment` 字段(`production` / `staging` / `development`) - Analytics 事件新增 `user`、`device`、`sdk`、`environment` 字段 - 信封层新增 `sentAt`(时钟偏差校正)和 `sdk`(SDK 版本追踪) - 查询端点新增 `level` 筛选参数