npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@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-telemetry
import { 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.nametransport.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;
    },
  },
});

生产配置至少需要确认:

  1. 动态路由已转成低基数 view.url_template
  2. URL path、user ID、错误和业务属性已完成隐私评审;
  3. Session 采样率有访问量和平均 Span/Session 的容量依据;
  4. 只向明确的自有 API 注入 traceparent,且 CORS 已允许该 header;
  5. 流式/长轮询请求不会无限延长 View loading time;
  6. SPA 的白屏 root 指向真实挂载节点;
  7. 同一 window 只有一个 SDK 实例;
  8. 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.idview.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 !== falsenew 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 字段分五层:

  1. Resource:service.name、环境、版本、SDK、设备和 session.sample_rate
  2. Scope:instrumentation name/version;
  3. Span envelope:trace/span ID、时间、kind、status;
  4. RUM attributes:Session/View/Action 关联、event.*span_typeresult 等;
  5. 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 |

完整层级见RUM 数据模型,所有精确字段见遥测字段字典

调试

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 与字段治理 | | 解决无数据、重复、缺字段和误报 | 调试与排障 |