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

@cxcxcx/client-logger

v1.4.1

Published

前端客户端日志 SDK:全局错误监控、UI 库自动识别、行为埋点、IndexedDB 即时持久化、定时批量上报;内置 Electron/HBuilder/Web 设备 ID 识别;支持 Vue 2/3、React、Electron

Readme

@cxcxcx/client-logger

前端客户端日志 SDK,适用于 Vue 2 / Vue 3 / React + axios 的 Web / Electron 项目。

提供全局错误监控、UI 组件库自动识别、行为埋点、IndexedDB 即时持久化、定时批量上报,以及 Electron 本地日志文件写入(可选,不上报文件)。

当前版本:1.4.1


目录


功能特性

| 能力 | 说明 | |------|------| | 全局错误捕获 | window.error(含静态资源)、unhandledrejection、Vue errorHandler + warnHandler、console.error、Worker、CSP、存储配额 | | API 错误记录 | axios 响应/业务错误适配 | | 行为埋点 | 页面访问(page_view)、全局点击(ui_click)、自定义 track | | UI 库自动识别 | 运行时探测 Element UI / Ant Design Vue / Naive UI / Vant 等,动态生成交互选择器 | | 可读化文案 | 动态识别:路由 meta.title、DOM 文本/aria、可选 data-track-label | | 脱敏 | token、password 等敏感字段自动处理;clientUserId 默认保留明文,可通过 maskClientUserId: true 开启脱敏 | | 本地持久化 | 每条日志立即写入 IndexedDB,DevTools 可即时查看;上报成功后才删除 | | 定时上报 | 按 flushInterval 定时从 IndexedDB 批量上报,支持 getFlushInterval 动态读取间隔 | | Electron | 主进程可选写入本地 .log;内置机器码 IPC;上报与 Web 相同走批量接口 |

更新日志

| 版本 | 说明 | |------|------| | 1.4.1 | 文档:新增「登录前不监控」接入指南;同步 fe-adhd 登录态门禁与延迟激活示例 | | 1.4.0 | 内置跨端设备 ID / 平台识别(Electron 机器码、HBuilder plus.uuid、Web localStorage);init 自动 resolveDeviceId;Electron 模块新增 log:get-machine-id | | 1.3.9 | 支持自定义 getDeviceId / getPlatform(Electron 机器码、HBuilder UUID 等) | | 1.3.8 | Breaking:移除 Electron 日志文件上传;Web / Electron 统一走批量日志上报;fileUploadPath / uploadLogFiles / setLogUploadConfig 已删除 | | 1.3.7 | Breaking:移除上报 meta 收集(含 fieldRemarks);批量上报仅发送 logs,文件上传仅发送 file | | 1.3.6 | clientUserId 默认改为不脱敏;maskClientUserId: true 时可手动开启脱敏 | | 1.3.5 | Electron 日志文件上传支持 Authorization 头(authToken 配置);uploadDiagnosticLogs 上传前自动刷新 token;新增 maskClientUserId 配置 | | 1.3.4 | 日志写入即落 IndexedDB(移除内存队列);默认上报间隔改为 60s;新增 getFlushInterval / setFlushInterval / refreshFlushInterval;移除 batchSize 满批触发与 error/hidden 提前 flush | | 1.3.3 | 日志写入即落 IndexedDB(移除内存队列);默认上报间隔改为 60s | | 1.3.2 | Breaking:移除包内写死的上报路径默认值;uploadPath / fileUploadPath 改由接入项目在 createClientLogger / createUploader / Electron 主进程中自行配置 | | 1.3.1 | 文档更新;fe-adhd 改为从 npm 安装 | | 1.3.0 | UI 组件库自动识别(Element UI / Ant Design Vue / Naive UI / Vant 等);扩展全局错误(console / worker / storage / csp) | | 1.2.2 | 静态资源加载失败(capture error)、Vue Router 导航错误(router.onError) | | 1.2.x | 基础全局错误、axios 适配、vue-router 页面/点击追踪、Electron 桥接 |


安装

npm install @cxcxcx/client-logger

Peer Dependencies(按需安装)

| 包 | 用途 | 是否必须 | |----|------|----------| | vue@^2.6 | Vue 2 错误处理器 | 使用 ./vue2 时需要 | | vue@^3.0 | Vue 3 错误处理器 | 使用 ./vue3 时需要 | | react@^16.8 | React Error Boundary | 使用 ./react 时需要 | | axios@^1.0 | HTTP 上报 | 需要批量上报时需要 | | dayjs@^1.0 | 时间格式化 | 已作为 dependencies 内置 |


Vue 2 项目接入指南

适用于 Vue 2.6+ + vue-router 3 + axios 的 Web / Electron 项目。

1. 安装

npm install @cxcxcx/client-logger

项目需已具备 vue@^2.6、vue-router@^3、axios@^1.0(一般 Vue 2 项目本身就有,无需重复安装)。

2. 新建 Logger 配置

创建 src/utils/logger.js:

/**
 * 客户端 Logger 配置
 */
import axios from 'axios'
import { createClientLogger } from '@cxcxcx/client-logger'
import { setupVue2ErrorHandler } from '@cxcxcx/client-logger/vue2'
import { setupRouterLogger, setupClickTrack } from '@cxcxcx/client-logger/vue-router'

const API_BASE = process.env.VUE_APP_API_BASE || 'https://api.example.com'

export const logger = createClientLogger({
  appVersion: process.env.VUE_APP_VERSION || '1.0.0',
  runEnv: process.env.VUE_APP_RUN_ENV || process.env.NODE_ENV || '',
  platform: 'web',
  enabled: true,
  level: 'info',
  flushInterval: 60000,
  getFlushInterval: () => Number(process.env.VUE_APP_LOG_FLUSH_INTERVAL) || 60000,
  uploadBaseURL: API_BASE,
  uploadPath: '/clientLog/batch',              // 必填:批量上报路径,由项目自行定义
  dbName: 'my_project_client_log',
  getAuthToken: () => localStorage.getItem('token') || '',
  getClientUserId: () => '',
  createHttpClient: () => axios.create({ baseURL: API_BASE, timeout: 20000 })
})

export function setupAppLogger(Vue, router) {
  logger.init({ uploadBaseURL: API_BASE })
  setupVue2ErrorHandler(logger, Vue)
  setupRouterLogger(router, logger)
  setupClickTrack(router, logger, {
    skipTarget(target) {
      return Boolean(target?.closest?.('input[type="password"]'))
    }
  })
}

3. 在入口初始化

src/main.js:

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import { setupAppLogger, logger } from '@/utils/logger'

setupAppLogger(Vue, router)
Vue.prototype.$logger = logger

new Vue({
  router,
  render: (h) => h(App)
}).$mount('#app')

4. 路由配置 meta.title(页面名自动识别)

// src/router/index.js
const routes = [
  {
    path: '/home',
    name: 'home',
    meta: { title: '首页' },
    component: () => import('@/views/home/index.vue')
  },
  {
    path: '/user/:id',
    name: 'userDetail',
    meta: { title: '用户详情' },
    component: () => import('@/views/user/detail.vue')
  }
]

路由切换后自动记录 page_view,例如:从「首页」进入「用户详情」。

5. axios API 错误监控(可选)

// src/api/request.js
import axios from 'axios'
import { createAxiosLoggerInterceptor } from '@cxcxcx/client-logger/axios'
import { logger } from '@/utils/logger'

const service = axios.create({ baseURL: 'https://api.example.com', timeout: 30000 })
const { onResponseError, onBusinessError } = createAxiosLoggerInterceptor(logger)

service.interceptors.response.use(
  (response) => {
    onBusinessError(response)
    return response
  },
  onResponseError
)

export default service

跳过某次请求的日志:axios.get('/api/heartbeat', { skipLog: true })

6. Vue CLI 构建配置

vue.config.js:

module.exports = {
  transpileDependencies: ['@cxcxcx/client-logger']
}

7. 组件内手动埋点

// Options API
this.$logger.track('form_submit', { formId: 'login' })

// 或直接 import
import { logger } from '@/utils/logger'
logger.track('order_created', { orderId: '123' })
logger.error('api', { url: '/pay', message: '支付失败' })

8. 验证是否生效

  1. 启动项目,切换几个页面
  2. 打开 DevTools → Application → IndexedDB → my_project_client_log
  3. 应能立即看到 page_view(页面访问)、ui_click(点击)等记录(刷新 Object Store 视图)
  4. 故意触发 Vue 报错,应能看到 level: error 的记录
  5. 上报成功后对应记录会从 IndexedDB 删除;失败则保留,等待下次定时上报

Vue 3 项目接入指南

适用于 Vue 3 + vue-router 4 + axios 的 Web / Electron 项目(Vite / Vue CLI 均可)。

1. 安装

npm install @cxcxcx/client-logger

项目需已具备 vue@^3、vue-router@^4、axios@^1.0(一般 Vue 3 项目本身就有,无需重复安装)。

2. 新建 Logger 配置

创建 src/utils/logger.js:

/**
 * 客户端 Logger 配置
 */
import axios from 'axios'
import { createClientLogger } from '@cxcxcx/client-logger'
import { setupVue3ErrorHandler, provideLogger } from '@cxcxcx/client-logger/vue3'
import { setupRouterLogger, setupClickTrack } from '@cxcxcx/client-logger/vue-router'

const API_BASE = import.meta.env.VITE_API_BASE || process.env.VUE_APP_API_BASE || 'https://api.example.com'

export const logger = createClientLogger({
  appVersion: import.meta.env.VITE_APP_VERSION || '1.0.0',
  runEnv: import.meta.env.MODE || process.env.NODE_ENV || '',
  platform: 'web',
  enabled: (import.meta.env.VITE_LOG_ENABLED ?? 'true') !== 'false',
  level: import.meta.env.VITE_LOG_LEVEL || 'info',
  flushInterval: Number(import.meta.env.VITE_LOG_FLUSH_INTERVAL || 60000),
  getFlushInterval: () => Number(import.meta.env.VITE_LOG_FLUSH_INTERVAL || 60000),
  uploadBaseURL: API_BASE,
  uploadPath: '/clientLog/batch',
  dbName: 'my_project_client_log',
  getAuthToken: () => localStorage.getItem('token') || '',
  getClientUserId: () => '',
  createHttpClient: () => axios.create({ baseURL: API_BASE, timeout: 20000 })
})

export function setupAppLogger(app, router) {
  logger.init({ uploadBaseURL: API_BASE })
  setupVue3ErrorHandler(logger, app)
  provideLogger(app, logger, 'logger')
  setupRouterLogger(router, logger)
  setupClickTrack(router, logger, {
    skipTarget(target) {
      return Boolean(target?.closest?.('input[type="password"]'))
    }
  })
}

Vite 项目用 import.meta.env.VITE_*;Vue CLI 项目用 process.env.VUE_APP_*,按实际构建工具选用即可。

3. 在入口初始化

src/main.js:

import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import { setupAppLogger } from '@/utils/logger'

const app = createApp(App)

setupAppLogger(app, router)

app.use(router)
app.mount('#app')

4. 组合式 API 中使用

<script setup>
import { inject } from 'vue'

const logger = inject('logger')

function handleSubmit() {
  logger.track('form_submit', { formId: 'register' })
}
</script>

Options API 仍可使用 this.$logger(setupVue3ErrorHandler 已挂载到 globalProperties)。

5. 路由、axios、构建配置

与 Vue 2 相同:

  • 路由表配置 meta.title → 自动 page_view
  • 点击按钮自动 ui_click(可见文字 / data-track-label)
  • axios 使用 @cxcxcx/client-logger/axios 拦截器
  • Vue CLI 需 transpileDependencies: ['@cxcxcx/client-logger']
  • Vite 一般无需额外配置

6. 可选:显式标记难识别的控件

<button data-track-label="导出报表" @click="handleExport">导出</button>

7. 验证是否生效

同 Vue 2:IndexedDB 中查看 page_view、ui_click、error 记录。


子模块说明

本包通过 exports 提供多个入口:

| 入口 | 说明 | |------|------| | @cxcxcx/client-logger | 核心工厂、detectUiLibraries / UI_LIBRARY_PROFILES 等工具 | | @cxcxcx/client-logger/vue2 | Vue 2 全局错误处理 | | @cxcxcx/client-logger/vue3 | Vue 3 全局错误处理 + provide | | @cxcxcx/client-logger/react | React Error Boundary + 路由/点击追踪 | | @cxcxcx/client-logger/axios | axios 拦截器工具 | | @cxcxcx/client-logger/vue-router | vue-router 3/4 页面追踪 + 导航错误 + 全局点击追踪 | | @cxcxcx/client-logger/electron | Electron 主进程日志模块(CommonJS) |


createClientLogger 配置项

createClientLogger({
  // --- 基础信息 ---
  appVersion: '1.0.0',           // 应用版本号
  runEnv: 'production',          // 运行环境:development / test / uat / production
  platform: 'web',               // web | electron

  // --- 开关与级别 ---
  enabled: true,                 // 是否启用(也可在 init 时覆盖)
  level: 'info',                 // 最低记录级别:debug / info / track / warn / error
  bindGlobalErrors: true,        // 是否绑定 window 全局错误(含 capture 阶段资源加载失败,默认 true)
  captureConsoleError: true,     // 是否收集第三方库 console.error(默认 true)
  captureWorkerError: true,      // 是否 patch Worker / SharedWorker 错误(默认 true)
  captureStorageQuotaError: true,// 是否监听 localStorage / IndexedDB 配额超限(默认 true)
  captureCspViolation: true,     // 是否监听 CSP 违规(默认 true)
  consoleErrorSkipPatterns: [],  // 额外忽略的 console.error 正则,如 [/ignore me/i]

  // --- 上报策略 ---
  flushInterval: 60000,          // 自动上报间隔(毫秒),默认 60s
  getFlushInterval: () => 60000, // 动态获取上报间隔(优先于 flushInterval,每次重启定时器时重新调用)
  uploadBaseURL: '',             // 上报 API 根地址(也可在 init 时传入)
  uploadPath: '/clientLog/batch',              // 【必填】批量上报路径,由接入项目定义

  // --- 存储 ---
  dbName: 'client_logger',                    // IndexedDB 库名
  legacyStorageKey: 'client_log_pending',     // 旧版 localStorage 迁移 key
  sessionKey: 'client_log_session_id',        // sessionStorage 会话 ID key
  deviceKey: 'client_log_device_id',          // localStorage 设备 ID key

  // --- 鉴权与用户 ---
  getAuthToken: () => '',          // 返回 Authorization 头内容(如 Bearer xxx 或 token 字符串)
  getClientUserId: () => '',       // 返回当前业务用户 ID(默认不脱敏)
  getDeviceId: () => '',           // 自定义设备 ID(可选;不传则自动识别)
  getPlatform: () => 'web',        // 自定义平台(可选;不传则自动识别 electron/app/web)
  autoDetectDevice: true,          // 是否在 init 时自动识别设备 ID(默认 true)
  plusReadyTimeout: 1500,          // HBuilder plusready 等待超时(毫秒)
  maskClientUserId: false,         // 是否脱敏 clientUserId(默认 false;userId 始终脱敏)

  // --- HTTP 客户端 ---
  createHttpClient: () => axiosInstance,  // 必须提供,用于 POST 上报

  // --- Electron 桥接(可选)---
  electronBridge: {
    writeClientLog: (entry) => Promise  // 可选:同步写入主进程本地 .log
  },

  // --- 可读化配置(可选)---
  routeTitleMap: { '/home': '首页' },
  pathPrefixTitles: [{ prefix: '/train', title: '训练中心' }],
  classHintMap: { 'submit-btn': '提交按钮' },
  elementTypeMap: { span: '文本' },
  autoDetectUiLibraries: true,   // 是否自动识别 UI 组件库(默认 true)
  uiLibraries: ['element-ui'],   // 手动指定库 id,设 [] 则仅用原生 HTML/ARIA 选择器
  trackMessages: { custom_action: '自定义动作' },
  logTypeMessages: { api: '接口错误' },
  behaviorLogBuilder: (action, ctx, helpers) => ({ msg, ctx }),  // 自定义行为文案
  sensitiveKeys: ['customSecret']   // 额外脱敏字段
})

1.3.2 起:uploadPath 为必填项,SDK 不再提供默认路径。若单独使用 createUploader,同样需要传入该项:

import { createUploader } from '@cxcxcx/client-logger'

const uploader = createUploader({
  uploadPath: '/clientLog/batch',
  createHttpClient: () => axios.create({ baseURL: API_BASE }),
  getAuthToken: () => token
})

日志级别优先级

debug(0) < info(1) < track(2) < warn(3) < error(4)

设置 level: 'track' 时,会记录 track、warn、error,但不记录 debug、info。

存储与上报流程(1.3.4+)

产生日志 → 立即写入 IndexedDB(pending_logs)
                ↓
         定时 flush(默认 60s,可动态配置)
                ↓
         批量 POST 到服务端
                ↓
    成功:按 id 删除已上报记录(防重复)
    失败:保留记录,下次定时重试
  • 不使用内存队列:日志产生后立即可在 DevTools → IndexedDB 中查看
  • 防重复上报:仅上报成功后才删除 IndexedDB 记录;flushing 锁防止并发重复提交
  • 触发时机:定时器、init 启动时重试历史记录、手动 logger.flush() / uploadDiagnosticLogs()

Logger 实例 API

logger.init(options?)

初始化 Logger,必须在应用启动时调用一次。

logger.init({
  enabled: true,
  level: 'info',
  flushInterval: 60000,
  uploadBaseURL: 'https://api.example.com'
})

初始化时会:

  • 打开 IndexedDB 并迁移旧 localStorage 数据
  • 尝试上报历史 pending 日志(启动时重试上次失败记录)
  • 绑定全局错误(若未禁用)
  • 启动定时 flush(按 getFlushInterval 或 flushInterval 动态取值)
  • 同步 Electron 上传配置

logger.debug(type, message, ctx?)

调试日志。

logger.debug('custom', '调试信息', { detail: 'xxx' })

logger.info(type, message, ctx?)

普通信息日志。

logger.info('logger', '客户端 Logger 已初始化', { flushInterval: logger.getFlushInterval() })

logger.track(action, ctx?)

行为埋点,level 固定为 track,type 固定为 behavior。

logger.track('user_selected', { clientUserId: '12345' })
logger.track('mqtt_connect')
logger.track('page_view', { msg: '访问页面:首页', fromPath: '/', toPath: '/home' })

ctx 中可传入 msg 覆盖自动生成的中文描述。


logger.warn(type, message, ctx?)

警告日志。

logger.warn('network', '网络不稳定', { retry: 3 })

logger.error(type, ctx?)

错误日志,与其他级别一样立即写入 IndexedDB,按定时器间隔上报。

// 写法 1:type + ctx 对象
logger.error('api', {
  url: '/user/info',
  method: 'get',
  status: 500,
  message: '服务器异常'
})

// 写法 2:仅传 ctx 对象
logger.error({
  message: '未知错误',
  stack: err.stack
})

logger.flush(reason?)

手动触发上报,返回 Promise。

const result = await logger.flush('manual')
// { ok: true, count: 10 } 或 { ok: false, error }

logger.getFlushInterval()

获取当前生效的上报间隔(毫秒)。若配置了 getFlushInterval 回调,每次调用都会重新读取。

const interval = logger.getFlushInterval()

logger.setFlushInterval(interval)

手动设置上报间隔并重启定时器。返回 true 表示设置成功。

logger.setFlushInterval(30000)

logger.refreshFlushInterval()

重新调用 getFlushInterval 回调(若有)并重启定时器,适用于运行时修改 localStorage / 远程配置后刷新间隔。

localStorage.setItem('log_flush_interval', '120000')
logger.refreshFlushInterval()

logger.uploadDiagnosticLogs()

诊断场景:立即 flush IndexedDB 中的待上报日志(与 logger.flush('diagnostic') 等价)。

await logger.uploadDiagnosticLogs()

logger.setRoute(routePath)

设置当前路由路径,写入每条日志的 route 字段。

logger.setRoute('/home')

vue-router 适配器会在 afterEach 中自动调用,一般无需手动设置。


logger.destroy()

停止定时 flush,重置初始化状态(SPA 热重载或测试场景可用)。


辅助方法

| 方法 | 说明 | |------|------| | logger.buildPageViewLog(fromRoute, toRoute) | 生成页面访问的可读 msg 和 ctx | | logger.buildClickLog(target, currentRoute) | 生成点击事件的可读 msg 和 ctx | | logger.getRouteTitle(route) | 根据路由对象获取中文页面名 | | logger.getFlushInterval() | 获取当前上报间隔(ms) | | logger.setFlushInterval(ms) | 设置上报间隔并重启定时器 | | logger.refreshFlushInterval() | 重新读取动态间隔并重启定时器 |


Vue 2 适配器

import { setupVue2ErrorHandler, createVue2Plugin } from '@cxcxcx/client-logger/vue2'

// 方式 1:直接设置 errorHandler(推荐,默认同时捕获 Vue warn)
setupVue2ErrorHandler(logger, Vue)

// 关闭 Vue warn 捕获
setupVue2ErrorHandler(logger, Vue, { captureWarn: false })

// 仅上报 Logger,不在控制台重复输出
setupVue2ErrorHandler(logger, Vue, { printToConsole: false })

// 方式 2:Vue 插件
Vue.use(createVue2Plugin(logger))

捕获 Vue 组件内未处理异常,记录为:

{
  "level": "error",
  "type": "vue",
  "msg": "Vue 组件错误",
  "ctx": {
    "message": "Cannot read property 'x' of undefined",
    "stack": "...",
    "info": "render",
    "route": "/home"
  }
}

Vue 开发警告(如模板引用未定义属性)默认不设置 warnHandler,由 Vue 原生 console.error 输出(红色),Logger 侧向监听并以 level: error 收集:

{
  "level": "error",
  "type": "vue",
  "msg": "Property or method \"xxx\" is not defined...",
  "ctx": {
    "message": "Property or method \"xxx\" is not defined...",
    "source": "vue-warn",
    "trace": "...",
    "route": "/assess"
  }
}

同时挂载 Vue.prototype.$logger = logger。


Vue 3 适配器

import { createApp } from 'vue'
import { setupVue3ErrorHandler, createVue3Plugin, provideLogger } from '@cxcxcx/client-logger/vue3'
import { setupRouterLogger, setupClickTrack } from '@cxcxcx/client-logger/vue-router'

const app = createApp(App)

// 方式 1:直接设置(推荐)
setupVue3ErrorHandler(logger, app)

// 方式 2:插件
app.use(createVue3Plugin(logger))

// 组合式 API:provide/inject
provideLogger(app, logger, 'logger')

logger.init({ uploadBaseURL: 'https://api.example.com' })
setupRouterLogger(router, logger)   // vue-router 4 兼容
setupClickTrack(router, logger)

app.mount('#app')

组合式 API 中使用:

import { inject } from 'vue'

const logger = inject('logger')
logger.track('button_click', { buttonId: 'submit' })

Options API 中仍可使用 this.$logger。


React 适配器

import React from 'react'
import { useLocation } from 'react-router-dom'
import {
  createReactErrorBoundary,
  createReactRouterTracker,
  setupReactClickTrack
} from '@cxcxcx/client-logger/react'

// 1. 错误边界
const LoggerErrorBoundary = createReactErrorBoundary(logger, { React })

// 2. 路由切换追踪
const ReactRouterTracker = createReactRouterTracker(logger, { React })

function AppTracker() {
  const location = useLocation()
  return <ReactRouterTracker location={location} />
}

// 3. 全局点击追踪
setupReactClickTrack(logger, () => ({ path: window.location.pathname }))

function App() {
  return (
    <LoggerErrorBoundary fallback={() => <div>页面出错了</div>}>
      <BrowserRouter>
        <AppTracker />
        <Routes>...</Routes>
      </BrowserRouter>
    </LoggerErrorBoundary>
  )
}

React 适配器 API

| 方法 | 说明 | |------|------| | createReactErrorBoundary(logger, { React, fallback, onError }) | 创建 Error Boundary 组件 | | createReactRouterTracker(logger, { React }) | 创建路由追踪组件,需传入 useLocation() 的 location | | setupReactClickTrack(logger, getCurrentRoute, options) | 全局点击追踪,getCurrentRoute 返回 { path } |

React 错误日志结构:

{
  "level": "error",
  "type": "react",
  "msg": "react",
  "ctx": {
    "message": "Cannot read properties of undefined",
    "stack": "...",
    "info": " at App (App.jsx:12:5)"
  }
}

axios 适配器

import { createAxiosLoggerInterceptor } from '@cxcxcx/client-logger/axios'

const { onResponseError, onBusinessError, logApiError } = createAxiosLoggerInterceptor(logger)

// 响应拦截:HTTP 错误
service.interceptors.response.use(
  (response) => {
    onBusinessError(response)  // 业务 code !== 200
    return response
  },
  onResponseError              // 网络/HTTP 状态码错误
)

跳过某次请求的日志

在 axios config 上设置:

axios.get('/api/heartbeat', { skipLog: true })

手动记录 API 错误

logApiError(config, { status: 401, msg: '未授权' })

扩展全局错误捕获

logger.init() 且 bindGlobalErrors !== false 时,除运行时错误外还会自动启用:

| type | 触发场景 | 说明 | |------|----------|------| | console | 第三方库 console.error | 自动跳过 ResizeObserver loop 等已知噪音;[Vue warn] 归入 vue | | worker | Worker / SharedWorker 运行错误 | 通过 patch 构造函数自动挂载监听 | | storage | localStorage / sessionStorage / IndexedDB 配额超限 | IndexedDB 含 Logger 自身缓冲写入失败 | | csp | Content-Security-Policy 违规 | securitypolicyviolation 事件 |

可按需关闭单项:

createClientLogger({
  captureConsoleError: false,
  captureWorkerError: false,
  captureStorageQuotaError: false,
  captureCspViolation: false,
  consoleErrorSkipPatterns: [/custom noise/i]
})

vue-router 适配器

页面访问追踪

import { setupRouterLogger } from '@cxcxcx/client-logger/vue-router'

setupRouterLogger(router, logger)

setupRouterLogger 会同时注册:

  • router.afterEach:记录 page_view
  • router.onError:记录路由导航失败(如 lazy chunk 加载失败、导航守卫 reject)

路由错误示例:

{
  "level": "error",
  "type": "router",
  "msg": "Loading chunk assess failed.",
  "ctx": {
    "message": "Loading chunk assess failed.",
    "route": "/assess",
    "routeName": "assess"
  }
}

每次路由切换后自动记录 page_view:

{
  "level": "track",
  "type": "behavior",
  "msg": "从「首页」进入「评估中心」",
  "ctx": {
    "action": "page_view",
    "fromPage": "首页",
    "toPage": "评估中心",
    "fromPath": "/home",
    "toPath": "/assess"
  }
}

全局点击追踪

import { setupClickTrack } from '@cxcxcx/client-logger/vue-router'

setupClickTrack(router, logger, {
  // 跳过某些路由下的点击记录
  skipRoute(route) {
    return route.path.startsWith('/game')
  },
  // 跳过某些 DOM 目标(如密码框、游戏画布)
  skipTarget(target) {
    return target.closest('input[type="password"]')
  }
})

自动识别按钮、链接及已探测到的 UI 组件库控件,生成中文描述:

内置支持:Element UI、Ant Design Vue、Naive UI、Vant、Arco Design、View UI、Vuetify、Quasar、TDesign。
fe-adhd 使用 Element UI,启动后会自动探测 .el-* 控件,无需手动配置。

{
  "level": "track",
  "type": "behavior",
  "msg": "在「首页」点击「开始评估」",
  "ctx": {
    "action": "ui_click",
    "page": "首页",
    "pagePath": "/home",
    "target": "开始评估",
    "targetType": "按钮",
    "tag": "button",
    "id": "",
    "className": "el-button el-button--primary",
    "text": "开始评估"
  }
}

Electron 集成

1.3.8 起:不再上传本地 .log 文件。Electron 与 Web 统一通过渲染进程批量上报;主进程仅可选写入本地文件便于排查。 1.4.0 起:内置 log:get-machine-id,配合 preload 暴露后可自动写入稳定 deviceId。

主进程(main.js)

const {
  setupElectronLog,
  registerLoggerIpcHandlers
} = require('@cxcxcx/client-logger/electron')

app.whenReady().then(() => {
  setupElectronLog()
  registerLoggerIpcHandlers()
})

本地日志目录:app.getPath('userData')/logs/client-YYYY-MM-DD.log

Preload(preload.js)

contextBridge.exposeInMainWorld('electronAPI', {
  isElectron: true,
  writeClientLog: (entry) => ipcRenderer.invoke('log:write', entry),
  getLogDirectory: () => ipcRenderer.invoke('log:get-log-dir'),
  getMachineId: () => ipcRenderer.invoke('log:get-machine-id')
})

渲染进程桥接

function createElectronBridge() {
  const api = window.electronAPI
  if (!api?.isElectron) return null

  return {
    writeClientLog: (entry) => api.writeClientLog(entry)
  }
}

const logger = createClientLogger({
  // platform / deviceId 默认自动识别,一般无需手动传
  getAuthToken: () => getToken(),
  electronBridge: createElectronBridge(),
  // ...其他配置(与 Web 相同,走 uploadPath 批量上报)
})

await logger.init({ uploadBaseURL: API_BASE })

渲染进程每条日志会:

  1. 立即写入 IndexedDB → 按 flushInterval 定时批量上报服务端
  2. (可选)通过 IPC 同步写入 Electron 本地 log 文件,仅作本地排查,不上报

设备 ID 识别优先级:Electron 机器码 → HBuilder plus.device.uuid → localStorage 伪 ID


日志结构与字段说明

每条日志上报时的数据结构:

{
  "ts": "2026-07-10 13:00:00",
  "level": "track",
  "type": "behavior",
  "msg": "在「首页」点击「开始评估」",
  "ctx": {
    "action": "ui_click",
    "page": "首页",
    "target": "开始评估"
  },
  "sessionId": "1720600000000-abc123",
  "deviceId": "dev-1720600000000-xyz789",
  "appVersion": "1.0.1",
  "runEnv": "production",
  "route": "/home",
  "clientUserId": "1234567890",
  "platform": "electron"
}

字段说明

| 字段 | 说明 | |------|------| | ts | 本地时间,格式 YYYY-MM-DD HH:mm:ss | | level | debug / info / track / warn / error | | type | logger / behavior / api / vue / runtime / resource / router / console / worker / storage / csp / unhandledrejection / mqtt 等 | | msg | 中文可读描述(每条日志都有) | | ctx | 事件详细上下文 | | sessionId | 单次打开应用的会话 ID(sessionStorage) | | deviceId | 设备持久 ID(localStorage) | | appVersion | 应用版本 | | runEnv | 运行环境 | | route | 当前页面路由 | | clientUserId | 当前用户 ID(默认不脱敏;maskClientUserId: true 时脱敏) | | platform | web / electron |

应用版本、环境、平台等上下文写在每条 log 上,上报请求不再附带顶层 meta。


上报接口约定

路径由接入项目配置:SDK 不再内置默认路径。请在 createClientLogger 中传入 uploadPath,并与后端约定一致。以下以 fe-adhd 项目为例。

批量日志上报

POST {uploadBaseURL}{uploadPath}
// 示例:POST https://api.example.com/adhd/clientLog/batch
Content-Type: application/json
Authorization: {token}

请求体:

{
  "logs": [
    { "ts": "...", "level": "error", "type": "api", "msg": "...", "ctx": {} }
  ]
}

响应: HTTP 2xx 即视为成功,SDK 会删除已上报的 IndexedDB 记录。


动态识别(推荐,跨项目零配置)

无需写死 routeTitleMap / classHintMap,按以下约定即可在其他项目直接使用。

页面名(路由)

在 vue-router 路由表配置 meta.title(本项目已配置):

{
  path: '/assess',
  name: 'assess',
  meta: { title: '评估中心' },
  component: ...
}

识别优先级:

route.meta.title → matched 记录 meta.title → route.name 可读化 → path

按钮/控件名(点击)

自动从 DOM 读取,优先级:

data-track-label → aria-label / title / alt → 可见文本 → class 语义化 → debugHint

任意项目可为特殊控件加属性(可选):

<button data-track-label="提交评估">提交</button>
<div class="scale-card" @click="...">  <!-- 有可见中文时会自动识别 -->

自动识别规则还包括:原生 button/a/input、自动探测到的 UI 组件库控件、cursor: pointer 区域。

UI 组件库自动识别

默认 autoDetectUiLibraries: true,首次点击时根据以下条件探测页面使用的组件库:

  1. 全局变量(如 window.ELEMENT、window.antd)
  2. Vue 2 原型(如 $ELEMENT)
  3. DOM 特征选择器(如 .el-button、.ant-btn、.n-button)

探测成功后,会动态生成对应 class 前缀的交互选择器与控件类型(按钮/复选框/标签页等)。

import { detectUiLibraries, UI_LIBRARY_PROFILES } from '@cxcxcx/client-logger'

// 查看当前页面识别结果
logger.getDetectedUiLibraryNames() // ['Element UI']

// 手动指定(关闭自动探测)
createClientLogger({
  autoDetectUiLibraries: false,
  uiLibraries: ['element-ui', 'ant-design-vue']
})

// 全部内置库 id
UI_LIBRARY_PROFILES.map((item) => item.id)
// element-ui | ant-design-vue | naive-ui | vant | arco-design | view-ui | vuetify | quasar | tdesign

点击日志 ctx 中会附带 uiLibraries 字段,记录本次识别到的库名。

最简接入(任意 Vue 2 项目)

setupRouterLogger(router, logger)
setupClickTrack(router, logger)

自定义可读化文案(可选覆盖)

仅在自动识别不满足时使用 routeTitleMap / classHintMap 覆盖。

路由中文名

createClientLogger({
  routeTitleMap: {
    '/login': '登录',
    '/home': '首页'
  },
  pathPrefixTitles: [
    { prefix: '/assess/scale', title: '量表筛查' }
  ]
})

优先级:route.meta.title > matched.meta.title > routeTitleMap > pathPrefixTitles > route.name > path。


控件 class 映射(可选)

createClientLogger({
  classHintMap: {
    'train-plan-edit': '编辑训练计划',
    'el-button--primary': '主按钮'
  }
})

点击时若元素文本为空,会尝试用 class 映射生成可读名称。


自定义行为埋点文案

function buildBehaviorLog(action, ctx) {
  if (action === 'device_connected') {
    return {
      msg: `设备已连接:${ctx.deviceName}`,
      ctx: { action, deviceName: ctx.deviceName }
    }
  }
  return null  // 返回 null 则使用默认文案(action 字符串)
}

createClientLogger({ behaviorLogBuilder: buildBehaviorLog })
logger.track('device_connected', { deviceName: '脑电帽' })
// msg: "设备已连接:脑电帽"

按环境开启/关闭

SDK 本身不绑定环境变量开关,由业务项目在 createClientLogger / setupAppLogger 中自行判断。

非开发环境开启(fe-adhd 当前策略)

const runEnv = process.env.VUE_APP_RUN_ENV || process.env.NODE_ENV || ''
const isLogEnabled = runEnv !== 'development'

export const logger = createClientLogger({
  runEnv,
  enabled: isLogEnabled,
  // ...
})

export function setupAppLogger(Vue, router) {
  // 实际项目还会叠加「登录前不监控」,见下一节
  logger.init({ uploadBaseURL: baseURL, enabled: isLogEnabled })
  if (!isLogEnabled) return  // 开发环境不挂载监听,零开销

  setupVue2ErrorHandler(logger, Vue)
  setupRouterLogger(router, logger)
  setupClickTrack(router, logger)
}

仅生产环境开启

const isLogEnabled = runEnv === 'production'

多环境白名单

const LOG_ENABLED_ENVS = ['uat', 'production']
const isLogEnabled = LOG_ENABLED_ENVS.includes(runEnv)

运行时临时关闭

logger.init({ enabled: false })

注意:init 只会执行一次。若首次以 enabled: false 初始化,之后无法再通过 init({ enabled: true }) 打开。需要「先关后开」时,请采用下方 登录前不监控 的延迟激活写法。


登录前不监控

SDK 不内置登录态判断。若业务要求「未登录不采集、不上报;登录后再挂载监控」,由接入项目自行门禁。推荐做法(fe-adhd 已采用):

  1. 延迟 init / 挂载适配器:无 token 时不调用 logger.init({ enabled: true }),也不挂 setupVue2ErrorHandler / setupRouterLogger / setupClickTrack
  2. 写入与上报门禁:对 debug / info / track / warn / error / flush / uploadDiagnosticLogs 包一层,无 token 直接 return
  3. 监听 token:store.watch(或登录成功回调)在拿到 token 后再 activateLogger();session 恢复、H5 换票等同理

核心逻辑示例

import store from '@/store'

export function hasAuthToken() {
  return Boolean(store.state.token)
}

/** 写入 / 上报增加登录态门禁 */
function applyAuthGate(loggerInstance) {
  ;['debug', 'info', 'track', 'warn', 'error'].forEach((name) => {
    const original = loggerInstance[name].bind(loggerInstance)
    loggerInstance[name] = (...args) => {
      if (!hasAuthToken()) return
      return original(...args)
    }
  })

  const originalFlush = loggerInstance.flush.bind(loggerInstance)
  loggerInstance.flush = (reason = 'manual') => {
    if (!hasAuthToken()) {
      return Promise.resolve({ ok: true, skipped: true, reason: 'no_auth' })
    }
    return originalFlush(reason)
  }

  return loggerInstance
}

export const logger = applyAuthGate(
  createClientLogger({
    enabled: isLogEnabled,
    getAuthToken: () => store.state.token,
    // ...
  })
)

let loggerInitialized = false
let adaptersInstalled = false

async function activateLogger(Vue, router) {
  if (!isLogEnabled || !hasAuthToken()) return

  if (!loggerInitialized) {
    await logger.init({ uploadBaseURL: baseURL, enabled: true })
    loggerInitialized = true
  }

  if (adaptersInstalled) return
  setupVue2ErrorHandler(logger, Vue)
  setupRouterLogger(router, logger)
  setupClickTrack(router, logger)
  adaptersInstalled = true
}

export async function setupAppLogger(Vue, router) {
  if (!isLogEnabled) {
    await logger.init({ uploadBaseURL: baseURL, enabled: false })
    return
  }

  // 登录成功 / 退出后再登:靠 token 变化激活
  store.watch(
    (state) => state.token,
    (token) => {
      if (token) activateLogger(Vue, router)
    }
  )

  // 已有 token(session 恢复 / H5 换票)则立即开启
  if (hasAuthToken()) {
    await activateLogger(Vue, router)
  }
}

注意点

| 点 | 说明 | |----|------| | 判定条件 | 一般用 store.state.token(或项目等价登录凭证) | | H5 / 扫码 | 换票成功写入 token 后应同样开启监控 | | 退出登录 | 门禁会自动让写入/上报变为空操作;定时 flush 若已启动,无 token 时业务侧 flush 包装会跳过 | | 适配器只挂一次 | 用 adaptersInstalled 标记,避免重复 afterEach / 点击监听 | | 与环境开关组合 | 先判断 isLogEnabled(环境),再判断 hasAuthToken()(登录) |


环境变量配置(可选)

以下变量非 SDK 内置,需项目在 createClientLogger 中自行读取并传入,用于调节级别与上报策略:

| 变量 | 说明 | 默认值 | |------|------|--------| | VUE_APP_LOG_LEVEL | 最低级别 | info | | VUE_APP_LOG_FLUSH_INTERVAL | 上报间隔(ms) | 60000 |

示例(支持运行时动态读取):

function resolveLogFlushInterval() {
  const fromEnv = Number(process.env.VUE_APP_LOG_FLUSH_INTERVAL)
  if (Number.isFinite(fromEnv) && fromEnv > 0) return fromEnv
  return 60000
}

createClientLogger({
  level: process.env.VUE_APP_LOG_LEVEL || 'info',
  flushInterval: resolveLogFlushInterval(),
  getFlushInterval: resolveLogFlushInterval
})

构建注意事项

Vue CLI 转译

本包源码为 ES Module,若目标项目使用 Vue CLI 且未转译 node_modules,需在 vue.config.js 中添加:

module.exports = {
  transpileDependencies: ['@cxcxcx/client-logger']
}

脱敏字段扩展

默认脱敏:token、password、authorization、phone、idcard、payload、eegdata 等。clientUserId 默认不脱敏,userId 始终脱敏。

createClientLogger({
  sensitiveKeys: ['customSecretField', 'biometricData'],
  maskClientUserId: true  // 需要时对 clientUserId 开启脱敏
})

完整接入示例(fe-adhd)

本项目(多模态 ADHD 评估与训练系统)在 Vue 2 标准接入基础上,增加了 Electron 与业务埋点扩展。

目录结构

packages/client-logger/src/
├── createLogger.js      # Logger 工厂
├── globalErrors.js      # Worker / CSP / console.error / 存储配额
├── uiLibraries.js       # UI 组件库自动识别
├── readable.js          # 页面名、点击描述可读化
├── storage.js           # IndexedDB 即时持久化
├── upload.js            # 批量上报
├── adapters/
│   ├── vue2.js          # Vue 2 错误处理
│   ├── vue3.js          # Vue 3 错误处理
│   ├── vueRouter.js     # 路由 + 点击追踪
│   ├── axios.js         # API 错误
│   └── react.js         # React Error Boundary

src/utils/logger/        # fe-adhd 业务接入
├── index.js             # createClientLogger + setupAppLogger
└── businessTracks.js    # MQTT / 脑电 / 眼动 / 用户切换

src/main.js              # setupAppLogger + initBusinessTracks
src/api/request.js       # createAxiosLoggerInterceptor
electron/main.js         # setupElectronLog + registerLoggerIpcHandlers
electron/preload.js      # writeClientLog IPC 暴露
vue.config.js            # transpileDependencies

index.js(标准接入 + 登录前门禁 + 项目扩展)

import axios from 'axios'
import { createClientLogger } from '@cxcxcx/client-logger'
import { setupVue2ErrorHandler } from '@cxcxcx/client-logger/vue2'
import { setupRouterLogger, setupClickTrack } from '@cxcxcx/client-logger/vue-router'
import store from '@/store'
import baseURL from '@/api/config'
import packageJson from '/package.json'
import { getElectronAPI } from '@/utils/electron'
import { buildAdhdBehaviorLog } from './behaviorLogBuilder'

const APP_VERSION = packageJson.version
const runEnv = process.env.VUE_APP_RUN_ENV || process.env.NODE_ENV || ''

/** 客户端日志监控:除开发环境都开启 */
export const isLogEnabled = runEnv !== 'development'

const CLIENT_LOG_UPLOAD_PATH = '/clientLog/batch'

function resolveLogFlushInterval() {
  const fromEnv = Number(process.env.VUE_APP_LOG_FLUSH_INTERVAL)
  if (Number.isFinite(fromEnv) && fromEnv > 0) return fromEnv
  return 60000
}

export function hasAuthToken() {
  return Boolean(store.state.token)
}

function applyAuthGate(loggerInstance) {
  ;['debug', 'info', 'track', 'warn', 'error'].forEach((name) => {
    const original = loggerInstance[name].bind(loggerInstance)
    loggerInstance[name] = (...args) => {
      if (!hasAuthToken()) return
      return original(...args)
    }
  })
  const originalFlush = loggerInstance.flush.bind(loggerInstance)
  loggerInstance.flush = (reason = 'manual') => {
    if (!hasAuthToken()) {
      return Promise.resolve({ ok: true, skipped: true, reason: 'no_auth' })
    }
    return originalFlush(reason)
  }
  return loggerInstance
}

export const logger = applyAuthGate(
  createClientLogger({
    appVersion: APP_VERSION,
    runEnv,
    enabled: isLogEnabled,
    level: process.env.VUE_APP_LOG_LEVEL || 'info',
    flushInterval: resolveLogFlushInterval(),
    getFlushInterval: resolveLogFlushInterval,
    uploadBaseURL: baseURL,
    uploadPath: CLIENT_LOG_UPLOAD_PATH,
    dbName: 'adhd_client_log',
    legacyStorageKey: 'adhd_client_log_pending',
    deviceKey: 'adhd_log_device_id',
    getAuthToken: () => store.state.token,
    getClientUserId: () =>
      store.state.user?.currentUser?.clientUserId || store.state.h5ClientUserId || '',
    createHttpClient: () => axios.create({ baseURL, timeout: 1000 * 20 }),
    behaviorLogBuilder: buildAdhdBehaviorLog
  })
)

let loggerInitialized = false
let adaptersInstalled = false

async function activateLogger(Vue, router) {
  if (!isLogEnabled || !hasAuthToken()) return
  if (!loggerInitialized) {
    await logger.init({ uploadBaseURL: baseURL, enabled: true })
    loggerInitialized = true
  }
  if (adaptersInstalled) return
  setupVue2ErrorHandler(logger, Vue)
  setupRouterLogger(router, logger)
  setupClickTrack(router, logger, {
    skipTarget(target) {
      return Boolean(target?.closest?.('input[type="password"]'))
    }
  })
  adaptersInstalled = true
}

export async function setupAppLogger(Vue, router) {
  if (!isLogEnabled) {
    await logger.init({ uploadBaseURL: baseURL, enabled: false })
    return
  }
  store.watch(
    (state) => state.token,
    (token) => {
      if (token) activateLogger(Vue, router)
    }
  )
  if (hasAuthToken()) {
    await activateLogger(Vue, router)
  }
}

完整说明见 登录前不监控。

businessTracks.js 业务埋点

// Vuex 监听设备状态(建议同时判断 hasAuthToken)
store.watch(
  (state) => state.device?.eeg?.status,
  (next, prev) => {
    if (!hasAuthToken() || next === prev) return
    logger.track('eeg_status_change', { from: prev, to: next })
  }
)

// MQTT 事件
mqttClient.on('connect', () => {
  if (!hasAuthToken()) return
  logger.track('mqtt_connect')
})
mqttClient.on('error', (err) => {
  if (!hasAuthToken()) return
  logger.error('mqtt', { message: err.message })
})

常见问题

Q: 日志上报失败会丢失吗?

不会。每条日志产生时已写入 IndexedDB;上报失败时记录保留,下次定时 flush(或启动时、手动 flush())会重试。上报成功后按 id 删除,不会重复上报。IndexedDB 最多保留 500 条,超出会删除最旧的记录。

Q: 为什么 DevTools 里看不到日志?

1.3.4 起每条日志立即写入 IndexedDB,打开 DevTools → Application → IndexedDB → 对应 dbName → pending_logs,刷新 Object Store 即可查看。若上报已成功,记录会被删除,因此可能看不到已上报的日志。

Q: 如何运行时修改上报间隔?

// 方式 1:直接设置
logger.setFlushInterval(120000)

// 方式 2:通过 getFlushInterval 数据源(如 localStorage)+ 刷新
localStorage.setItem('adhd_log_flush_interval', '120000')
logger.refreshFlushInterval()

Q: 如何临时关闭日志?

logger.init({ enabled: false })

init 只执行一次;若需要「未登录关闭、登录后再开」,请看 登录前不监控,不要依赖二次 init({ enabled: true })。

Q: 如何实现登录前不监控?

SDK 不内置登录判断。在业务层:无 token 时不 init、不挂适配器;并对写入/flush 做 token 门禁;store.watch(token) 在登录后延迟激活。详见 登录前不监控。

Q: 如何排除某些 API 不上报?

axios.get('/api/poll', { skipLog: true })

Q: 组件内如何使用?

this.$logger.track('button_clicked', { buttonId: 'submit' })
// 或
import { logger } from '@/utils/logger'
logger.track('button_clicked')

Q: 支持 Vue 3 或 React 吗?

支持。核心层与框架无关;1.1.0 起提供官方适配器:

  • Vue 3:@cxcxcx/client-logger/vue3
  • React:@cxcxcx/client-logger/react
  • vue-router 4 页面/点击追踪:继续使用 @cxcxcx/client-logger/vue-router

Q: Electron 和 Web 上报方式一样吗?

一样。统一通过渲染进程 uploadPath 批量上报 JSON 日志。Electron 主进程仅可选写入本地 .log 文件便于排查,不再上传文件。

Q: 上报路径在哪里配置?

在接入项目的 createClientLogger 中传入 uploadPath,SDK 包内不再写死默认路径。

不需要单独封装 clientLog.js API 文件——createClientLogger 内部已集成上报逻辑。

Q: 为什么需要 createHttpClient?

SDK 不强制绑定 axios 实例,便于复用项目已有的 baseURL、拦截器、超时等配置。


导出清单

主入口 @cxcxcx/client-logger 导出:

| 导出 | 说明 | |------|------| | createClientLogger | 创建 Logger 实例 | | createLogStorage | IndexedDB 存储(高级用法) | | createUploader | 上报模块(高级用法) | | createSanitizer | 脱敏模块 | | createReadableHelpers | 可读化模块 | | createMessageResolver | 文案解析 | | formatLogTimestamp | 时间格式化 | | getUploadFieldRemarks | 获取字段备注 | | stripLogRemarks | 移除 log 上的 _remarks | | LOG_LEVELS | 级别常量 |


License

MIT