@pluve/logger-sdk 0.0.13 → 0.0.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -12,33 +12,26 @@
12
12
  ## 快速开始
13
13
 
14
14
  ```ts
15
- import { LoggerSDK } from './core/loggerSDK';
15
+ import { LoggerSDK } from '@pluve/logger-sdk';
16
16
 
17
17
  const sdk = LoggerSDK.getInstance();
18
18
 
19
19
  sdk.init({
20
- // endpoint: 'https://logger.yfpharmacy.com/log', // 实际使用以 config 中 API 为准
21
- appId: 'fe-vue-pc-seed-frontend',
22
- env: 'develop',
23
- logStage: 'develop',
24
- debug: true,
25
- // 采集与性能
20
+ appId: 'app',
21
+ logStage: 'product',
22
+ debug: false,
26
23
  enableGzip: true,
27
- enableBatch: true,
28
- batchSize: 10,
24
+ enableBatch: false,
25
+ batchSize: 20,
29
26
  batchInterval: 15000,
30
27
  maxPixelUrlLen: 8192,
31
- // 重试
32
- enableRetry: true,
33
- maxRetries: 3,
34
- retryDelay: 1000,
35
- retryBackoff: true,
36
- // 身份与鉴权
28
+ enableRetry: false,
37
29
  userId: 'u-001',
38
30
  storeCode: 's-001',
39
31
  token: '<token>',
40
- // 采样率(可选)
41
32
  sampleRate: 1,
33
+ enableAutoCapture: true,
34
+ autoCapture: { js: true, promise: true, resource: true, wechat: true },
42
35
  });
43
36
 
44
37
  // 业务日志
@@ -57,6 +50,7 @@ sdk.setStage('testing');
57
50
  ## 配置项(节选)
58
51
 
59
52
  - enableGzip:是否启用 gzip 压缩(默认 true)
53
+ - gzipBatchMinSize:开启批量压缩的最小 items 数量(默认 2)
60
54
  - enableBatch:是否启用批量上报(默认需显式开启)
61
55
  - batchSize:批量阈值(默认 10)
62
56
  - batchInterval:批量定时器间隔(默认 15000ms)
@@ -64,16 +58,24 @@ sdk.setStage('testing');
64
58
  - enableStorage:队列持久化开关(默认 true)
65
59
  - enableRetry/maxRetries/retryDelay/retryBackoff:重试相关配置
66
60
  - sampleRate/levelSampleRate/sourceSampleRate/pathSampleRate:采样配置
61
+ - enableAutoCapture:自动采集总开关(默认开启)
62
+ - autoCapture:细粒度开关 { js, promise, resource, wechat }
67
63
 
68
- 详细定义参考类型文件:[sdkOptions.ts](src/pages/logger-sdk-tester/logger-sdk-v2/types/sdkOptions.ts).
64
+ 说明:
65
+
66
+ - enableGzip 仅在启用批量模式时生效;当 items 数量 ≥ gzipBatchMinSize 时自动压缩,否则走未压缩编码
67
+ - enableStorage 默认在开启批量时启用;即使未开启批量,若 enableStorage 未显式设为 false,也会启用
68
+ - autoCapture 默认全部开启;可按需关闭各子项,或关闭总开关
69
+
70
+ 详细定义参考类型文件:[sdkOptions.ts](./src/types/sdkOptions.ts).
69
71
 
70
72
  ## 压缩实现
71
73
 
72
74
  - gzipCompress(data)
73
75
  - 浏览器支持 CompressionStream 时,流式 gzip 压缩,再转 Base64 字符串
74
- - 不支持时,回退到 fflate.gzipSync(默认压缩级别),转 Base64
76
+ - 不支持时,回退到 fflate.gzipSync,使用 btoa/Buffer 生成 Base64
75
77
  - 返回值为 Base64 字符串,适用于传输(像素图 URL/JSON 体)
76
- - 代码参考:[compression.ts](src/pages/logger-sdk-tester/logger-sdk-v2/compress/compression.ts).
78
+ - 代码参考:[compression.ts](./src/compress/compression.ts).
77
79
  - 备注:当前默认压缩级别,无级别可调参数暴露;如需级别控制可在 fflate 路径引入配置。
78
80
 
79
81
  ## 传输适配
@@ -85,7 +87,7 @@ sdk.setStage('testing');
85
87
  - 请求体字符串化 safeStringify
86
88
  - enableGzip 时使用 gzipCompress 生成 Base64,Content-Type 设为 `application/json; charset=utf-8`
87
89
  - PixelImageTransport
88
- - 字符串化 items,enableGzip 时压缩为 Base64URL 参数附带 `gzip=1`
90
+ - 字符串化 items,enableGzip 时压缩为 Base64;非 gzip 情况下优先使用全局 Base64,其次 btoa,再次 encodeURIComponent;URL 参数附带 `gzip=1`
89
91
  - 构造像素上报 URL(含 appId、appStage、items、cacheBuster)
90
92
  - URL 超长打印警告
91
93
  - BeaconTransport
@@ -93,26 +95,26 @@ sdk.setStage('testing');
93
95
 
94
96
  代码参考:
95
97
 
96
- - 适配器选择:[transportAdapter.ts](src/pages/logger-sdk-tester/logger-sdk-v2/transport/transportAdapter.ts)
97
- - WeChat:[wechatTransport.ts](src/pages/logger-sdk-tester/logger-sdk-v2/transport/wechatTransport.ts)
98
- - Pixel Image:[pixelImageTransport.ts](src/pages/logger-sdk-tester/logger-sdk-v2/transport/pixelImageTransport.ts)
99
- - Beacon:[beaconTransport.ts](src/pages/logger-sdk-tester/logger-sdk-v2/transport/beaconTransport.ts)
98
+ - 适配器选择:[transportAdapter.ts](./src/transport/transportAdapter.ts)
99
+ - WeChat:[wechatTransport.ts](./src/transport/wechatTransport.ts)
100
+ - Pixel Image:[pixelImageTransport.ts](./src/transport/pixelImageTransport.ts)
101
+ - Beacon:[beaconTransport.ts](./src/transport/beaconTransport.ts)
100
102
 
101
103
  ## 批量与队列
102
104
 
103
105
  - 启用批量后,事件入队,当队列大小达到 batchSize 或定时器触发时 flush
104
106
  - flush:按 appId|stage 分组与分块(最多 200/块),批量发送成功后出队
105
- - 队列持久化:localforage 存储,页面刷新/卸载可恢复
107
+ - 队列持久化:优先 IndexedDB(localforage),失败回退 localStorage;仅在浏览器环境启用
106
108
  - 页面事件触发 flush:visibilitychange(hidden)、pagehide、beforeunload
107
109
  - 代码参考:
108
- - [queueManager.ts](src/pages/logger-sdk-tester/logger-sdk-v2/core/queueManager.ts)
109
- - [loggerSDK.ts(flush/sendBatch)](src/pages/logger-sdk-tester/logger-sdk-v2/core/loggerSDK.ts#L452-L485)
110
+ - [queueManager.ts](./src/core/queueManager.ts)
111
+ - [loggerSDK.ts(flush/sendBatch)](./src/core/loggerSDK.ts#L394-L467)
110
112
 
111
113
  ## 重试策略
112
114
 
113
115
  - 单个事件 sendEvent 与批量 sendBatch 均支持 executeWithRetry
114
116
  - 可配置最大次数、基础延迟、指数退避
115
- - 代码参考:[retryManager.ts](src/pages/logger-sdk-tester/logger-sdk-v2/core/retryManager.ts)
117
+ - 代码参考:[retryManager.ts](./src/core/retryManager.ts)
116
118
 
117
119
  ## 采样与开关
118
120
 
@@ -124,13 +126,13 @@ sdk.setStage('testing');
124
126
  - 综合 level/source/path/global 采样率
125
127
  - 基于 seed 哈希概率决定是否发送
126
128
  - 参考:
127
- - [loggerSDK.ts(shouldSend)](src/pages/logger-sdk-tester/logger-sdk-v2/core/loggerSDK.ts#L348-L411)
128
- - [api.ts](src/pages/logger-sdk-tester/logger-sdk-v2/types/api.ts)
129
+ - [loggerSDK.ts(shouldSend)](./src/core/loggerSDK.ts#L324-L365)
130
+ - [api.ts](./src/types/api.ts)
129
131
 
130
132
  ## 数据格式
131
133
 
132
134
  - LogEvent:标准化日志格式(logId/seq/appId/stage/level/traceId/frontendId/url/location/message/throwable/userId/storeCode/tags)
133
- - 类型参考:[logEvent.ts](src/pages/logger-sdk-tester/logger-sdk-v2/types/logEvent.ts)
135
+ - 类型参考:[logEvent.ts](./src/types/logEvent.ts)
134
136
  - ReportData:上报批次(appId/appStage/items[])
135
137
 
136
138
  ## 限制与注意
@@ -139,8 +141,72 @@ sdk.setStage('testing');
139
141
  - 像素图:URL 长度受限;压缩可显著降低长度但 Base64 会比二进制略增
140
142
  - 压缩:CompressionStream 不提供压缩级别参数;fflate 同步压缩适合小批量数据
141
143
  - 安全:不要在日志中包含敏感数据(token、密码、隐私信息等)
144
+ - 依赖:js-base64、localforage 均为 peerDependencies;UMD 外部化由应用或 CDN 提供全局;ESM/CJS 下由应用安装解析
142
145
 
143
146
  ## 流程图
144
147
 
145
148
  - 完整 Mermaid 流程图见文件:
146
- - [logger-sdk.mermaid](src/pages/logger-sdk-tester/logger-sdk-v2/logger-sdk.mermaid)
149
+ - [logger-sdk.mermaid](./src/logger-sdk.mermaid)
150
+ - [logger-sdk.svg](./src/logger-sdk.svg)
151
+
152
+ ## 类型与打包
153
+
154
+ - 类型入口:package.json exports.types 与 typesVersions 指向 dist/types
155
+ - TypeScript 解析:推荐在应用设置 moduleResolution: "bundler" 或 "nodenext"/"node16"
156
+ - 声明构建:`yarn build-types`(tsconfig.types.json 禁用默认 @types 扫描,避免冲突)
157
+
158
+ ## 依赖说明
159
+
160
+ - peerDependencies(由应用安装或 CDN 提供)
161
+ - js-base64(^3.7.5)
162
+ - localforage(^1.10.0)
163
+ - 安装示例(应用侧)
164
+ - `yarn add js-base64 localforage`
165
+ - CDN 全局(UMD 产物)
166
+ - 先引入 js-base64 提供全局 Base64,再引入 logger-sdk UMD
167
+ - localforage 可选(IndexedDB 持久化),未提供时回退到 localStorage
168
+ - 构建外部化(UMD)
169
+ - .fatherrc.ts 已将 js-base64 与 localforage 外部化映射到全局变量
170
+ - 运行时回退策略
171
+ - PixelImageTransport:优先使用全局 Base64,其次 btoa,再次 encodeURIComponent
172
+ - 压缩:CompressionStream 优先;回退 fflate.gzipSync + btoa/Buffer
173
+ - 队列:优先 localforage(IndexedDB);失败回退 localStorage(仅浏览器启用)
174
+
175
+ ## 编码细节(传输 × 压缩)
176
+
177
+ - WechatTransport(POST)
178
+ - 未压缩
179
+ - Body:JSON(safeStringify(payload))
180
+ - Header:Content-Type: application/json;token: <opts.token>
181
+ - items:字符串化后的数组(message/throwable 等为字符串)
182
+ - Endpoint:[config.getReportApi](.src/config/index.ts)
183
+ - 启用压缩
184
+ - items:safeStringify(items) 后使用 [gzipCompress](.src/compress/compression.ts) 生成 Base64
185
+ - Body:JSON(...payload, items: Base64)
186
+ - Header:Content-Type: application/json; charset=utf-8;token: <opts.token>
187
+
188
+ - BeaconTransport(sendBeacon)
189
+ - 未压缩
190
+ - Body:JSON(safeStringify(payload))
191
+ - Content-Type:application/json(通过 Blob 指定)
192
+ - 发送方式:navigator.sendBeacon(endpoint, blob)
193
+ - 启用压缩(满足批量条件或显式开启)
194
+ - items:safeStringify(items) 后 gzipCompress → Base64
195
+ - Body:JSON(...payload, items: Base64)
196
+ - Content-Type:application/json(Blob)
197
+
198
+ - PixelImageTransport(Image GET)
199
+ - 未压缩
200
+ - items:safeStringify(items)
201
+ - 编码:优先使用 Base64.encode(items),其次 encodeURIComponent(items)
202
+ - Query 参数:items=encodeURIComponent(编码结果);gzip=0;携带 appId、appStage、cacheBuster
203
+ - 启用压缩
204
+ - items:safeStringify(items) 后 gzipCompress → Base64
205
+ - Query 参数:items=encodeURIComponent(Base64);gzip=1;携带 appId、appStage、cacheBuster
206
+ - URL 长度检查:默认最大 8192,超长打印警告
207
+
208
+ 说明:
209
+
210
+ - Base64 来源优先级:全局 Base64(CDN 或应用注入) > 原生 btoa > encodeURIComponent 回退
211
+ - 压缩实现优先级:CompressionStream(浏览器) > fflate.gzipSync(通用),均返回 Base64 字符串便于传输
212
+ - 所有传输模式的 items 都基于统一结构(level/traceId/frontendId/location/message/throwable)
@@ -40,7 +40,7 @@ async function gzipCompress(data) {
40
40
  } catch (e) {
41
41
  console.log("gzipCompress 压缩失败", e);
42
42
  }
43
- return data;
43
+ throw new Error("gzipCompress 不支持压缩");
44
44
  }
45
45
  function convert2Base64FromArray(data) {
46
46
  let binary = "";
@@ -55,7 +55,7 @@ function convert2Base64FromArray(data) {
55
55
  }
56
56
  throw new Error("convert2Base64FromArray 不支持转换");
57
57
  }
58
- function convert2Base64(data) {
58
+ async function convert2Base64(data) {
59
59
  if (typeof btoa !== "undefined") {
60
60
  try {
61
61
  return btoa(data);
@@ -65,6 +65,12 @@ function convert2Base64(data) {
65
65
  if (typeof Buffer !== "undefined") {
66
66
  return Buffer.from(data, "base64").toString("base64");
67
67
  }
68
+ try {
69
+ const mod = await import("js-base64");
70
+ return mod.Base64.encode(data);
71
+ } catch (e) {
72
+ console.log("convert2Base64 动态引入 js-base64 库失败", e);
73
+ }
68
74
  return data;
69
75
  }
70
76
  function isGzipSupported() {
@@ -50,9 +50,9 @@ var LoggerSDK = class {
50
50
  debug: !!options.debug,
51
51
  /** 是否启用 gzip 压缩,默认 true */
52
52
  enableGzip: options.enableGzip !== false,
53
- // 默认启用
54
- gzipOnlyInBatchMode: options.gzipOnlyInBatchMode !== false,
55
- // 默认启用
53
+ // 默认启用,但仅在批量模式开启情况下有效
54
+ gzipBatchMinSize: options.gzipBatchMinSize || 2,
55
+ // 默认 2 条数据开启 gzip 压缩
56
56
  /** 最大像素图 URL 长度,默认 1900 */
57
57
  maxPixelUrlLen: options.maxPixelUrlLen || 8192,
58
58
  // 批量上报配置(默认关闭,需显式开启)
@@ -23,10 +23,7 @@ var QueueManager = class {
23
23
  return this.localforage;
24
24
  }
25
25
  try {
26
- const mod = await import(
27
- /* @vite-ignore */
28
- "localforage"
29
- );
26
+ const mod = await import("localforage");
30
27
  this.localforage = mod;
31
28
  return this.localforage;
32
29
  } catch (e) {
@@ -18,7 +18,7 @@ var BeaconTransport = class {
18
18
  let body = typeof payload === "string" ? payload : safeStringify(payload);
19
19
  const endpoint = getReportApi(((_a = this.opts) == null ? void 0 : _a.env) || "develop");
20
20
  let contentType = "application/json";
21
- if (((_b = this.opts) == null ? void 0 : _b.enableGzip) && (!((_c = this.opts) == null ? void 0 : _c.gzipOnlyInBatchMode) || payload.items.length > 0)) {
21
+ if (((_b = this.opts) == null ? void 0 : _b.enableGzip) && payload.items.length >= ((_c = this.opts) == null ? void 0 : _c.gzipBatchMinSize)) {
22
22
  const compressedItems = await gzipCompress(safeStringify(payload.items));
23
23
  body = safeStringify({
24
24
  ...payload,
@@ -20,7 +20,7 @@ var PixelImageTransport = class {
20
20
  const param = "items";
21
21
  const maxLen = ((_b = this.opts) == null ? void 0 : _b.maxPixelUrlLen) || 8192;
22
22
  let compressedBody;
23
- if (((_c = this.opts) == null ? void 0 : _c.enableGzip) && (!((_d = this.opts) == null ? void 0 : _d.gzipOnlyInBatchMode) || payload.items.length > 0)) {
23
+ if (((_c = this.opts) == null ? void 0 : _c.enableGzip) && payload.items.length >= ((_d = this.opts) == null ? void 0 : _d.gzipBatchMinSize)) {
24
24
  const t = now();
25
25
  logDebug(!!((_e = this.opts) == null ? void 0 : _e.debug), "PixelImage request gzip compress body: ", body);
26
26
  compressedBody = await gzipCompress(body);
@@ -28,7 +28,7 @@ var PixelImageTransport = class {
28
28
  logDebug(!!((_g = this.opts) == null ? void 0 : _g.debug), `original body size: ${body.length}, compressed body size: ${compressedBody.length}`);
29
29
  logDebug(!!((_h = this.opts) == null ? void 0 : _h.debug), "PixelImage request gzip compress body: ", compressedBody);
30
30
  } else {
31
- compressedBody = convert2Base64(body);
31
+ compressedBody = await convert2Base64(body);
32
32
  }
33
33
  const cacheBuster = `_=${Date.now()}`;
34
34
  const qs = `appId=${((_i = this.opts) == null ? void 0 : _i.appId) || ""}&appStage=${((_j = this.opts) == null ? void 0 : _j.logStage) || ""}&${param}=${encodeURIComponent(compressedBody)}&gzip=${((_k = this.opts) == null ? void 0 : _k.enableGzip) ? 1 : 0}&${cacheBuster}`;
@@ -19,7 +19,7 @@ var WechatTransport = class {
19
19
  const endpoint = getReportApi(((_a = this.opts) == null ? void 0 : _a.env) || "develop");
20
20
  const timeout = 1e4;
21
21
  let contentType = "application/json";
22
- if (((_b = this.opts) == null ? void 0 : _b.enableGzip) && (!((_c = this.opts) == null ? void 0 : _c.gzipOnlyInBatchMode) || payload.items.length > 0)) {
22
+ if (((_b = this.opts) == null ? void 0 : _b.enableGzip) && payload.items.length >= ((_c = this.opts) == null ? void 0 : _c.gzipBatchMinSize)) {
23
23
  const t = now();
24
24
  logDebug(!!((_d = this.opts) == null ? void 0 : _d.debug), "WeChat request enable gzip compress: ", t);
25
25
  const compressedItems = await gzipCompress(safeStringify(payload.items));
@@ -5,7 +5,7 @@
5
5
  */
6
6
  export declare function gzipCompress(data: string): Promise<string>;
7
7
  export declare function convert2Base64FromArray(data: any): any;
8
- export declare function convert2Base64(data: string): string;
8
+ export declare function convert2Base64(data: string): Promise<string>;
9
9
  /**
10
10
  * 检查是否支持 gzip 压缩
11
11
  */
@@ -15,10 +15,10 @@ export interface SDKOptions {
15
15
  enableBatch?: boolean;
16
16
  /** 批量上报最大数量,默认 10 */
17
17
  batchSize?: number;
18
- /** 是否启用 gzip 压缩,默认 true */
18
+ /** 是否启用 gzip 压缩,默认 true (仅在批量模式下有效) */
19
19
  enableGzip?: boolean;
20
- /** 是否仅在批量模式下启用 gzip 压缩,默认 true */
21
- gzipOnlyInBatchMode?: boolean;
20
+ /** 批量模式下启用 gzip 压缩的最小 items 数量,默认 2 */
21
+ gzipBatchMinSize?: number;
22
22
  /** 最大像素图 URL 长度,默认 1900 */
23
23
  maxPixelUrlLen?: number;
24
24
  /** 批量上报时间间隔(毫秒),默认 5000 */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pluve/logger-sdk",
3
- "version": "0.0.13",
3
+ "version": "0.0.15",
4
4
  "description": "logger sdk",
5
5
  "keywords": [
6
6
  "logger"
@@ -58,15 +58,14 @@
58
58
  },
59
59
  "devDependencies": {
60
60
  "jsdom": "^27.4.0",
61
- "vitest": "^4.0.15",
62
- "localforage": "^1.10.0",
63
- "@mermaid-js/mermaid-cli": "^10.9.1"
61
+ "vitest": "^4.0.15"
64
62
  },
65
63
  "dependencies": {
66
64
  "fflate": "^0.8.2",
67
65
  "stacktrace-js": "^2.0.2"
68
66
  },
69
67
  "peerDependencies": {
70
- "localforage": "^1.10.0"
68
+ "localforage": "^1.10.0",
69
+ "js-base64": "^3.7.8"
71
70
  }
72
71
  }