@lhxktxwd/stackflare-browser
v0.1.1
Published
给业务前端项目使用的日志上传 SDK,目标是默认接入简单、需要定制时也足够灵活。
Readme
@lhxktxwd/stackflare-browser
给业务前端项目使用的日志上传 SDK,目标是默认接入简单、需要定制时也足够灵活。
安装时只需要这个包,不需要再额外安装 @log-monitor/shared。
快速开始
import { createLogMonitorSdk } from '@lhxktxwd/stackflare-browser';
const sdk = createLogMonitorSdk({
endpoint: 'https://your-domain.com',
appId: 'mobile-h5',
clientKey: 'your-client-key',
env: 'prod',
appVersion: '1.2.3'
});
sdk.installAutoCapture();常用用法
1. 手动上报一条日志
sdk.capture({
level: 'error',
type: 'custom',
message: 'submit order failed',
extra: {
orderId: 'o_1001'
}
});2. 直接上报 Error
try {
throw new Error('manual test');
} catch (error) {
sdk.captureError(error, {
scene: 'checkout'
});
}3. 上报接口异常
sdk.captureApiError({
error,
method: 'POST',
requestUrl: '/api/orders',
status: 500,
responseBody: responseData,
extra: {
scene: 'submit-order'
}
});4. 包装 fetch
const monitoredFetch = sdk.wrapFetch(window.fetch.bind(window));
await monitoredFetch('/api/orders', {
method: 'POST',
body: JSON.stringify({ orderId: 'o_1' })
});默认会自动上报:
fetch抛出的网络异常- HTTP
4xx / 5xx响应
5. 接入 uniapp
uniapp 的 uni.request 不是标准 fetch,建议给 SDK 单独配置上传 transport,不要复用业务项目封装过的请求实例,避免把 platform、tenant-id 等业务请求头带到日志上报里。
import { createLogMonitorSdk, createUniRequestTransport } from '@lhxktxwd/stackflare-browser';
const sdk = createLogMonitorSdk({
endpoint: 'https://winsan.com.cn/view/log-monitor',
appId: 'mobile-h5',
clientKey: 'your-client-key',
env: 'prod',
transport: createUniRequestTransport(uni.request)
});
sdk.installAutoCapture();SDK 只会给上传请求设置 content-type 和 x-log-monitor-key。
6. 接入 axios
import axios from 'axios';
import { createLogMonitorSdk } from '@lhxktxwd/stackflare-browser';
const sdk = createLogMonitorSdk({
endpoint: 'https://your-domain.com',
appId: 'mobile-h5',
clientKey: 'your-client-key',
env: 'prod'
});
const request = axios.create();
const teardownAxios = sdk.instrumentAxios(request);这样 axios 请求失败时会自动上报接口异常;如果后面不需要了,可以执行 teardownAxios() 移除拦截器。
如果你们项目里已经有统一请求实例,通常就是这样接:
import axios from 'axios';
import { createLogMonitorSdk } from '@lhxktxwd/stackflare-browser';
export const sdk = createLogMonitorSdk({
endpoint: 'https://your-domain.com',
appId: 'mobile-h5',
clientKey: 'your-client-key',
env: 'prod',
getContext: () => ({
userId: window.__CURRENT_USER__?.id,
route: window.location.pathname
})
});
export const request = axios.create({
baseURL: 'https://api.example.com',
timeout: 10000
});
sdk.instrumentAxios(request);默认会上报:
- 请求抛出的异常
error.response里的状态码、状态文案、请求地址、请求方法- 请求体和响应体
如果你只想上报 5xx,或者想补充业务字段,也可以这样写:
sdk.instrumentAxios(request, {
captureHttpErrorStatus: (response) => response.status >= 500,
buildInput({ error, response }) {
return {
error,
requestUrl: response?.config?.url,
method: response?.config?.method,
status: response?.status,
extra: {
scene: 'submit-order'
}
};
}
});框架接入示例
Vue 项目
可以在 src/monitor.ts 里统一初始化:
import axios from 'axios';
import { createLogMonitorSdk } from '@lhxktxwd/stackflare-browser';
export const sdk = createLogMonitorSdk({
endpoint: 'https://your-domain.com',
appId: 'mobile-h5',
clientKey: 'your-client-key',
env: import.meta.env.PROD ? 'prod' : 'dev',
appVersion: __APP_VERSION__,
getContext: () => ({
route: window.location.pathname,
userId: window.__CURRENT_USER__?.id
})
});
export const request = axios.create({
baseURL: '/api',
timeout: 10000
});
sdk.installAutoCapture();
sdk.instrumentAxios(request);然后在 main.ts 里引入一次:
import { createApp } from 'vue';
import App from './App.vue';
import './monitor';
createApp(App).mount('#app');如果你们项目用了 vue-router,也可以在路由切换后同步上下文:
import { router } from './router';
import { sdk } from './monitor';
router.afterEach((to) => {
sdk.updateContext({
route: to.fullPath
});
});React 项目
可以在 src/monitor.ts 里统一初始化:
import axios from 'axios';
import { createLogMonitorSdk } from '@lhxktxwd/stackflare-browser';
export const sdk = createLogMonitorSdk({
endpoint: 'https://your-domain.com',
appId: 'web-admin',
clientKey: 'your-client-key',
env: import.meta.env.PROD ? 'prod' : 'dev',
getContext: () => ({
route: window.location.pathname,
userId: window.__CURRENT_USER__?.id
})
});
export const request = axios.create({
baseURL: '/api',
timeout: 10000
});
sdk.installAutoCapture();
sdk.instrumentAxios(request);然后在应用入口引入一次:
import './monitor';
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<App />
</React.StrictMode>
);如果路由是 React Router,可以在路由变化时同步上下文:
import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';
import { sdk } from './monitor';
export function RouteMonitor() {
const location = useLocation();
useEffect(() => {
sdk.updateContext({
route: `${location.pathname}${location.search}`
});
}, [location.pathname, location.search]);
return null;
}灵活配置
const sdk = createLogMonitorSdk({
endpoint: 'https://your-domain.com',
appId: 'mobile-h5',
clientKey: 'your-client-key',
env: 'prod',
appVersion: '1.2.3',
defaultContext: {
route: '/orders',
extra: {
tenant: 'cn'
}
},
getContext: () => ({
userId: currentUser?.id,
extra: {
release: window.__APP_VERSION__
}
}),
beforeSend(payload) {
if (payload.message.includes('ignore')) {
return null;
}
return {
...payload,
extra: {
...(payload.extra || {}),
appChannel: 'h5'
}
};
},
sanitize: {
sensitiveKeys: ['payToken', /^x-/i],
replacement: '[Filtered]'
},
batch: {
maxBatchSize: 10,
flushIntervalMs: 5000,
maxRetries: 2,
retryDelayMs: 1500
}
});SDK 默认会在发送前脱敏常见敏感字段,例如 authorization、cookie、token、password、secret、phone、idCard。如需完全关闭:
const sdk = createLogMonitorSdk({
endpoint: 'https://your-domain.com',
appId: 'mobile-h5',
clientKey: 'your-client-key',
env: 'prod',
sanitize: false
});wrapFetch 和 instrumentAxios 也支持自定义:
const monitoredFetch = sdk.wrapFetch(window.fetch.bind(window), {
captureHttpErrorStatus: (response) => response.status >= 500,
buildInput({ input, response, error }) {
if (!response && !error) return null;
return {
error,
requestUrl: typeof input === 'string' ? input : undefined,
status: response?.status,
extra: {
scene: 'order-submit'
}
};
}
});能力说明
capture:手动上报任意日志captureError:快速上报ErrorcaptureApiError:快速上报接口异常wrapFetch:自动采集fetch请求异常instrumentAxios:自动采集axios请求异常installAutoCapture:自动采集运行时异常updateContext:动态更新公共上下文flush:立即发送当前批次队列destroy:卸载监听并清空发送队列
Source Map 自动上传
发布构建后可以在 CI/CD 中上传 .map 文件:
npx @lhxktxwd/stackflare-browser upload-sourcemaps \
--endpoint https://your-domain.com \
--upload-key "$STACKFLARE_SOURCE_MAP_UPLOAD_KEY" \
--app-id mobile-h5 \
--app-version "$APP_VERSION" \
--dir dist/assets \
--bundle-prefix /assetsSTACKFLARE_SOURCE_MAP_UPLOAD_KEY 可以在后台“应用配置”里复制,对应具体 app,只能上传该 app 的 Source Map。
也可以用环境变量:
STACKFLARE_ENDPOINT=https://your-domain.com \
STACKFLARE_SOURCE_MAP_UPLOAD_KEY=xxx \
STACKFLARE_APP_ID=mobile-h5 \
STACKFLARE_APP_VERSION=1.2.3 \
STACKFLARE_SOURCEMAP_DIR=dist/assets \
npx @lhxktxwd/stackflare-browser upload-sourcemapsCLI 会优先读取 Source Map JSON 中的 file 字段作为打包文件名;没有 file 字段时,会用 .map 文件相对 --dir 的路径去掉 .map 后缀。
如果项目已经安装了 @lhxktxwd/stackflare-browser,也可以使用包管理器执行本地 bin:
pnpm exec stackflare upload-sourcemaps构建与发包准备
当前仓库已经补好包构建配置:
pnpm build:packages执行后会生成:
packages/shared/distpackages/sdk/dist
当前 SDK 已经把发布所需的共享类型内聚到了包内部,外部使用时只需要安装 @lhxktxwd/stackflare-browser。
发布前可以先做一次本地打包校验:
pnpm pack:sdk这会先构建产物,再执行 npm pack --dry-run,用于确认最终会被发布的文件是否正确。
发布流程
推荐在仓库根目录使用一键发布命令:
npm login --registry https://registry.npmjs.org/
npm whoami --registry https://registry.npmjs.org/# 发布 beta 版本
pnpm release:sdk:beta
# 发布正式版本
pnpm release:sdk:latest这两个命令都会先执行测试、构建包产物和 npm pack --dry-run,通过后才发布。
如果没有登录 npm,命令会在发布前直接失败并提示先登录。
如果 npm 账号开启了双因素认证,发布时需要传一次性验证码:
NPM_CONFIG_OTP=123456 pnpm release:sdk:beta
NPM_CONFIG_OTP=123456 pnpm release:sdk:latestCI/CD 发布时需要使用 npm granular access token,并在 npm token 配置里开启 publish 所需的 2FA bypass 权限。
发布前建议确认:
packages/sdk/package.json里的name和version已经是你要发布的最终值- 你已经登录 npm:
npm login - 当前 git 提交已经稳定,避免发布后源码和 npm 包内容对不上
