hx-web-tracker
v0.1.1
Published
Lightweight web tracking SDK (SLS client) extracted from growth project
Maintainers
Readme
hx-web-tracker
轻量级前端埋点 SDK,基于阿里云 SLS(Simple Log Service)日志服务,提供自动化的用户行为追踪能力。
核心特性:
- 🎯 自动采集用户行为:点击、输入、页面浏览、HTTP 请求
- 📦 批量上报与离线缓存:支持本地持久化,网络恢复后自动重试
- 🔐 安全的数据脱敏:内置字段掩码策略,保护敏感信息
- 🎨 丰富的上下文增强:自动附加会话、用户、设备、视图等上下文信息
- 🔗 请求追踪与动作关联:支持 trace_id 和 action_id 串联前后端日志
源码结构: 核心代码位于 core/(追踪引擎)、collectors/(行为采集器)、context/(上下文管理)三个目录。
安装
npm i hx-web-tracker快速集成示例
在你的项目里新增一个本地封装(例如 src/utils/logger.ts),负责初始化、注册采集器、提供 track/setView/refreshUser 等入口。下面是一份可直接拷贝的示例,包含必要的 JSDoc 说明,按注释替换占位符即可开箱使用。
import type { ViewInfo, UserInfo } from 'hx-web-tracker';
import {
init as trackerInit,
track as coreTrack,
setContextView,
refreshContextUser,
setTraceId,
registerCollector,
createClickCollector,
createInputCollector,
createRequestCollector,
createViewCollector,
} from 'hx-web-tracker';
import router from '@/router';
import axios from '@/utils/request'; // 替换为你的 axios 实例
const ENV = import.meta.env;
const LOG_ENDPOINT = '<你的 SLS endpoint>'; // 例:cn-beijing.log.aliyuncs.com
const LOG_PROJECT = '<你的 project>';
const LOG_LOGSTORE = ENV.MODE === 'development' ? '<test_logstore>' : '<prod_logstore>';
const LOG_TOPIC = '<可选 topic>';
/**
* 获取临时凭证(请接入你们的鉴权服务,不要硬编码 AK/SK)。
*/
async function fetchCredential() {
return {
accessKeyId: '',
accessKeySecret: '',
securityToken: '',
expireAt: Date.now() + 3600_000,
};
}
/**
* 兼容 Vue Router 3(currentRoute 为对象)与 4(currentRoute 为 ref)。
* 将路由对象转换为视图信息,用于 view_start/view_end。
*/
function unwrapRoute(route: any) {
if (!route || typeof route !== 'object') return route;
return 'value' in route ? (route as any).value : route;
}
function toRouteInfo(route: any): ViewInfo {
const r = unwrapRoute(route);
if (!r || typeof r !== 'object') {
return {
id: typeof window !== 'undefined' ? window.location.pathname : 'unknown',
};
}
const fullPath = r.fullPath ?? r.path ?? 'unknown';
const metaTitle = r.meta?.title_name ?? r.meta?.title;
return {
id: String(fullPath),
name: metaTitle ? String(metaTitle) : undefined,
title: metaTitle ? String(metaTitle) : undefined,
path: r.path ? String(r.path) : undefined,
};
}
/**
* 追加自定义字段:页面 URL/标题与视口大小。
* page_title 优先取 document.title,page_url 优先用视图 id/path 构造。
*/
function createCustomFieldResolvers() {
return [
(context: { view?: ViewInfo }) => {
const hasWindow = typeof window !== 'undefined';
const hasDocument = typeof document !== 'undefined';
const viewportWidth = hasWindow && typeof window.innerWidth === 'number' ? window.innerWidth : -1;
const viewportHeight = hasWindow && typeof window.innerHeight === 'number' ? window.innerHeight : -1;
const view = context.view ?? {};
const viewTitle = (view.title ?? view.name) as string | undefined;
const docTitle = hasDocument && typeof document.title === 'string' ? document.title : '';
const pageTitle =
docTitle.trim().length > 0
? docTitle
: typeof viewTitle === 'string' && viewTitle.trim().length > 0
? viewTitle
: '';
const viewId = typeof view.id === 'string' ? view.id : '';
const viewPath = typeof view.path === 'string' ? view.path : '';
const isAbsoluteUrl = /^https?:\/\//i.test(viewId);
const pageUrl =
isAbsoluteUrl && viewId
? viewId
: hasWindow && viewPath
? `${window.location.origin}${viewPath}`
: hasWindow && viewId
? `${window.location.origin}${viewId}`
: hasWindow
? window.location.href
: viewId || viewPath;
return {
page_url: pageUrl,
page_title: pageTitle,
viewport_width: viewportWidth,
viewport_height: viewportHeight,
};
},
];
}
let initialized = false;
let initPromise: Promise<void> | null = null;
let manualViewSetter: ((view: ViewInfo) => void) | null = null;
function registerBuiltInCollectors() {
registerCollector('click', createClickCollector(), { priority: 80, override: true });
registerCollector('input', createInputCollector({ maskStrategy: 'none' }), { priority: 70, override: true });
registerCollector(
'request',
createRequestCollector({
instrumentFetch: true,
instrumentXHR: true,
axiosInstances: [axios as any],
ignoredUrls: [/\\.js$/i, /\\.css$/i, /\\.map$/i, /__vite_ping/],
}),
{ priority: 60, override: true },
);
registerCollector(
'view',
createViewCollector({
router,
getRouteInfo: toRouteInfo, // 兼容 v3/v4 的 currentRoute/afterEach 入参
registerManualSetter: (setter) => {
manualViewSetter = setter;
},
}),
{ priority: 90, override: true },
);
}
/**
* 初始化埋点 SDK(幂等);可在应用入口直接调用,或在首次 track/send 前懒加载。
*/
export async function initTracker(): Promise<void> {
if (initialized) return;
if (initPromise) return initPromise;
registerBuiltInCollectors();
initPromise = trackerInit({
endpoint: LOG_ENDPOINT,
project: LOG_PROJECT,
logstore: LOG_LOGSTORE,
topic: LOG_TOPIC,
debug: !!ENV.DEV,
fetchCredential,
context: {
getUserInfo: async (): Promise<UserInfo> => ({ id: '', name: '', role: '' }), // TODO: 返回真实用户
resolveView: () => toRouteInfo(router.currentRoute),
customFieldResolvers: createCustomFieldResolvers(),
},
})
.then(() => {
initialized = true;
})
.catch((error) => {
initPromise = null;
throw error;
});
return initPromise;
}
/**
* 统一导出的 tracker 对象,便于业务侧调用。
*/
export const tracker = {
/**
* 手动初始化,可选(track 时会自动懒加载)。
*/
init: initTracker,
/**
* 上报业务事件。
*/
track(eventType: string, payload?: Record<string, unknown>) {
return initTracker().then(() => {
coreTrack({ event_type: eventType, payload });
});
},
/**
* 更新当前视图(若需手动埋点页面切换)。
*/
setView(view: ViewInfo) {
return initTracker().then(() => {
if (manualViewSetter) {
manualViewSetter(view);
} else {
setContextView(view);
}
});
},
/**
* 刷新用户上下文(登录/切换用户后调用)。
*/
refreshUser(user?: Partial<UserInfo>) {
return initTracker().then(() => refreshContextUser(user as UserInfo));
},
/**
* 设置 traceId 以串联前后端日志。
*/
setTraceId,
};使用方式:
- 在应用入口(如
main.ts)调用void tracker.init(),或在首次tracker.track()前懒加载。 - 路由已自动注册视图埋点,如需手动控制页面视图可调用
tracker.setView({ id, title, path })。 - 登录/切换用户后调用
tracker.refreshUser(user)刷新上下文。 - 需要链路追踪时调用
tracker.setTraceId(traceId)。
埋点字段说明
通用字段
每条日志都会包含以下通用字段:
| 字段 | 描述 | 示例值 | 备注 |
|------|------|--------|------|
| timestamp | 格式化时间字符串,格式为 YYYY-MM-DD HH:mm:ss | 2025-09-22 10:54:22 | 用于页面展示,24 小时制,无毫秒 |
| timestamp_ms | 事件产生时的 Unix 毫秒时间戳 | 1758509662127 | 供后端做时间排序/计算 |
| topic | 日志主题标签,SDK 初始化时配置 | web-tracker | 用于给上报数据分类 |
| event_type | 事件类型标识 | ui_click / http_request / view_start 等 | 详见下方事件类型说明 |
event_type 详细说明
| 事件类型 | 说明 | 触发场景 |
|---------|------|----------|
| session_start | 会话启动 | SDK 初始化时自动落一次,标记新会话启动 |
| view_start / view_end | 页面浏览 | 路由切换或手动调用 setView 时记录页面进入/离开及停留时长 |
| ui_click | 点击行为 | 内置采集器捕捉左键/右键/双击/长按等行为 |
| ui_input | 表单输入 | 输入框/文本域/下拉框失焦时,读取当前值(默认忽略 password/hidden/file),做截断或脱敏后上报 |
| http_request | 网络请求 | Axios/fetch/XHR 请求结束时落一条,包含耗时、状态码、错误信息等 |
| adjust_column_width | 表格列宽调整 | 通用表格列宽调整成功时手动上报,记录表名/字段/新宽度 |
| filter_change | 筛选条件变更 | 筛选项变动时手动上报,覆盖静态筛选面板和动态筛选面板 |
| download_table | 表格导出 | 表格导出操作时手动上报,附带导出表名与查询条件 |
| no_permission | 无权限访问 | 无权限页面被访问时手动上报,用于排查权限配置 |
| custom_event | 自定义事件 | tracker.send 若未传 event_name 会退回到此默认值(保留能力,目前未实际使用) |
自动附加的上下文字段
每个事件都会自动附加以下上下文信息:
会话上下文
| 字段名 | 描述 | 示例值 | 备注 |
|-------|------|--------|------|
| ctx_session_id | 当前浏览器会话 ID | "6501c4-6b6c" | 基于 sessionStorage,30 分钟无操作会刷新 |
| ctx_session_start | 本次会话开始的毫秒时间戳 | 1762805400000 | 与 ctx_elapsed_sec 配合判断会话持续时长 |
| ctx_window_id | 当前标签页 ID | "w-98f2..." | 同一账号多标签页同时操作时用于区分 |
| ctx_timestamp | 事件捕获时间(毫秒) | 1762809111574 | 若网络阻塞,可与 timestamp_ms 对照 |
| ctx_elapsed_sec | 会话开始到事件触发的秒数 | 3200 | 用于分析用户停留时长 |
| ctx_event_id | 埋点事件唯一 ID | "a1b2c3...-1762809111574-42" | 由会话 ID + 时间戳 + 计数器拼接 |
页面上下文
路由存在时自动携带的页面视图信息:
| 字段名 | 描述 | 示例值 | 备注 |
|-------|------|--------|------|
| ctx_view_id | 当前视图唯一标识(通常为路由 fullPath) | "/settings/profile?tab=basic" | 必带,包含查询参数 |
| ctx_view_name | 路由 meta.title 或手动设置的页面名称 | "用户设置" | 若未配置标题则为空 |
| ctx_view_path | 路由 path(不含查询参数) | "/settings/profile" | 便于做权限/菜单映射 |
用户上下文
| 字段名 | 描述 | 示例值 | 备注 |
|-------|------|--------|------|
| ctx_user_id | 当前登录用户 ID | "u_123" | 用户唯一标识 |
| ctx_user_name | 用户姓名 | "张三" | 空字符串表示尚未拿到用户信息 |
| ctx_user_role | 用户角色名称 | "admin" | 按业务侧返回 |
| ctx_user_department | 用户所属部门 | "研发部门" | 后端若无该字段则为空 |
设备上下文
| 字段名 | 描述 | 示例值 | 备注 |
|-------|------|--------|------|
| ctx_device_user_agent | 完整 UA 字符串 | "Mozilla/5.0 (...) Chrome/128.0.0.0 Safari/537.36" | 用于兼容性诊断。UA 是 "User-Agent",浏览器在请求网页时发送给服务器的一段标识字符串,里面包含浏览器类型、版本、操作系统、设备等信息 |
| ctx_device_browser_name | 浏览器名称 | "Chrome" | 基于 UA 解析 |
| ctx_device_browser_version | 浏览器版本 | "128.0.0.0" | 基于 UA 解析 |
| ctx_device_browser_engine | 浏览器渲染内核 | "Blink" | 区分 WebKit/Gecko 等 |
| ctx_device_os_name | 操作系统名称 | "macOS" | 移动端会返回 Android、iOS 等 |
| ctx_device_os_version | 操作系统版本 | "14.5.1" | 若 UA 无法解析则为 unknown |
| ctx_device_device_type | 终端类型 | "desktop" | 支持 mobile/tablet/tv |
| ctx_device_screen_width | 屏幕宽度(px) | 1440 | 取自 window.screen.width,无法获取时为 -1 |
| ctx_device_screen_height | 屏幕高度(px) | 900 | 取自 window.screen.height,无法获取时为 -1 |
| ctx_device_language | 浏览器首选语言 | "zh-CN" | 可用于语言偏好分析 |
追踪上下文
| 字段名 | 描述 | 示例值 | 备注 |
|-------|------|--------|------|
| trace_id | 全局追踪 ID | "f4b2c8a1-9d3e-4f12-b7a6-1e8c9d0f5a2b" | 关联前后端日志,由 setTraceId() 设置 |
| action_id | 当前用户操作的唯一编号 | "d3a9c1f4-7b2c-4c5e-9a10-2f3e8c5d6b7a" | 点击触发后,在行动存活期内的事件/请求都会带上,用于把"同一波操作"串起来 |
| action_type | 行动类型 | "click" / "form_submit" | 示例:click(由点击自动生成),业务手动 startAction('submit') 时会出现对应类型 |
| action_meta | 行动元信息(JSON 结构) | "{\"track_id\":\"search_btn\",\"track_name\":\"查询\",\"tag\":\"BUTTON\"}" | 便于知道是哪个控件触发了这次行动 |
扩展上下文
自定义字段解析器(customFieldResolvers)可以添加额外的上下文信息,字段前缀为 ctx_extra_*:
| 字段名 | 描述 | 示例值 | 备注 |
|-------|------|--------|------|
| ctx_extra_page_url | 事件触发时页面的完整 URL | "https://example.com/path?query=1#hash" | 包含协议、域名、路径、查询参数和 hash,方便后端还原用户的实际访问地址 |
| ctx_extra_page_title | 当前页面设置的浏览器显示标题 | "用户管理 - 后台系统" | 依赖浏览器 document.title |
| ctx_extra_viewport_width | 浏览器可视区域宽度 | 1440 | 记录事件发生时的视口宽度(像素) |
| ctx_extra_viewport_height | 浏览器可视区域高度 | 900 | 记录事件发生时的视口高度(像素) |
自动采集事件详情
SDK 会自动采集以下用户行为事件,无需手动调用:
会话事件
| 事件类型 | 触发时机 | 主要字段 |
|---------|---------|---------|
| session_start | SDK 初始化完成 | 会话开始标记 |
页面浏览事件
| 事件类型 | 触发时机 | 主要字段 |
|---------|---------|---------|
| view_start | 进入新页面/路由 | view_id, view_name, view_path |
| view_end | 离开当前页面 | view_id, duration_ms(停留时长) |
用户交互事件
| 事件类型 | 触发时机 | 主要字段 |
|---------|---------|---------|
| ui_click | 点击/长按/右键/双击 | click_type, track_id, track_name, element_tag, page_x, page_y |
| ui_input | 输入框失焦 | field_name, field_type, value_masked, value_length, form_name |
ui_click 事件详细字段说明:
| 字段名 | 描述 | 示例值 | 备注 |
|-------|------|--------|------|
| click_type | 点击类型 | "left" / "right" / "double" / "longpress" | 区分不同的点击行为 |
| element_tag | 触发元素的标签名 | "BUTTON" / "DIV" / "A" | 大写形式 |
| element_id | 元素的 id 属性 | "submitBtn" / "" | 无 id 时为空字符串 |
| element_classes | 元素 class 列表 | "el-button primary" | 以空格拼接的完整 class 字符串 |
| page_x | 点击点相对页面左上角的 X 坐标 | 842 | 单位为像素 |
| page_y | 点击点相对页面顶端的 Y 坐标 | 412 | 单位为像素 |
| text_mask | 目标元素文本内容的脱敏结果 | "保存修改" / "操作按钮..." | 最多 120 字符,超出截断并追加 ... |
| track_id | 元素上 data-track-id 的值 | "btn-save" / "" | 用于区分被点击元素,业务侧可自定义设置 |
| track_name | 元素展示文案或 data-track-name | "保存修改" | 供业务阅读,优先取 data-track-name,否则取元素文本 |
ui_input 事件详细字段说明:
| 字段名 | 描述 | 示例值 | 备注 |
|-------|------|--------|------|
| field_name | 失焦输入控件的名称/定位信息 | "account_name" / "email" / "search_keyword" | 优先取 data-track-field,其次 name/id/placeholder |
| field_type | 表单控件类型 | "text" / "select" / "textarea" / "checkbox" | 区分不同类型的输入控件 |
| form_name | 输入控件所在表单的 name 或 id | "login_form" / "user_profile_form" / "" | 用于关联同一表单内的多个字段,无表单时为空 |
| value_masked | 当前值的脱敏结果 | "***" / "[length:8]" / "option1,option2" | 根据 mask 策略脱敏。多选值用逗号连接 |
| previous_masked | 上次上报时脱敏后的旧值 | "***" / "[length:5]" / "" | 用于对比值是否发生变化,首次上报时为空 |
| value_length | 当前值长度 | 8 / 15 / 2 | 字符串长度或数组项数,帮助判断输入内容的规模 |
网络请求事件
| 事件类型 | 触发时机 | 主要字段 |
|---------|---------|---------|
| http_request | 发起 HTTP 请求(fetch/XHR/axios) | method, url, status, duration_ms, success, response_size |
http_request 事件详细字段说明:
| 字段名 | 描述 | 示例值 | 备注 |
|-------|------|--------|------|
| request_id | 每次请求埋点的唯一标识 | "e7f3b9dc-2a16-4d1d-9f2d-5de8a2e1c42b" | 用于串联同一条日志,类型为 string |
| method | HTTP 请求方法,表示这条 http_request 埋点对应的接口调用类型 | "GET" / "POST" / "PUT" / "DELETE" | 值来源于实际请求,已自动转成大写;常见包括 GET、POST、PUT、DELETE、PATCH、OPTIONS,若业务里发起其他方法(如 HEAD、TRACE)也会按原样上报 |
| url | 请求的完整地址 | "https://api.example.com/report?date=2025-09-10" | 包含协议、域名、路径和查询参数 |
| status | HTTP 状态码 | 200 / 401 / 500 | 请求返回的 HTTP 状态码,请求失败时为 0 |
| duration_ms | 请求耗时(毫秒) | 352 | 自发出请求到收到响应的总耗时,等价于 Network 面板里显示的 Time |
| success | 请求是否成功 | true / false | 请求是否视为成功(状态码在 200–399 且未抛错),类型为 boolean |
| request_body_digest | 对本次请求 body 的简化摘要 | "{\"name\":\"test\",\"status\"..." | 默认取原始内容或其 JSON 字符串,并在 200 个字符后截断,超出部分以 ... 结尾。帮助排查接口调用时传了哪些核心参数,同时通过截断/掩码降低敏感数据泄露风险 |
| response_digest | 响应体的摘要 | "{\"result\":\"ok\",\"list\":[...]}" / "" / "[unserializable]" | 默认截取前 200 个字符,超长追加 ...,类型为 string 或 null |
| response_size | 响应体大小估算 | 1250 / undefined | 单位是字节(字符数)。优先级:encoded > decoded > transfer > content-length > 估算。类型为 number 或 undefined(无法计算时) |
| response_size_transfer | 传输总大小(含头,压缩后) | 1856 | 单位是字节。来源 Performance API 的 transferSize。跨域无 TAO 或缓存命中时可能为 0/undefined |
| response_size_encoded | 压缩后的响应体大小 | 1720 | 单位是字节。Performance 的 encodedBodySize。压缩请求时与 decoded 不同;未压缩时它们相等 |
| response_size_decoded | 解压后的响应体大小 | 4096 | 单位是字节。Performance 的 decodedBodySize。可视为原始体积;缓存命中仍可能有值 |
| response_size_content_length | 响应头中的 Content-Length 值 | 1720 | 单位是字节。chunked/流式或未返回该头时为空;通常代表压缩后体积 |
| response_size_source | 大小取值来源 | "performance" / "header" | 可取 "performance" 或 "header";缺失表示无法确定 |
| response_compressed | 是否启用压缩 | true / false | 根据 Content-Encoding 判断。无法读头时为空 |
| response_content_encoding | Content-Encoding 值 | "gzip" / "br" / "deflate" | 无法读头或未压缩则为空 |
| response_cache_hit | 是否疑似缓存命中 | true / false | 启发式标记(transferSize=0 且有 body size),非严格判定 |
| error_message | 请求失败时的错误信息 | "Network Error" / "timeout of 5000ms exceeded" | 仅在请求失败时出现。请求失败(网络错误、状态码非 2xx/3xx、Axios reject 等)时,会尝试提取 error.message、响应体中的报错内容等写入,帮助后端排查接口异常。请求成功时该字段为空或不存在 |
| trace_id | 全局追踪 ID | "f4b2c8a1-9d3e-4f12-b7a6-1e8c9d0f5a2b" | 通过请求头 x-trace-id 发送,与 payload 中的 trace_id 对应,便于后端链路关联 |
| action_id | 当前操作 ID | "d3a9c1f4-7b2c-4c5e-9a10-2f3e8c5d6b7a" | 通过请求头 x-action-id 发送,与 payload 中的 action_id 对应。同一行动内的请求头值一致 |
手动埋点
除了自动采集,你也可以手动上报自定义事件:
// 简单事件
tracker.track('button_click', {
button_name: 'signup',
button_text: '立即注册'
});
// 复杂业务事件
tracker.track('order_submit', {
order_id: 'ORD_123456',
amount: 99.99,
currency: 'CNY',
item_count: 3
});表格列宽/拖拽埋点示例(通用写法)
SDK 不强制表格埋点,以下为通用示例,字段请替换为你的业务含义:
VXE Table 列宽拖拽(vxe-table)
<!-- template -->
<vxe-table @column-resizable-change="handleResize" ...>
...
</vxe-table>import tracker from '@/utils/logger'; // 你的业务封装
const handleResize = ({ column }: { column: any }) => {
tracker.track('adjust_column_width', {
table: 'example_table', // 自定义表名
field: column?.property || column?.field || '',
field_label: column?.title || '',
width: column?.resizeWidth,
});
};VXE Table 行拖拽(Sortable)
import Sortable from 'sortablejs';
import tracker from '@/utils/logger';
const rowDrop = () => {
const $table = tableRef.value;
Sortable.create($table.$el.querySelector('.vxe-table--body-wrapper tbody'), {
handle: '.drag-btn',
onEnd({ oldIndex, newIndex }) {
tracker.track('table_row_reorder', {
table: 'example_table',
from_index: oldIndex,
to_index: newIndex,
row_id: rows[oldIndex]?.id,
});
},
});
};Element Plus el-table 列宽拖拽
<!-- template -->
<el-table :data="rows" border @header-dragend="handleHeaderDragend">
...
</el-table>import tracker from '@/utils/logger';
const handleHeaderDragend = (newWidth: number, oldWidth: number, column: any) => {
tracker.track('adjust_column_width', {
table: 'example_table',
field: column?.property || column?.prop || '',
field_label: column?.label || '',
width: newWidth,
old_width: oldWidth,
});
};以上仅为示例,请用你的表名/业务字段替换占位符。
完整事件示例
{
"event_type": "ui_click",
"timestamp": "2024-05-20 10:24:11",
"timestamp_ms": "1716181451000",
"click_type": "left",
"track_id": "ad-card-cta",
"track_name": "立即报名",
"element_tag": "BUTTON",
"element_id": "signup-btn",
"element_classes": "btn btn-primary",
"text_mask": "立即报名",
"page_x": 812,
"page_y": 364,
"ctx_session_id": "6501c4-6b6c",
"ctx_session_start": "1716178251000",
"ctx_window_id": "a7f3e9-2c1d",
"ctx_timestamp": "1716181451000",
"ctx_elapsed_sec": "3200",
"ctx_event_id": "6501c4-6b6c-1716181451000-42",
"ctx_view_id": "/campaign/list",
"ctx_view_name": "Campaign List",
"ctx_view_path": "/campaign/list",
"ctx_user_id": "u_123",
"ctx_user_name": "张三",
"ctx_user_role": "admin",
"ctx_device_browser_name": "Chrome",
"ctx_device_browser_version": "124.0.6367.118",
"ctx_device_os_name": "macOS",
"action_id": "f4b2c8a1-9d3e-4f12-b7a6-1e8c9d0f5a2b",
"action_type": "click"
}💡 提示: 完整的字段定义和数据契约请参考 operation-log-reference.mdx
关键配置说明
endpoint/project/logstore/fetchCredential:必填,由使用方提供,勿硬编码敏感值到 SDK 内。router:用于路由级视图埋点;非路由视图可调用setView。axiosInstances/instrumentFetch/instrumentXHR:启用请求埋点并传入实例。ignoredUrls:可过滤静态资源或不需埋点的接口。context.getUserInfo/refreshContextUser:提供当前用户信息,可加掩码处理。
发布提示
- 发布前检查
.npmignore/files确保只带源码与类型;无敏感配置。 - 可在 CI 中加入 git-secrets/敏感信息扫描。
- 根据需要补充构建脚本(rollup/tsc)与类型打包。
