@blueking/open-telemetry
v0.0.30
Published
OpenTelemetry browser SDK for BK RUM
Readme
@blueking/open-telemetry
面向浏览器真实用户监控(RUM)的 OpenTelemetry SDK。它把 Session、View、Fetch/XHR、静态资源、错误、Web Vitals、Action、白屏、Long Task、WebSocket、CSP 与设备上下文建模为原始 Span,并通过 OTLP/HTTP JSON 上报。
SDK 只创建一个 WebTracerProvider:
Browser events
→ Session / View / Action runtime context
→ RUM Span
→ Session sampling + context/filter hooks
→ Simple/BatchSpanProcessor
→ POST <transport.endpoint>/v1/rum不创建 Metric/Log Provider,不采集 HTTP Body 或 WebSocket message,也不在浏览器做计数、分位数、错误分组等聚合;这些工作由服务端从原始 Span 派生。
安装
pnpm add @blueking/open-telemetryimport { BkOpenTelemetry } from '@blueking/open-telemetry';
export const rum = new BkOpenTelemetry({
application: {
name: 'order-center',
environment: 'production',
version: '2026.07.21',
},
transport: {
endpoint: 'https://rum-collector.example.com',
token: 'limited-browser-write-token',
},
});application.name 和 transport.endpoint 必填。endpoint 是根地址,SDK 去掉末尾斜杠后固定追加 /v1/rum。构造后默认自动启动。
默认能力
| 能力 | 默认 | 浏览器来源 | 主要产出 |
| --- | --- | --- | --- |
| Session | 基础能力 | localStorage、用户活动、visibility | browser.session start/rotate/end |
| View / Router | auto | Navigation Timing、History、hash、BFCache | browser.view start/update/end |
| View Loading Time | 开 | 请求、DOM、资源、window.open activity | view.loading_time |
| Fetch/XHR | 开 | fetch、XMLHttpRequest、Resource Timing | browser.resource CLIENT |
| 静态资源 | 开 | Resource PerformanceObserver | browser.resource CLIENT |
| Error | 开 | error、unhandledrejection、资源加载错误 | 三类 Error Span + exception event |
| Web Vitals | 开 | web-vitals / PerformanceObserver | CLS/FCP/INP/LCP/TTFB Span |
| Action | 开 | click 与 activity | action.click、挫败信号 |
| Blank Screen | 开 | 页面稳定 + DOM 九点采样 | browser.blank_screen |
| Device | 开 | UA/UA-CH、Screen、Network Information | Resource 与运行时属性 |
| Long Task | 关 | Long Animation Frame / Long Tasks | browser.long_task |
| WebSocket | 关 | WebSocket 生命周期 | connect/open/error/close Span |
| CSP | 关 | securitypolicyviolation | csp.violation |
Session 和 View 是普通 Span 的必要关联边界,不能通过一个 tracking.session: false 关闭。精确默认值、降级行为和成本见能力矩阵。
推荐生产配置
export const rum = new BkOpenTelemetry({
application: {
name: 'order-center',
environment: 'production',
version: window.__APP_VERSION__,
},
transport: {
endpoint: 'https://rum-collector.example.com',
token: window.__RUM_WRITE_TOKEN__,
processor: 'batch',
timeoutMillis: 10_000,
},
session: {
sampleRate: 0.2,
},
tracking: {
view: {
getUrlTemplate(rawUrl) {
return new URL(rawUrl, location.href).pathname.replace(/\/orders\/[^/]+/, '/orders/:id');
},
excludedActivityUrls: [/\/events\/stream(?:\?|$)/],
},
request: {
excludedUrls: [/\/health(?:\?|$)/],
allowedTracingUrls: [
rawUrl => new URL(rawUrl, location.href).origin === 'https://api.example.com',
],
},
blankScreen: {
rootSelector: '#app',
},
},
context: {
user: { id: window.__USER__?.rumId },
attributes: {
page: () => ({ 'biz.module': 'order' }),
},
},
privacy: {
redactAttributes(attributes) {
const next = { ...attributes };
delete next['biz.secret'];
return next;
},
},
});生产配置至少需要确认:
- 动态路由已转成低基数
view.url_template; - URL path、user ID、错误和业务属性已完成隐私评审;
- Session 采样率有访问量和平均 Span/Session 的容量依据;
- 只向明确的自有 API 注入
traceparent,且 CORS 已允许该 header; - 流式/长轮询请求不会无限延长 View loading time;
- SPA 的白屏 root 指向真实挂载节点;
- 同一 window 只有一个 SDK 实例;
- Collector 接受 OTLP JSON,并对
/v1/rum、鉴权、CORS、body limit 做过真实浏览器验证。
上报协议
最终请求:
POST <endpoint>/v1/rum
Content-Type: application/json
Authorization: Bearer <token> # 配置 token 时
{
"resourceSpans": [
{
"resource": { "attributes": [...] },
"scopeSpans": [
{
"scope": { "name": "bk-rum" },
"spans": [...]
}
]
}
]
}当前使用 @opentelemetry/[email protected] 的浏览器 JSON exporter:默认请求超时 10 秒、并发出口上限 30、不压缩;429/502/503/504 和部分网络错误可重试,并支持 Retry-After。
默认 BatchSpanProcessor:queue 2048、batch 512、delay 5000ms、export timeout 30000ms。队列满后新结束的 Span 被丢弃。详细重试、keepalive、flush 与 CORS 规则见上报协议、批处理与采样。
Session 采样与导出条件
session: {
sampleRate: 0.2,
}- 范围 0–1,默认 1,越界截断;
- 根据
application.name + session.id稳定 hash; - 同一 Session 的决定一致,轮换后重新计算;
- 未采样时
startSpan()返回 non-recording Span,手动事件不导出; - 普通 Span 还必须有有效
session.id与view.id; browser.session是允许没有 View 的例外;- SDK 自身
/v1/rum请求、显式 discard 和 beforeSend 返回 false 的 Span 会被过滤。
手动事件
rum.reportAction({
name: 'submit-order',
attributes: { 'biz.order.channel': 'web' },
});
rum.reportCustomEvent({
name: 'checkout_completed',
attributes: { 'biz.payment.method': 'card' },
});
rum.reportCustomEvent({
name: 'checkout_failed',
error: new Error('payment rejected'),
});reportAction()立即创建action.custom,不是持续关联后续请求的自动 Action;reportCustomEvent()创建custom.<name>;带 Error 时记录 exception 和 ERROR status;- name 是检索维度,必须有限枚举,不能拼用户/订单/随机 ID;
- 两者都受当前 Session 采样约束。
默认 autoStart: true 是异步启动,构造后立即同步调用的手动事件不会排队,可能因为 View 尚未建立而丢失。首个手动事件不能丢时,使用 autoStart: false,先 await rum.start(),manual View 再调用 startView(),之后才开放业务上报。
用户和 View
rum.setUser({ id: 'stable-safe-user-id' });
rum.setUser({ id: undefined });
rum.setViewName('order-detail');
rum.setViewLoadingTime();SDK 不 hash user.id。View 默认自动监听 History/hash;query-only 变化不建新 View。
需要完全由 Router 控制时:
const rum = new BkOpenTelemetry({
application: { name: 'manual-view-app' },
transport: { endpoint: 'https://rum-collector.example.com' },
autoStart: false,
tracking: { view: { tracking: 'manual' } },
});
await rum.start();
rum.startView({ name: 'order-detail', url: location.href });先 start 建立 Session,再 startView;不要同时使用自动和手动路由边界。Vue、React、Hash Router、SSR 与微前端示例见框架与路由集成。
Consent
const rum = new BkOpenTelemetry({
application: { name: 'consent-app' },
transport: { endpoint: 'https://rum-collector.example.com' },
privacy: { trackingConsent: 'not-granted' },
});
await rum.setTrackingConsent('granted');
await rum.setTrackingConsent('not-granted');未授权时不安装插件/instrumentation。撤销会关闭采集、清理 Session 上下文并 flush 已结束的队列数据,但不会删除服务端已经接收的数据,也不会删除 localStorage 中仍有效的 Session 记录;再次授权后可能复用该 Session。
trackingConsent: 'not-granted' 控制的是采集和发送,不会推迟构造阶段的所有浏览器读取:只要 tracking.device !== false,new BkOpenTelemetry() 仍会读取 UA/UA-CH、内存和逻辑处理器等 Device Resource 快照,但此时不会发送。合规要求“授权前不能读取”时,应在授权后再构造实例。完整边界见隐私、Consent 与字段治理。
扩展
SDK 扩展分两类:
const rum = new BkOpenTelemetry({
application: { name: 'extensible-app' },
transport: { endpoint: 'https://rum-collector.example.com' },
autoStart: false,
});
rum.use(myRumPlugin);
rum.useInstrumentation(myOtelInstrumentation);
await rum.start();BkOTPlugin获得 Session/View/Action runtime context、脱敏、活动和startSpan()能力;- OTel instrumentation 绑定到 SDK 私有 WebTracerProvider,但它的 Span 仍须通过 RUM 上下文过滤;
use()/useInstrumentation()只能在首次 start 前调用;- Consent 撤销和 shutdown 会关闭扩展。
契约、生命周期和历史时间关联见插件与 OpenTelemetry 扩展。
隐私 Hook 只处理 Resource/Span 的 attributes。recordException() 产生的 exception message/stacktrace 不对 tracking.beforeSend 暴露;敏感异常内容必须在产生前避免,或在 Collector 端二次清洗。
SDK 默认不附加独立页面属性;页面地址使用经过 redactUrl 处理的 view.url,低基数路由聚合使用 view.url_template。业务需要额外页面维度时,可通过 context.attributes.page 添加。
数据模型和主要字段
OTLP 字段分五层:
- Resource:
service.name、环境、版本、SDK、设备和session.sample_rate; - Scope:instrumentation name/version;
- Span envelope:trace/span ID、时间、kind、status;
- RUM attributes:Session/View/Action 关联、
event.*、span_type、result等; - Span events:exception、Long Task script/attribution。
主要分类:
| Span | 关键字段 |
| --- | --- |
| browser.session | phase、session.id/start_time、reason、previous_id |
| browser.view | phase、version、name、URL group、loading type/time、navigation timings |
| browser.resource | URL、method/status、resource type、size/protocol、DNS/TCP/TLS/TTFB/download |
| Error spans | message/source/handled、code location、exception event |
| browser.web_vital | metric/value/rating 与 attribution |
| action.click | action ID/name/target/loading time/frustration |
| browser.blank_screen | reason/score/sample counts/root/center element |
| browser.long_task | duration/blocking、entry type、script/attribution events |
调试
const rum = new BkOpenTelemetry({
application: { name: 'debug-app' },
transport: {
endpoint: 'https://rum-collector.example.com',
processor: 'simple',
},
session: { sampleRate: 1 },
debug: true,
autoStart: false,
});
await rum.start();
console.log({
sampled: rum.isSampled(),
session: rum.isSessionActive(),
view: rum.isViewActive(),
attributes: rum.getRuntimeAttributes(),
});
rum.reportCustomEvent({ name: 'rum_probe' });
await rum.flush();如果有 debug Span、没有请求,检查 CORS/endpoint/exporter;如果没有 Span,检查 Consent、启动、采样、Session/View、能力开关和 beforeSend。完整诊断树见调试与排障。
文档导航
| 任务 | 文档 | | --- | --- | | 首次安装和验证 | 快速上手 | | 上线检查和容量/隐私/CORS | 生产接入指南 | | 判断支持范围、默认开关和降级 | 能力矩阵 | | Vue/React/Router/SSR/微前端 | 框架与路由集成 | | 理解模块、启动、过滤和导出 | 架构与数据流 | | 理解 OTLP 层级和字段归属 | RUM 数据模型 | | 查全部配置和默认值 | 配置参考 | | 查类、方法、类型和扩展 API | API 参考 | | 查 Span 名和逐字段定义 | 遥测字段字典 | | 查协议、batch、重试和采样 | 上报协议、批处理与采样 | | 查 Consent 和脱敏边界 | 隐私、Consent 与字段治理 | | 解决无数据、重复、缺字段和误报 | 调试与排障 |
