A backend-agnostic, privacy-first analytics SDK for modern browsers. Tracelet gives product code one typed API while keeping queues, codecs, compression and delivery behind replaceable contracts.
Live playground · Quickstart · SDK evaluation guide · Examples · API reference · 简体中文
- Backend-agnostic: send the same event model to your own HTTP collector or any custom
Transport. - Reliable in the browser: persist pending events in IndexedDB and coordinate shared queues across tabs.
- Privacy-aware: check consent before identities or events are created; autocapture never reads DOM text or form values.
- Type-safe: model event names and properties with a TypeScript event map.
- Replaceable by design: swap storage, routing, codecs, compression and plugins without changing product instrumentation.
npm install tracelet-browserimport {
HttpTransport,
createAnalytics,
createBrowserEventStorage,
} from "tracelet-browser";
interface Events {
order_submitted: { orderId: string; amount: number };
}
const eventStorage = await createBrowserEventStorage({
databaseName: "storefront-analytics",
onFallback(error) {
console.warn("IndexedDB unavailable; analytics will use memory", error);
},
});
const analytics = createAnalytics<Events>({
appId: "storefront",
eventStorage,
transport: new HttpTransport({
endpoint: "https://events.example.com/v1/batch",
}),
});
await analytics.track("order_submitted", {
orderId: "o_123",
amount: 199,
});Try this lifecycle without creating a backend in the interactive playground. Tracelet is framework-independent and tested in Chromium, Firefox and WebKit on every change.
Runnable starters:
Planning: 0.3 release readiness · Observability RFC · Compatibility matrix
Community: Support · Contributing · Code of Conduct · Accessibility · Security · Issues
The project is currently suitable for evaluation, prototypes and early production integrations with an explicit delivery validation plan. See the changelog before upgrading.
Tracelet 是一个后端无关、隐私优先的浏览器埋点 SDK。SDK 核心保持框架无关,React 官网作为独立 workspace 维护。
安装:
npm install tracelet-browser
pnpm add tracelet-browser
yarn add tracelet-browser
tracelet已在 npm 被其他项目占用,因此本项目使用tracelet-browser作为 npm 包名。
- 官网:tracelet-opal.vercel.app
- 在线体验:Playground
- 快速开始:Quickstart
- SDK 选型指南:TypeScript 前端埋点 SDK:从选型到上线
- 场景示例:Examples
- API 查询:Reference
- npm:tracelet-browser
- 源码:shishikami/Tracelet
当前版本见页面顶部的 npm badge,适合评估、原型和对投递行为有明确验证方案的早期项目。
Tracelet 以现代 Chrome、Edge、Firefox 和 Safari 为目标。核心逻辑、npm JavaScript 导入和 TypeScript 声明已经验证;Chromium、Firefox 和 WebKit 会在 CI 中运行真实浏览器测试。使用 CompressionStream 等可选浏览器能力前,仍应结合目标版本验证支持情况。
track / page / identify
-> standard event
-> plugins
-> event storage
-> event route
-> batch
-> codec
-> compression
-> transport
import {
BrowserLocalStorage,
HttpTransport,
createAnalytics,
createBrowserEventStorage,
} from "tracelet-browser";
interface Events {
order_submitted: {
orderId: string;
amount: number;
};
}
const eventStorage = await createBrowserEventStorage({
databaseName: "shop-analytics",
onFallback(error) {
console.warn("IndexedDB 不可用,埋点队列将退回内存", error);
},
});
const analytics = createAnalytics<Events>({
appId: "shop-web",
identityStorage: new BrowserLocalStorage(),
eventStorage,
transport: new HttpTransport({
endpoint: "https://events.example.com/v1/batch",
}),
});
await analytics.track("order_submitted", {
orderId: "o_123",
amount: 199,
});自动采集是可选插件,不会扫描 DOM,也不会读取元素文本、表单值或 data-* 属性。它目前只处理 SPA 页面浏览和浏览器全局错误:
import {
createAnalytics,
createAutoCapturePlugin,
} from "tracelet-browser";
const analytics = createAnalytics({
appId: "shop-web",
transport,
plugins: [
createAutoCapturePlugin({
pageViews: {
initial: true,
// Hash 可能包含敏感信息,仅 Hash Router 项目显式开启。
includeHash: false,
},
errors: {
// 堆栈可能包含文件路径或参数,因此默认关闭。
includeStack: false,
},
}),
],
});
await analytics.init();页面浏览通过 page() 事件进入统一管线。URL 上下文默认移除 query 和 hash;Hash Router 可以用 includeHash: true 开启 fragment 路由采集。window.error 生成 $error,未处理的 Promise 拒绝生成 $unhandled_rejection。它们都会遵守 Consent,调用 shutdown() 后会移除监听器并恢复被包装的 History 方法。
关键业务事实仍应使用显式 track(),例如支付成功或订单创建;自动采集适合补充页面访问和技术异常,而不是替代业务事件设计。
需要用户授权后才能采集时,将初始状态设为 pending:
const analytics = createAnalytics({
appId: "shop-web",
transport,
consent: {
initialState: "pending",
clearQueueOnDenied: true,
},
});
await analytics.track("ignored_before_consent", {});
await analytics.setConsent("granted");
await analytics.track("collected_after_consent", {});
// 撤回授权会清空未发送队列并重置身份。
await analytics.setConsent("denied");默认值仍为 granted,便于服务端已统一处理授权的项目保持兼容。
IndexedDbEventStorage按写入顺序持久化未发送事件。- 单事件默认限制为 64 KiB,编码后单批次默认限制为 256 KiB,可通过
maxEventBytes和maxBatchBytes调整。 visibilitychange和pagehide会触发最后一次 Flush。- Web Locks API 会协调同源多 Tab 的共享队列发送,避免并发 Flush 重复读取同一批事件。
- Session 默认在连续 30 分钟无事件后自动续期,可通过
sessionTimeoutMs调整。 - 网络、HTTP 408、429 和 5xx 使用指数退避与随机抖动重试。
- HTTP 4xx 等不可重试错误会从队列移除,防止永久阻塞。
- 每个事件拥有稳定
eventId,服务端应按该字段幂等去重。
可通过构造参数 onDiagnostic 或运行时 subscribeDiagnostics() 观察 SDK 自身状态,不会生成新的业务埋点。投递事件使用 attemptId 关联同一次发送:
const analytics = createAnalytics({
appId: "shop-web",
transport,
onDiagnostic(event) {
console.debug("[analytics]", event);
},
});
const unsubscribe = analytics.subscribeDiagnostics((event) => {
if (event.type === "flush_failed") {
console.warn(event.attemptId, event.status, event.retryable);
}
});
const snapshot = await analytics.getQueueSnapshot();
console.info(snapshot.pendingEvents, snapshot.delivery.phase);
unsubscribe();getQueueSnapshot() 返回只读的近似快照。pendingEvents 来自当前 EventStorage,oldestEventTimestamp 是事件时间而不是精确入队时间;delivery.phase 为 idle、sending 或 waiting-retry。诊断与快照都不会包含事件 Properties、用户 ID、请求体或鉴权头。
使用 IndexedDbEventStorage 时,可以读取浏览器提供的近似容量信息:
const estimate = await analytics.getStorageEstimate();
if (estimate && (estimate.usageRatio ?? 0) >= 0.8) {
console.warn("Origin storage usage is high", estimate);
}usageBytes、quotaBytes、availableBytes 和 usageRatio 都是整个 Origin 的估算值,可能包含应用自己的 IndexedDB、Cache Storage 等数据,并非 Tracelet 独占空间;浏览器也可能为了隐私而舍入或省略字段。当 IndexedDB 写入实际触发配额错误时,SDK 不会让 track() 影响业务流程,而是丢弃当前事件并发出 event_dropped: storage-quota 与 storage_quota_exceeded 诊断。SDK 不会擅自删除其他应用数据。
完整示例见 examples/browser.ts;官网 Quickstart 来自 examples/quickstart.ts,并在 CI 中执行 TypeScript 类型检查。
Tracelet 不要求框架 Wrapper。推荐在渲染过程之外创建一个应用级实例,并只把初始化连接到框架根生命周期:
- React:根部渲染
TraceletBootstrap,避免在普通组件卸载时关闭全局单例。 - Vue:在
app.mount()前注册traceletPlugin,组合式函数通过注入取得同一实例。 - Next.js:从根布局渲染带
"use client"的TraceletClient,避免在 Server Component 中初始化浏览器能力。
可运行代码位于 examples/frameworks,详细边界见 docs/framework-integrations.md。这些示例会在 CI 中由真实 React/Vue 类型检查。
EventRouter 会在 Codec 与 Compression 之前检查原始事件,并把每个事件交给第一个匹配的 Transport。未匹配事件必须进入 fallback,因此不会因规则遗漏永久阻塞队列:
import { EventRouter, HttpTransport, createAnalytics } from "tracelet-browser";
const analytics = createAnalytics({
appId: "shop-web",
transport: new EventRouter({
routes: [
{
name: "technical-errors",
match: (event) =>
event.name === "$error" || event.name === "$unhandled_rejection",
transport: new HttpTransport({
name: "alert-collector",
endpoint: "https://alerts.example.com/v1/batch",
}),
},
],
fallback: new HttpTransport({
name: "product-collector",
endpoint: "https://events.example.com/v1/batch",
}),
}),
});规则按声明顺序匹配。SDK 保持队列的 FIFO 顺序,并在目标 Transport 改变时切分批次;一次 flush() 仍只发送一个批次。flush_started、flush_succeeded 和 flush_failed 诊断事件会携带实际的 transport 名称,方便区分多个端点。完整示例见 examples/routing.ts。
Core 不依赖具体 Protobuf Runtime。使用 protobufjs、ts-proto 或 Buf 生成编码器后,通过 ProtobufCodec 注入:
import { ProtobufCodec, createAnalytics } from "tracelet-browser";
import { EventBatch } from "./generated/analytics.js";
const codec = new ProtobufCodec((batch) => {
return EventBatch.encode(EventBatch.fromJSON(batch)).finish();
});
const analytics = createAnalytics({
appId: "shop-web",
codec,
transport,
});示例协议位于 proto/analytics.proto。实际项目建议把生成代码放在独立包,例如 @tracelet/codec-protobuf。
- 默认队列仍是内存存储;浏览器生产环境应显式使用
createBrowserEventStorage(),优先选择 IndexedDB 并在启动失败时退回内存。 - gzip 使用浏览器原生
CompressionStream,不支持时不会自动降级。 - 自动采集暂不包含点击、输入和 DOM 内容采集;这是有意的隐私与稳定性边界。
properties和context在示例 Protobuf 中以 JSON bytes 保存,优先保持通用性;稳定后可以逐步强类型化。
pnpm build
pnpm build:docs
pnpm local:server
pnpm test:browser浏览器访问 http://127.0.0.1:4318 查看网页版使用手册;访问 http://127.0.0.1:4318/lab 打开完整的 SDK 验证台。页面支持:
- 自定义事件名、Properties 和批量数量。
- 仅入队或立即 Flush,并实时查看 IndexedDB 队列长度。
- Identify、身份重置和 Consent 状态切换。
- 注入 503、429、500、400 以及服务端响应延迟。
- 观察指数退避、失败保留、不可重试丢弃等行为。
- 查看服务端事件、批次统计、请求日志和 SDK 诊断日志。
Collector 同时提供以下调试接口:
http://127.0.0.1:4318/health
http://127.0.0.1:4318/v1/events
http://127.0.0.1:4318/v1/stats
| 方法 | 路径 | 用途 |
|---|---|---|
POST |
/v1/batch |
接收 SDK 批量事件 |
POST |
/v1/batch/:channel |
验证多个 Transport 的条件路由 |
GET |
/v1/events?limit=100 |
查询最近事件 |
DELETE |
/v1/events |
清空事件和统计 |
GET |
/v1/stats |
查询批次、失败、事件名和请求日志 |
POST |
/v1/control/failure |
配置后续请求的失败状态与延迟 |
本地 Collector 仅用于开发验证:事件只保存在内存中,进程退出即清空,不包含生产鉴权、限流和持久化能力。
官网采用 React、Vite、Tailwind CSS 和 shadcn/ui 的源码组件模式;这些依赖只存在于私有的 website workspace,不会进入 SDK 的 npm 包:
pnpm dev:docs
pnpm build:docs官网源码位于 website/,构建产物位于 out/。精简首页、Quickstart、Examples、API Reference 与两篇指南分别使用独立 URL,英文页面统一增加 /en 前缀。所有页面共享组件与中英文搜索,文档页提供粘性侧边目录,并具有各自的 canonical、hreflang、首个 HTML 响应摘要和搜索元数据。仓库附带 vercel.json,导入 Vercel 后会自动安装 workspace 依赖、运行构建并发布 out/。其他静态托管平台只需把构建命令设为 pnpm build:docs、输出目录设为 out。框架演进判断见 docs/website-framework.md。
src/ SDK 源码(不依赖 React)
tests/ SDK 行为测试
examples/ 浏览器接入和本地验证示例
website/ React + Vite + Tailwind 官网
proto/ 可选的 Protobuf 协议示例
项目通过 GitHub Actions 自动完成持续集成与 npm 发布:
- Pull Request 和
main分支推送会在 Node.js 20、24 上运行类型检查、测试、官网构建和 npm 包内容检查。 - CI 会把
npm pack生成的 tarball 安装进空白项目,验证 JavaScript 导入、TypeScript 声明和 Vite 构建,避免“源码能用但发布包不可用”。 - Node.js 24 还会安装同一个 tarball,并完成 React/Vite、Vue/Vite、Next.js/Turbopack、Webpack 与 Rollup 的真实生产构建。
- Node.js 检查通过后,会在 Chromium、Firefox 和 WebKit 上运行浏览器端到端测试。
- 发布 GitHub Release 后,工作流会校验 Git tag 与
package.json版本一致,再通过 npm Trusted Publishing 提交待审核版本;SemVer 预发布版本固定使用betadist-tag,正式版本使用latest。 - Vercel 保持 Git 集成:Pull Request 生成 Preview,
main更新生产站点,不在 Actions 中重复部署。
首次启用自动发布时,需要在 npm 包的 Settings → Trusted Publisher 中添加 GitHub Actions:
- Organization or user:
shishikami - Repository:
Tracelet - Workflow filename:
publish.yml - Environment:留空
- Allowed action:保持直接
npm publish关闭;npm stage publish默认允许
日常发布步骤:
- 更新
package.json版本和CHANGELOG.md。 - 合并到
main,等待 CI 通过。 - 创建同版本 tag(例如版本
0.2.0对应v0.2.0)。 - 发布 GitHub Release;
Publish npm package工作流会把版本提交到 npm 暂存区。 - 在 npm 的 Staged Packages 中检查包内容,并通过 Passkey/2FA 批准,版本才会正式公开。
本地发布前检查仍可使用:
pnpm check
pnpm smoke:package
pnpm smoke:ecosystem
pnpm pack:check完整发布检查表和故障处理见 RELEASING.md。
如果自动发布失败,请先修复 CI、Trusted Publisher 或 staged publishing 配置,再重新创建新的版本;不要绕过人工审批直接执行 npm publish。已经公开到 npm 的版本号不能覆盖。
正式发布前请更新 package.json 版本号。npm 包只包含 dist/、README、CHANGELOG、LICENSE 和 npm 必需的 package.json 清单,不包含官网与测试文件。
0.2.0 里程碑已完成:
- 浏览器测试覆盖 pagehide、gzip 和异常恢复
- 多 Transport 路由与条件投递
- 存储配额估算与 IndexedDB 配额诊断
- 文档搜索和框架适配示例
0.3.0 规划状态:
- 可观测 API 设计稿:队列快照、投递状态和多订阅者语义。
- 兼容性与韧性测试矩阵:明确自动化、部分覆盖、手工验证和待验证边界。
- Vercel Collector starter:CORS、安全大小边界、gzip、鉴权扩展和持久化 Sink 契约。
- 实现并验证可观测 API:队列快照、运行时订阅、Attempt 关联与三浏览器覆盖。
- 发布
0.3.0-beta.1;接下来通过 Playground 收集 API 使用反馈。 - 增加 CSP、离线恢复、隐藏页 Flush 与真实跨标签页锁的三浏览器夹具。
- 增加 React、Vue、Next.js、Webpack 与 Rollup 的真实生产构建夹具。
- 评估 CDN / IIFE 构建:0.3 保持原生 ESM-only,等待真实无打包器需求后再扩展。
- 增加 IndexedDB 启动探测与显式内存后备队列,避免首个
track()因持久存储不可用而影响业务。
0.3 的公开命名与 ESM-only 分发范围已经冻结;剩余发布门禁见 0.3 release readiness。
版本变化见 CHANGELOG.md。