- LogController: 新增 /issues/{id}/events, /issues/{id}/trend, 管理接口
(resolve/ignore/assign/bulk),queryIssues 改用 level/status/q 参数
- LogService: 全面重写,eventId 幂等、breadcrumbs 存储、affectedUsers 计数、
Issue status 管理、getIssueEvents/getIssueTrend 独立接口
- Entity: LogIssueEventEntity 增加 eventId/exceptionType/exceptionValue/breadcrumbs;
LogIssueEntity 增加 status/affectedUsers/assignee;LogEventEntity 增加 eventId
- Repository: LogIssueEventRepository/LogIssueRepository/LogEventRepository 新增
idempotency/filter/trend/bulk 查询方法
- DTO: IssueActionRequest/IssueTrendResponse 新增;IssueResponse/IssueEventResponse 扩展
- V3 migration: log_issue_events/log_issues/log_events 结构升级
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1438 行
40 KiB
Markdown
1438 行
40 KiB
Markdown
# 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<T>`。
|
||
|
||
### 通用响应格式
|
||
|
||
```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=<hmac>` 请求头,接收方用 `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` 筛选参数
|