10 KiB
10 KiB
分布式追踪(Distributed Tracing)设计文档
一、背景与目标
1.1 背景
当前 SDK 已支持错误采集、性能指标(Web Vitals)、网络请求监控、用户行为采集等能力,但缺乏前后端链路打通的分布式追踪能力。当用户遇到"接口慢"或"报错"时,无法关联前端操作与后端服务的内部调用链路,排查问题效率低。
1.2 目标
- 第一阶段(P0):实现 Trace/Span 核心模型 + 前后端 Trace 传播 + 手动 API
- 第二阶段(P1):自动 Instrumentation(Page 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 接口
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 接口
interface Transaction extends Span {
name: string; // 事务名称(如 "GET /api/users")
spans: Span[]; // 子 Span 列表
transaction: string; // 事务名称冗余字段
type?: string; // 事务类型:page-load、navigation 等
sampled?: boolean; // 是否采样
}
3.3 上报事件类型
新增 transaction 事件类型:
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:
LightSentry.init({
dsn: '...',
tracingOrigins: [
'api.example.com',
/^https:\/\/internal\./,
'localhost',
],
});
默认匹配同源域名。
五、核心模块设计
5.1 TracingManager
职责:
- 维护当前活跃的 Transaction
- 维护 Span 栈(当前执行上下文)
- 生成 trace_id / span_id
- 管理 Span 的父子关系
关键方法:
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 类
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 类
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
具体改动:
- 请求开始时,如果存在活跃 Transaction,创建 Span
- 向匹配的域名注入
sentry-traceheader - 请求结束时,设置 Span status 和 data,finish Span
- 保持原有
network事件上报(兼容)
6.2 PerformancePlugin 改造(第二阶段)
改造为 Page Load Transaction:
- Navigation Timing → 拆解为多个 Span
- Resource Timing → 作为 resource Span
- Web Vitals → 作为标记点或 measurement
6.3 新增 RouterPlugin(第二阶段)
监听路由变化:
- History API(pushState / replaceState / popstate)
- hashchange
- 每次路由变化创建新的 navigation Transaction
七、对外 API
7.1 顶层 API
// 开始事务
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 配置项
interface LightConfig {
// ...
tracesSampleRate?: number; // 全局 Trace 采样率 0~1,默认 0
tracesSampler?: (context: any) => number; // 动态采样函数
tracingOrigins?: (string | RegExp)[]; // 追踪域名白名单
tracePropagationTargets?: (string | RegExp)[]; // trace header 传播目标
}
八、采样策略(第一阶段简化版)
第一阶段采用简单策略:
- 配置
tracesSampleRate(默认 0 = 关闭) - Transaction 创建时决定是否采样
- 采样决策通过
sentry-trace的sampled标志传递给后端 - 未采样的 Transaction 不上报,但仍传递 trace_id(用于关联)
九、实施计划
第一阶段:核心能力(已完成)
- 设计文档
- 新增 Trace/Span 类型定义
- 实现 Span 类
- 实现 Transaction 类
- 实现 TracingManager
- 改造 NetworkPlugin(注入 trace header + 创建 HTTP Span)
- 对外暴露 startTransaction / startSpan API
- 集成到 Client
- 编译验证
- 错误事件自动关联 Trace 上下文
- Transaction 完成后自动上报
- Span 栈生命周期管理
- 未采样 Transaction 不收集 Span(性能优化)
第二阶段:自动 Instrumentation(已完成)
- PerformancePlugin 改造:Page Load Transaction
- Navigation Timing 拆解为 browser.* Span 树
- Resource Timing 转为 resource.* Span
- Web Vitals 作为 measurements 附加到 Transaction
- LCP/FP/FCP 等关键指标标记
- 新增 RouterPlugin(路由变化追踪)
- History API 监听(pushState / replaceState / popstate)
- Hash 路由监听(hashchange)
- 路由切换期间的 Span 自动关联
- 新增 InteractionPlugin(用户交互追踪)
- 点击/表单提交等交互自动创建 Transaction
- 交互期间的网络请求自动关联
- Error 与 Trace 深度集成
- 错误事件自动附加 contexts.trace
- 错误作为 error Span 加入当前 Transaction(可选)
第三阶段:进阶能力(已完成)
- Baggage header 支持
- Transaction 携带 user、release 等信息
- NetworkPlugin 自动注入 baggage header
- 后端可以读取 baggage 传递的信息
- 动态采样 tracesSampler 实现
- tracesSampler 函数支持
- 根据 transaction 上下文动态决定采样率
- 完整 Span Status 映射增强
- 数据库操作状态映射(dbStatusToSpanStatus)
- 自定义操作状态映射(statusToSpanStatus)
- Span status 与 error 关联(setStatusFromResponse)
- 错误作为 error Span 加入 Transaction(可选功能)
十、兼容性考虑
- 向后兼容:默认
tracesSampleRate = 0,即默认关闭,不影响现有功能 - 事件类型新增:新增
transaction事件类型,后端需支持 - NetworkPlugin:保持原有
network事件上报,同时新增 Span(可配置开关) - 浏览器兼容性:不依赖新 API,使用现有能力实现