audit-log-sdk
v1.1.0
Published
A tracking SDK for Vue/React/UniApp applications with manual and automatic tracking capabilities
Maintainers
Readme
Audit Log SDK
一个轻量级的前端审计日志SDK,支持 Vue、React 和 UniApp 项目,提供手动埋点和 API 自动拦截功能。
安装
npm install audit-log-sdk
# 或
yarn add audit-log-sdk
# 或
pnpm add audit-log-sdk快速开始
Vue 项目
// main.ts
import { createApp } from 'vue';
import { AuditLog, SystemNo } from 'audit-log-sdk';
import App from './App.vue';
const app = createApp(App);
app.use(AuditLog, {
reportUrl: '/apiLog/save-batch', // 日志上报地址(必填)
systemNo: SystemNo.OMS, // 系统编码(必填)
system: 'OMS', // 系统名称
userId: 1001, // 用户ID
userName: '张三', // 用户名称
systemToken: 'xxx-token', // 用户token
authorization: 'Basic xxx-token', // 上报接口的 Authorization Token(必填)
});
app.mount('#app');React 项目
// index.tsx
import { createAuditLog, SystemNo } from 'audit-log-sdk';
const audit = createAuditLog({
reportUrl: '/apiLog/save-batch',
systemNo: SystemNo.OMS,
authorization: 'Basic xxxx',
system: 'OMS',
userId: 1001,
userName: '张三',
authorization: 'Basic xxx-token', // 上报接口的 Authorization Token(可选)
});
export default audit;UniApp 项目
Vue3 模式(推荐)
// main.js
import { createSSRApp } from 'vue';
import { createUniAuditLog, SystemNo } from 'audit-log-sdk';
import App from './App.vue';
export function createApp() {
const app = createSSRApp(App);
app.use(createUniAuditLog({
reportUrl: 'https://your-api.com/apiLog/save-batch', // 小程序端需使用完整URL
systemNo: SystemNo.OMS,
system: 'OMS',
userId: 1001,
userName: '张三',
authorization: 'Basic xxx-token',
}));
return { app };
}Vue2 模式
// main.js
import Vue from 'vue';
import { UniAuditLog, SystemNo } from 'audit-log-sdk';
Vue.use(UniAuditLog, {
reportUrl: 'https://your-api.com/apiLog/save-batch',
systemNo: SystemNo.OMS,
system: 'OMS',
});非 Vue 插件模式(直接初始化)
// utils/audit.ts
import { initUniAppDirect, track, SystemNo } from 'audit-log-sdk';
initUniAppDirect({
reportUrl: 'https://your-api.com/apiLog/save-batch',
systemNo: SystemNo.OMS,
system: 'OMS',
});
export { track };App.vue 生命周期集成(可选)
SDK 默认通过 uni.onError 和 uni.onUnhandledRejection 自动捕获全局错误。如果你的 UniApp 版本不支持这些 API,可以手动在 App.vue 中集成:
// App.vue
import { onAppError, onAppUnhandledRejection, onPageShow } from 'audit-log-sdk';
export default {
onError(err) {
onAppError(err);
},
onUnhandledRejection(err) {
onAppUnhandledRejection(err.reason);
},
};页面生命周期集成(推荐)
为了正确追踪页面来源(referUrl),建议在页面 onShow 中调用 onPageShow:
// 页面中
import { onPageShow } from 'audit-log-sdk';
export default {
onShow() {
onPageShow();
},
};配置项
基础配置
| 参数 | 类型 | 必填 | 默认值 | 说明 |
| --------------- | --------------- | ---- | -------- | -------------------------------------------- |
| reportUrl | string | 是 | - | 日志上报接口地址 |
| systemNo | number | 是 | - | 系统编码(0=OMS, 1=TMS, 2=IOT) |
| system | string | 否 | - | 系统名称 |
| userId | number| string | 否 | - | 用户ID |
| userName | string | 否 | - | 用户名称 |
| systemToken | string | 否 | - | 用户token |
| userIp | string | 否 | - | 用户IP |
| channelId | string | 否 | 自动生成 | 渠道ID(设备ID) |
| channelType | string | 否 | 自动检测 | 渠道类型(web/APP/miniapp) |
| authorization | string | 否 | - | 上报接口的 Authorization Token,用于接口鉴权 |
| platform | string | 否 | 'auto' | 运行平台:'browser' / 'uniapp' / 'auto' |
注意:
systemVersion由 SDK 内部自动从 package.json 读取,无需用户配置。platform默认为'auto',SDK 会自动检测运行环境。UniApp 小程序/App 端自动识别为'uniapp',浏览器端自动识别为'browser'。如需强制指定,可手动设置。
功能配置
| 参数 | 类型 | 默认值 | 说明 |
| --------------------- | -------- | ------ | ------------------------------------------ |
| batchSize | number | 10 | 批量发送数量 |
| flushInterval | number | 5000 | 发送间隔(ms) |
| samplingRate | number | 1 | 采样率(0-1) |
| enableAutoTracking | boolean | true | 开启自动追踪(错误追踪) |
| enableApiIntercept | boolean | true | 开启API拦截 |
| enableErrorTracking | boolean | true | 开启错误追踪 |
| excludeUrls | string[] | [] | 排除的URL列表(黑名单,命中不拦截) |
| includeUrls | string[] | [] | 包含的URL列表(白名单,仅拦截匹配的) |
| beforeSend | function | - | 发送前回调,返回 null/false 可丢弃该条日志 |
| afterSend | function | - | 发送后回调 |
DOM 追踪配置 (domTrackerConfig)
通过监听用户交互事件(点击、提交等),将触发 API 请求的 DOM 元素信息关联到日志中。
注意:DOM 追踪仅在浏览器环境和 UniApp H5 端可用,小程序/App 端无 DOM,此功能自动禁用。
domTrackerConfig: {
enable: true, // 是否启用DOM追踪(默认 true,跟随API拦截)
timeWindow: 500, // 关联时间窗口(ms),默认 500
events: ['click', 'submit'], // 监听的事件类型
maxTextLength: 50, // 元素文本截断长度,默认 50
maxSelectorDepth: 5, // CSS选择器最大深度,默认 5
}工作原理:
- DomTracker 监听
click/submit等事件,缓存最近触发的 DOM 元素信息 + 时间戳 - API 拦截器捕获到请求时,检查时间窗口内是否有匹配的用户交互事件
- 有关联则记录
triggerDom字段,无则不记录(不强关联) - 关联后立即清除缓存,时间窗口过期也会自动清除,避免残留误关联
triggerDom 字段格式(JSON 字符串):
{
"selector": "div.container > form > button#submit-btn",
"tagName": "button",
"text": "提交"
}注意:以下场景无法关联 DOM 信息(
triggerDom为空):
- 页面加载/组件挂载时自动发出的请求
- 定时器/轮询触发的请求
- 路由切换触发的请求
- 交互后延迟超过时间窗口才发出的请求
小程序/App 端手动设置 DOM 触发信息
小程序和 App 端无 DOM API,无法自动追踪。可使用 setTriggerDom 在点击事件回调中手动设置:
import { setTriggerDom } from 'audit-log-sdk';
// 在 @tap 回调中调用,关联后续接口请求
getOrgIdList() {
setTriggerDom('u-input.org-select', '请选择组织结构');
this.$http.get({ url: '/api/list' }).then(...)
}Vue 插件模式下可通过 this.$audit 调用:
this.$audit.setTriggerDom('button.submit', '提交订单');
this.$http.post({ url: '/api/submit', data: form }).then(...)| 参数 | 类型 | 说明 |
| ---------- | ------ | ---------------------------- |
| selector | string | 元素选择器/标识 |
| text | string | 元素文本内容(截断前 50 字) |
注意:H5 端已自动追踪 DOM 事件,无需手动调用。设置后 500ms 内的下一个接口请求会关联此信息,自动填入
triggerDom字段。
API 拦截配置 (apiInterceptConfig)
apiInterceptConfig: {
interceptRequest: true, // 是否记录请求信息(默认 true)
interceptResponse: true, // 是否记录响应状态(默认 true)
logRequestBody: true, // 是否记录请求体(默认 true)
logResponseBody: false, // 是否读取响应体用于提取错误信息(默认 false)
logHeaders: true, // 是否记录请求头(默认 true)
maxBodySize: 10000, // 最大body大小(默认 10000)
excludeUrls: ['/health'], // API拦截层排除的URL
}说明:每个 API 请求只会产生一条日志。httpBody 记录的是请求体;当接口返回错误时,会从响应中提取 errorCode 和 errorMsg 写入对应字段。
手动埋点
基础用法
import { track, EventStatus } from 'audit-log-sdk';
// 基础埋点
track({
eventNo: 'ORDER_CREATE',
event: '创建订单',
});
// 带状态的埋点
track({
eventNo: 'ORDER_SUBMIT',
event: '提交订单',
eventStatus: EventStatus.SUCCESS, // 0=成功, 1=失败, 2=异常
});
// 带错误信息的埋点
track({
eventNo: 'ORDER_ERROR',
event: '订单错误',
eventStatus: EventStatus.ERROR,
errorCode: 'ERR_001',
errorMsg: '库存不足',
});
// 立即发送
track({
eventNo: 'IMPORTANT_EVENT',
event: '重要事件',
immediate: true,
});完整参数
track({
eventNo: 'ORDER_QUERY', // 事件编码
event: '查询订单', // 事件名称
eventStatus: EventStatus.SUCCESS, // 事件状态:0=成功 1=失败 2=异常
errorCode: '', // 错误编码(失败时自动从响应提取)
errorMsg: '', // 错误描述(失败时自动从响应提取)
httpUrl: '/api/order/query', // 请求地址
httpBody: '{"page":1}', // 请求报文
httpMethod: HttpMethod.POST, // 请求方式
operationDuration: 150, // 操作耗时(ms)
extText: '{"extra":"data"}', // 扩展字段
immediate: false, // 是否立即发送
});API 方法
设置用户信息
import { setUser, setToken, setAuthorization } from 'audit-log-sdk';
// 登录后设置用户信息
setUser(1001, '张三', 'xxx-token');
// 单独设置用户 token
setToken('new-user-token');
// 动态修改上报接口的 Authorization Token
setAuthorization('Basic new-auth-token');设置页面上下文
import { setPageContext, clearPageContext } from 'audit-log-sdk';
// 进入页面时设置
setPageContext('订单管理', 'ORDER_MGMT');
// 离开页面时清除
clearPageContext();手动刷新队列
import { flush } from 'audit-log-sdk';
// 立即发送当前队列中的所有日志
flush();Vue Router 集成
// router/index.ts
import { setPageContext, clearPageContext } from 'audit-log-sdk';
router.beforeEach((to, from, next) => {
clearPageContext();
next();
});
router.afterEach((to) => {
if (to.meta?.businessType) {
setPageContext(to.meta.businessType, to.meta.businessTypeNo);
}
});
// 路由定义
const routes = [
{
path: '/order/list',
component: OrderList,
meta: {
businessType: '订单列表',
businessTypeNo: 'ORDER_LIST',
},
},
];Vue 组件中使用
// Vue 3
import { getCurrentInstance } from 'vue';
export default {
setup() {
const { proxy } = getCurrentInstance();
const handleClick = () => {
proxy.$audit.track({
eventNo: 'BTN_CLICK',
event: '按钮点击',
});
};
return { handleClick };
},
};
// Vue 2
export default {
methods: {
handleClick() {
this.$audit.track({
eventNo: 'BTN_CLICK',
event: '按钮点击',
});
},
},
};React 组件中使用
// hooks/useAudit.ts
import { getAuditLog } from 'audit-log-sdk';
export const useAudit = () => {
const audit = getAuditLog();
return audit;
};
// 组件中使用
import { useAudit } from './hooks/useAudit';
function MyComponent() {
const audit = useAudit();
const handleClick = () => {
audit?.track({
eventNo: 'BTN_CLICK',
event: '按钮点击',
});
};
return <button onClick={handleClick}>提交</button>;
}UniApp 平台说明
平台自动检测
SDK 会在初始化时自动检测运行环境:
| 运行环境 | 检测结果 | 使用的平台适配器 |
| --------- | -------- | ---------------- |
| 浏览器 | browser | 浏览器 API(fetch、localStorage、DOM 等) |
| UniApp H5 | browser | 浏览器 API(H5 模式下浏览器 API 完整可用) |
| UniApp 小程序 | uniapp | UniApp API(uni.request、uni.getStorageSync 等) |
| UniApp App | uniapp | UniApp API(uni.request、uni.getStorageSync 等) |
你也可以通过配置
platform: 'uniapp'或platform: 'browser'强制指定平台。
UniApp 与浏览器的差异
| 功能 | 浏览器 | UniApp 小程序/App |
| ---- | ------ | ----------------- |
| 网络请求上报 | fetch + keepalive | uni.request |
| 本地存储 | localStorage | uni.getStorageSync / uni.setStorageSync |
| UserAgent | navigator.userAgent | 通过 uni.getSystemInfoSync() 拼接 |
| 页面 URL | window.location.href | getCurrentPages() 路由拼接 |
| 页面标题 | document.title | 页面路由(兜底) |
| 来源页面 | document.referrer | SDK 维护页面栈 |
| 渠道类型 | 基于 UA 判断(web/APP) | 基于 uniPlatform 判断(APP/miniapp) |
| API 拦截 | 拦截 fetch 和 XMLHttpRequest | 拦截 uni.request |
| 错误捕获 | window.addEventListener('error') | uni.onError |
| Promise 拒绝 | window.addEventListener('unhandledrejection') | uni.onUnhandledRejection |
| DOM 追踪 | 支持 | 不支持(自动禁用) |
| 页面隐藏处理 | beforeunload / pagehide / visibilitychange | uni.onAppHide |
UniApp 小程序端注意事项
- reportUrl 必须使用完整 URL:小程序不支持相对路径,必须使用
https://开头的完整地址 - 无 DOM 追踪:小程序端没有 DOM,
domTrackerConfig和triggerDom字段不生效 - 页面来源追踪:小程序端没有
document.referrer,需在页面onShow中调用onPageShow()来维护页面栈 - 错误捕获:部分低版本 UniApp 不支持
uni.onError,需在 App.vue 中手动调用onAppError() - channelType 差异:小程序端
channelType返回'miniapp',App 端返回'APP',浏览器端返回'web'
发送数据格式
SDK 批量发送 JSON 数据(浏览器端使用 fetch + keepalive,UniApp 端使用 uni.request):
{
"dataList": [
{
"systemNo": 0,
"system": "OMS",
"systemVersion": "1.0.16",
"referenceId": "1700000000000_abc123def",
"httpUrl": "/api/user/info",
"httpMethod": 2,
"httpBody": "{\"id\":1001}",
"operationTime": "2024-01-01 12:00:00",
"operationDuration": 230,
"eventStatus": 0,
"errorCode": null,
"errorMsg": null,
"businessType": "订单管理",
"businessTypeNo": "ORDER_MGMT",
"userId": 1001,
"userName": "张三",
"systemToken": "xxx-token",
"userIp": null,
"channelId": "device-id-xxx",
"channelType": "web",
"userAgent": "Mozilla/5.0...",
"referUrl": "https://example.com/dashboard",
"triggerDom": "{\"selector\":\"button#submit-btn\",\"tagName\":\"button\",\"text\":\"提交\"}"
}
]
}API 拦截日志示例(成功,含触发DOM)
{
"referenceId": "xxx",
"httpUrl": "/api/user/info",
"httpMethod": 2,
"httpBody": "{\"id\":1001}",
"operationTime": "2024-01-01 12:00:00",
"operationDuration": 150,
"eventStatus": 0,
"errorCode": null,
"errorMsg": null,
"triggerDom": "{\"selector\":\"button#query-btn\",\"tagName\":\"button\",\"text\":\"查询\"}"
}API 拦截日志示例(失败,自动提取错误信息)
{
"referenceId": "xxx",
"httpUrl": "/api/user/info",
"httpMethod": 2,
"httpBody": "{\"id\":1001}",
"operationTime": "2024-01-01 12:00:00",
"operationDuration": 230,
"eventStatus": 1,
"errorCode": "50001",
"errorMsg": "用户不存在"
}错误信息提取规则:SDK 会尝试从响应 JSON 中按以下顺序查找字段:
code/errorCode→message/msg/errorMsg
枚举值
SystemNo 系统编码
| 值 | 说明 | | -- | ---- | | 0 | OMS | | 1 | TMS | | 2 | IOT |
EventStatus 事件状态
| 值 | 说明 | | -- | ---- | | 0 | 成功 | | 1 | 失败 | | 2 | 异常 |
HttpMethod 请求方式
| 值 | 说明 | | -- | ------ | | 1 | GET | | 2 | POST | | 3 | DELETE | | 4 | PATCH | | 5 | PUT |
URL 过滤规则
支持通配符 * 匹配,例如 /api/* 可匹配所有以 /api/ 开头的 URL。
excludeUrls(黑名单):命中则不拦截
excludeUrls: ['/health', '/static/*'] // 这两个不拦,其他都拦includeUrls(白名单):未命中则不拦截
includeUrls: ['/api/*'] // 只有 /api/ 开头的拦截,其他都不拦组合使用
apiInterceptConfig: {
excludeUrls: ['/health'], // 先排除健康检查
includeUrls: ['/api/*'], // 再只保留 /api/ 开头的请求
}注意事项
- reportUrl 和 systemNo 是必填项
- 浏览器端使用
fetch + keepalive发送数据,支持自定义请求头(如 Authorization),页面关闭时数据仍可正常发送;UniApp 端使用uni.request - SDK 自身的上报请求不会被拦截器拦截(自动过滤)
- 浏览器的 OPTIONS 预检请求 会被自动过滤,不会产生日志
- 每个 API 请求只产生一条日志,
httpBody始终记录请求体 - 接口返回错误(状态码 >= 400)时,SDK 会尝试从响应 JSON 中提取
errorCode和errorMsg keepalive有请求体大小限制(一般 64KB),超大会直接失败(仅浏览器端)- 建议在生产环境设置合理的
samplingRate以减少日志量 - 页面上下文需要在路由切换时设置,用于标识日志来源页面
- 排除项目中不需要拦截的接口,如文件上传接口等大流量场景
- UniApp 小程序端
reportUrl必须使用完整 HTTPS URL - UniApp 小程序端 不支持 DOM 追踪,
triggerDom字段始终为空
License
MIT
