light-sentry-sdk/docs/07-tracing.md

349 lines
10 KiB
Markdown
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.

# 分布式追踪Distributed Tracing设计文档
## 一、背景与目标
### 1.1 背景
当前 SDK 已支持错误采集、性能指标Web Vitals、网络请求监控、用户行为采集等能力但缺乏**前后端链路打通**的分布式追踪能力。当用户遇到"接口慢"或"报错"时,无法关联前端操作与后端服务的内部调用链路,排查问题效率低。
### 1.2 目标
- **第一阶段P0**:实现 Trace/Span 核心模型 + 前后端 Trace 传播 + 手动 API
- **第二阶段P1**:自动 InstrumentationPage Load、路由变化、用户交互
- **第三阶段P2**:采样策略升级 + Baggage 支持 + 完整开发者体验
---
## 二、核心概念
### 2.1 Trace
一条完整的分布式调用链路,全局唯一 `trace_id` 标识。一条 Trace 跨越多个服务、多个进程。
### 2.2 Transaction
Trace 中的"根 Span",代表一个完整的用户操作或业务流程。前端常见的 Transaction 类型:
- `pageload`:页面加载
- `navigation`路由切换SPA
- `user-interaction`:用户交互(点击、提交等)
- `custom`:自定义事务
### 2.3 Span
Transaction 中的具体操作步骤,有父子关系。常见 Span 类型:
- `http`HTTP 请求
- `resource`:资源加载
- `browser`浏览器内部操作DOM 解析、脚本执行等)
- `db`:数据库查询(后端)
- `middleware`:中间件(后端)
### 2.4 Span 关系
```
Trace (trace_id = abc123)
└── Transaction: "page-load" (span_id = s1)
├── Span: "http GET /api/user" (span_id = s2, parent = s1)
│ └── 后端服务的多个 Span通过 trace header 关联)
├── Span: "resource /static/app.js" (span_id = s3, parent = s1)
└── Span: "dom-parse" (span_id = s4, parent = s1)
```
---
## 三、数据模型设计
### 3.1 Span 接口
```typescript
interface Span {
trace_id: string; // 16/32 位十六进制,全局唯一
span_id: string; // 16 位十六进制Span 唯一
parent_span_id?: string; // 父 Span ID
op?: string; // 操作类型http、resource、browser 等
description?: string; // 描述信息
start_timestamp: number; // 开始时间(毫秒时间戳)
timestamp: number; // 结束时间(毫秒时间戳)
status?: SpanStatus; // 状态
tags?: Record<string, string>; // 标签
data?: Record<string, unknown>; // 附加数据
same_process_as_parent?: boolean;
}
type SpanStatus =
| 'ok'
| 'cancelled'
| 'unknown'
| 'invalid_argument'
| 'deadline_exceeded'
| 'not_found'
| 'already_exists'
| 'permission_denied'
| 'resource_exhausted'
| 'failed_precondition'
| 'aborted'
| 'out_of_range'
| 'unimplemented'
| 'internal_error'
| 'unavailable'
| 'data_loss'
| 'unauthenticated';
```
### 3.2 Transaction 接口
```typescript
interface Transaction extends Span {
name: string; // 事务名称(如 "GET /api/users"
spans: Span[]; // 子 Span 列表
transaction: string; // 事务名称冗余字段
type?: string; // 事务类型page-load、navigation 等
sampled?: boolean; // 是否采样
}
```
### 3.3 上报事件类型
新增 `transaction` 事件类型:
```typescript
interface TransactionEvent extends BaseEvent {
type: 'transaction';
transaction: string;
spans: Span[];
start_timestamp: number;
timestamp: number;
// ...
}
```
---
## 四、前后端 Trace 传播
### 4.1 协议选择
采用 **Sentry Trace 协议**`sentry-trace` header同时支持 W3C Trace Context 作为兼容。
### 4.2 sentry-trace Header 格式
```
sentry-trace: {trace_id}-{span_id}-{sampled}
```
- `trace_id`: 32 位十六进制字符串
- `span_id`: 16 位十六进制字符串
- `sampled`: 可选,`1` = 采样,`0` = 不采样
示例:
```
sentry-trace: 8839b8a3914c485bb1f52ce3c08c9c0b-b89e261f348c022b-1
```
### 4.3 baggage Header后续阶段
```
baggage: sentry-trace_id=xxx,sentry-public_key=xxx,sentry-sample_rate=1.0
```
### 4.4 追踪域名白名单
为避免向第三方接口泄漏 trace 信息,需要配置 `tracingOrigins`
```typescript
LightSentry.init({
dsn: '...',
tracingOrigins: [
'api.example.com',
/^https:\/\/internal\./,
'localhost',
],
});
```
默认匹配同源域名。
---
## 五、核心模块设计
### 5.1 TracingManager
职责:
- 维护当前活跃的 Transaction
- 维护 Span 栈(当前执行上下文)
- 生成 trace_id / span_id
- 管理 Span 的父子关系
关键方法:
```typescript
class TracingManager {
startTransaction(context: TransactionContext): Transaction;
getCurrentTransaction(): Transaction | null;
setCurrentTransaction(transaction: Transaction | null): void;
startSpan(context: SpanContext): Span | null;
getCurrentSpan(): Span | null;
finishSpan(span: Span): void;
}
```
### 5.2 Span 类
```typescript
class Span {
traceId: string;
spanId: string;
parentSpanId?: string;
op?: string;
description?: string;
startTimestamp: number;
endTimestamp?: number;
status?: string;
tags: Record<string, string>;
data: Record<string, unknown>;
setTag(key: string, value: string): this;
setData(key: string, value: unknown): this;
setStatus(status: string): this;
startChild(context: SpanContext): Span;
finish(endTimestamp?: number): void;
toJSON(): Span;
toTraceparent(): string; // 生成 sentry-trace header 值
}
```
### 5.3 Transaction 类
```typescript
class Transaction extends Span {
name: string;
spans: Span[];
type?: string;
sampled?: boolean;
finish(endTimestamp?: number): void;
toJSON(): Transaction;
}
```
---
## 六、插件改造
### 6.1 NetworkPlugin 改造
**改造前**:上报独立的 `network` 事件
**改造后**:创建 `http` Span关联到当前 Transaction并注入 trace header
具体改动:
1. 请求开始时,如果存在活跃 Transaction创建 Span
2. 向匹配的域名注入 `sentry-trace` header
3. 请求结束时,设置 Span status 和 datafinish Span
4. 保持原有 `network` 事件上报(兼容)
### 6.2 PerformancePlugin 改造(第二阶段)
改造为 Page Load Transaction
- Navigation Timing → 拆解为多个 Span
- Resource Timing → 作为 resource Span
- Web Vitals → 作为标记点或 measurement
### 6.3 新增 RouterPlugin第二阶段
监听路由变化:
- History APIpushState / replaceState / popstate
- hashchange
- 每次路由变化创建新的 navigation Transaction
---
## 七、对外 API
### 7.1 顶层 API
```typescript
// 开始事务
LightSentry.startTransaction({
name: 'search-products',
op: 'user-interaction',
});
// 获取当前 Span
const span = LightSentry.getCurrentSpan();
// 创建子 Span基于当前活跃 Span
const childSpan = LightSentry.startSpan({
op: 'http',
description: 'GET /api/search',
});
// 手动设置 Span
LightSentry.setSpan(span);
```
### 7.2 配置项
```typescript
interface LightConfig {
// ...
tracesSampleRate?: number; // 全局 Trace 采样率 0~1默认 0
tracesSampler?: (context: any) => number; // 动态采样函数
tracingOrigins?: (string | RegExp)[]; // 追踪域名白名单
tracePropagationTargets?: (string | RegExp)[]; // trace header 传播目标
}
```
---
## 八、采样策略(第一阶段简化版)
第一阶段采用简单策略:
1. 配置 `tracesSampleRate`(默认 0 = 关闭)
2. Transaction 创建时决定是否采样
3. 采样决策通过 `sentry-trace``sampled` 标志传递给后端
4. 未采样的 Transaction 不上报,但仍传递 trace_id用于关联
---
## 九、实施计划
### 第一阶段:核心能力(已完成)
- [x] 设计文档
- [x] 新增 Trace/Span 类型定义
- [x] 实现 Span 类
- [x] 实现 Transaction 类
- [x] 实现 TracingManager
- [x] 改造 NetworkPlugin注入 trace header + 创建 HTTP Span
- [x] 对外暴露 startTransaction / startSpan API
- [x] 集成到 Client
- [x] 编译验证
- [x] 错误事件自动关联 Trace 上下文
- [x] Transaction 完成后自动上报
- [x] Span 栈生命周期管理
- [x] 未采样 Transaction 不收集 Span性能优化
### 第二阶段:自动 Instrumentation已完成
- [x] PerformancePlugin 改造Page Load Transaction
- [x] Navigation Timing 拆解为 browser.* Span 树
- [x] Resource Timing 转为 resource.* Span
- [x] Web Vitals 作为 measurements 附加到 Transaction
- [x] LCP/FP/FCP 等关键指标标记
- [x] 新增 RouterPlugin路由变化追踪
- [x] History API 监听pushState / replaceState / popstate
- [x] Hash 路由监听hashchange
- [x] 路由切换期间的 Span 自动关联
- [x] 新增 InteractionPlugin用户交互追踪
- [x] 点击/表单提交等交互自动创建 Transaction
- [x] 交互期间的网络请求自动关联
- [x] Error 与 Trace 深度集成
- [x] 错误事件自动附加 contexts.trace
- [ ] 错误作为 error Span 加入当前 Transaction可选
### 第三阶段:进阶能力(已完成)
- [x] Baggage header 支持
- [x] Transaction 携带 user、release 等信息
- [x] NetworkPlugin 自动注入 baggage header
- [x] 后端可以读取 baggage 传递的信息
- [x] 动态采样 tracesSampler 实现
- [x] tracesSampler 函数支持
- [x] 根据 transaction 上下文动态决定采样率
- [x] 完整 Span Status 映射增强
- [x] 数据库操作状态映射dbStatusToSpanStatus
- [x] 自定义操作状态映射statusToSpanStatus
- [x] Span status 与 error 关联setStatusFromResponse
- [x] 错误作为 error Span 加入 Transaction可选功能
---
## 十、兼容性考虑
1. **向后兼容**:默认 `tracesSampleRate = 0`,即默认关闭,不影响现有功能
2. **事件类型新增**:新增 `transaction` 事件类型,后端需支持
3. **NetworkPlugin**:保持原有 `network` 事件上报,同时新增 Span可配置开关
4. **浏览器兼容性**:不依赖新 API使用现有能力实现
---
## 十一、参考资料
- [Sentry Tracing 文档](https://docs.sentry.io/product/sentry-basics/concepts/tracing/)
- [Sentry Performance 数据模型](https://develop.sentry.dev/sdk/performance/)
- [W3C Trace Context](https://www.w3.org/TR/trace-context/)