当用户反馈“页面打开很慢”时,前端工程师手里通常只有 Performance 面板和几个接口耗时日志。可一旦请求经过网关、鉴权服务、订单服务再到数据库,任何一个环节的抖动都会被前端完整感知,却很难被前端完整定位。Lightstep 这类分布式跟踪平台的价值就在这里:它把一次用户操作产生的所有调用串成一条链路,前端作为链路的起点,负责生成 trace id 并随着请求一路传播下去。本文以 Vue 3 项目为例,讲清楚接入 Lightstep 的完整工程化方案。

一、先理解原理:OpenTelemetry 与 trace context 传播
Lightstep 已经全面转向 OpenTelemetry(简称 OTel)作为标准采集协议,所以接入 Vue 3 的本质就是在浏览器端引入 OTel 的 Web SDK。OTel 的核心概念有三个:Trace 表示一次完整请求链路,Span 表示链路中的一个操作单元,Context 则负责把 trace 标识在服务之间传递。
浏览器端的特殊性在于它是整条链路的发起方。前端需要通过 W3C Trace Context 规范生成 traceparent 头,格式类似 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01,其中包含 trace id 和当前 span id。后端服务收到这个请求头后,会延续同一个 trace id 继续创建下游 span,这样 Lightstep 平台上就能看到一条从用户点击按钮开始、贯穿所有微服务的完整瀑布图。
需要注意的是,浏览器发送自定义请求头会触发 CORS 预检,后端必须在响应中显式允许 traceparent 这个头部,否则跨域场景下链路会直接断掉,这是实践中最常见的坑之一。
二、Vue 3 项目中的代码落地
先安装依赖。OTel 的 Web 包拆分得比较细,按需引入即可:
npm install @opentelemetry/api \ @opentelemetry/sdk-trace-web \ @opentelemetry/exporter-trace-otlp-http \ @opentelemetry/instrumentation \ @opentelemetry/instrumentation-fetch \ @opentelemetry/instrumentation-document-load \ @opentelemetry/context-zone
接下来在项目入口文件初始化 Provider。Exporter 指向 Lightstep 的 OTLP HTTP 端点,鉴权用的是 Lightstep 控制台里创建的 Access Token:
// src/tracing.js
import { WebTracerProvider } from '@opentelemetry/sdk-trace-web';
import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-base';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
import { FetchInstrumentation } from '@opentelemetry/instrumentation-fetch';
import { DocumentLoadInstrumentation } from '@opentelemetry/instrumentation-document-load';
import { registerInstrumentations } from '@opentelemetry/instrumentation';
import { ZoneContextManager } from '@opentelemetry/context-zone';
import { W3CTraceContextPropagator } from '@opentelemetry/core';
const exporter = new OTLPTraceExporter({
url: 'https://ingest.lightstep.com/traces/otlp-http/v1/traces',
headers: {
'lightstep-access-token': '你的AccessToken'
}
});
const provider = new WebTracerProvider();
provider.addSpanProcessor(new BatchSpanProcessor(exporter));
provider.register({
contextManager: new ZoneContextManager(),
propagator: new W3CTraceContextPropagator()
});
registerInstrumentations({
instrumentations: [
new DocumentLoadInstrumentation(), // 自动采集页面加载
new FetchInstrumentation({
propagateTraceHeaderCorsUrls: [/https:\/\/api\.ipipp\.com\/.*/]
})
]
});在 main.js 中引入这个文件后,所有通过 fetch 发出的请求都会自动携带 traceparent 头。由于 ZoneContextManager 基于 Zone.js 实现,异步操作之间的上下文不会丢失,这对 Vue 3 大量使用的 async setup 和组合式函数非常关键。如果你用的是 Zone.js 的边缘版本,建议确认它与 Vue 的响应式系统没有冲突,官方推荐使用 patched 过的 zone.js 0.11 以上版本。
三、结合 Vue Router 与业务埋点细化 Span
自动埋点只能覆盖请求层面,用户视角的“页面切换耗时”需要手动补 Span。Vue Router 提供了很好的挂载点:
// src/router/index.js
import { trace } from '@opentelemetry/api';
import { tracer } from './tracing';
router.beforeEach((to, from) => {
const span = tracer.startSpan(`route:${to.path}`, {
attributes: {
'vue.route.from': from.fullPath,
'vue.route.to': to.fullPath
}
});
// 存到路由 meta 上, afterEach 时结束
to.meta.span = span;
});
router.afterEach((to) => {
to.meta.span?.end();
});除了路由,业务关键动作也可以埋点,比如提交订单、文件上传。手动 Span 的命名建议带上业务语义前缀,例如 order:submit、upload:avatar,这样在 Lightstep 中按 operation name 聚合分析时,能直接对齐到产品指标。属性字段尽量使用结构化的键值对,避免把大段 JSON 塞进 attribute,会显著增加上报体积。
对于 Pinia 中的异步 action,可以在 action 内部创建子 Span,把接口耗时和状态更新耗时分开统计,排查“接口快但页面慢”这类问题时非常有用。
四、生产环境的采样、性能与成本控制
全量上报在生产环境通常不可取。OTel 支持配置 Sampler,常用的做法是错误请求全量采样、正常请求按比例采样:
import { ParentBasedSampler, TraceIdRatioBasedSampler, AlwaysOnSampler } from '@opentelemetry/core';
provider.register({
sampler: new ParentBasedSampler({
root: new TraceIdRatioBasedSampler(0.1) // 根请求采样 10%
})
});第二个要处理的问题是代码体积与构建集成。Web SDK 打包后大约增加 40KB gzip 左右,建议通过动态 import 延迟初始化,让首屏渲染不受影响。同时由于生产代码经过压缩,报错和 Span 中的堆栈会指向压缩后的位置,需要把 Sourcemap 上传到 Lightstep,才能在链路详情里直接看到原始代码行号。Vite 项目可以在构建时生成 Sourcemap,再通过 Lightstep 的 CLI 或 CI 脚本批量上传,上传完成后记得删除构建产物中的 map 文件,避免泄漏源码。
最后一个建议是建立“前端慢请求看板”。在 Lightstep 中以 operation 维度创建仪表盘,把 P95 耗时、错误率与后端服务的对应指标放在同一视图里,前后端共用同一条链路数据,扯皮式的排障会减少很多。分布式跟踪真正的收益不是多几个图表,而是让前端第一次拥有了穿透后端黑盒的能力。
Vue 3分布式跟踪Lightstep前端监控OpenTelemetry修改时间:2026-09-08 15:57:09