@pluve/logger-sdk 0.0.26 → 0.0.28
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 +18 -27
- package/dist/esm/core/loggerSDK.js +43 -51
- package/dist/esm/index.js +3 -1
- package/dist/types/core/loggerSDK.d.ts +3 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/types/sdkOptions.d.ts +7 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @pluve/logger-sdk 说明文档
|
|
2
2
|
|
|
3
3
|
## 概览
|
|
4
4
|
|
|
5
|
-
-
|
|
5
|
+
- 环境支持:浏览器
|
|
6
6
|
- 采集与采样:开关、日志等级白名单、分级/来源/路径采样率
|
|
7
|
-
- 传输适配:Beacon、像素图 Image
|
|
7
|
+
- 传输适配:Beacon、像素图 Image
|
|
8
8
|
- 批量与队列:批量阈值与定时器、队列持久化(localforage)
|
|
9
9
|
- 重试策略:可配置最大次数与指数退避
|
|
10
10
|
- 压缩能力:可选 gzip 压缩,优先使用浏览器 CompressionStream,回退 fflate.gzipSync
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
## 快速开始
|
|
13
13
|
|
|
14
14
|
```ts
|
|
15
|
-
import { LoggerSDK } from '@pluve/logger-sdk';
|
|
15
|
+
import { LoggerSDK, SecurityType } from '@pluve/logger-sdk';
|
|
16
16
|
|
|
17
17
|
const sdk = LoggerSDK.getInstance();
|
|
18
18
|
|
|
@@ -29,16 +29,17 @@ sdk.init({
|
|
|
29
29
|
userId: 'u-001',
|
|
30
30
|
storeCode: 's-001',
|
|
31
31
|
token: '<token>',
|
|
32
|
+
securityType: SecurityType.BASE,
|
|
32
33
|
sampleRate: 1,
|
|
33
34
|
enableAutoCapture: true,
|
|
34
|
-
autoCapture: { js: true, promise: true, resource: true
|
|
35
|
+
autoCapture: { js: true, promise: true, resource: true },
|
|
35
36
|
});
|
|
36
37
|
|
|
37
38
|
// 业务日志
|
|
38
39
|
sdk.track({
|
|
39
40
|
message: '用户点击了购买按钮',
|
|
40
41
|
traceId: 'trace-xyz',
|
|
41
|
-
logLevel: '
|
|
42
|
+
logLevel: 'ERROR',
|
|
42
43
|
});
|
|
43
44
|
|
|
44
45
|
// 动态设置
|
|
@@ -51,14 +52,20 @@ sdk.setStage('testing');
|
|
|
51
52
|
|
|
52
53
|
- enableGzip:是否启用 gzip 压缩(默认 true)
|
|
53
54
|
- enableBatch:是否启用批量上报(默认需显式开启)
|
|
54
|
-
- batchSize:批量阈值(默认
|
|
55
|
+
- batchSize:批量阈值(默认 20)
|
|
55
56
|
- batchInterval:批量定时器间隔(默认 15000ms)
|
|
56
57
|
- maxPixelUrlLen:像素图 URL 最大长度(默认 8192)
|
|
57
58
|
- enableStorage:队列持久化开关(默认 true)
|
|
58
|
-
-
|
|
59
|
+
- storeCode:店铺编码(可选)
|
|
60
|
+
- securityType:安全类型(默认 SecurityType.BASE / 'BASE')
|
|
61
|
+
- SecurityType.ACCOUNT_CENTER / '2B':账号中心
|
|
62
|
+
- SecurityType.EHP / 'EHP':EHP 医疗
|
|
63
|
+
- SecurityType.CUSTOMER / 'CUSTOMER':C 端会员(预留)
|
|
64
|
+
- SecurityType.BASE / 'BASE':兜底 MD5
|
|
65
|
+
- token:鉴权 Token
|
|
59
66
|
- sampleRate/levelSampleRate/sourceSampleRate/pathSampleRate:采样配置
|
|
60
67
|
- enableAutoCapture:自动采集总开关(默认开启)
|
|
61
|
-
- autoCapture:细粒度开关 { js, promise, resource
|
|
68
|
+
- autoCapture:细粒度开关 { js, promise, resource }
|
|
62
69
|
|
|
63
70
|
说明:
|
|
64
71
|
|
|
@@ -80,11 +87,7 @@ sdk.setStage('testing');
|
|
|
80
87
|
## 传输适配
|
|
81
88
|
|
|
82
89
|
- TransportAdapter 根据环境选择传输器:
|
|
83
|
-
- 微信:WechatTransport(wx.request POST,支持 gzip)
|
|
84
90
|
- 浏览器:优先 BeaconTransport(sendBeacon Blob),否则 PixelImageTransport(Image GET)
|
|
85
|
-
- WechatTransport
|
|
86
|
-
- 请求体字符串化 safeStringify
|
|
87
|
-
- enableGzip 时使用 gzipCompress 生成 Base64,Content-Type 设为 `application/json; charset=utf-8`
|
|
88
91
|
- PixelImageTransport
|
|
89
92
|
- 字符串化 items,enableGzip 时压缩为 Base64;非 gzip 情况下优先使用全局 Base64,其次 btoa,再次 encodeURIComponent;URL 参数附带 `gzip=1`
|
|
90
93
|
- 构造像素上报 URL(含 appId、appStage、items、cacheBuster)
|
|
@@ -95,7 +98,6 @@ sdk.setStage('testing');
|
|
|
95
98
|
代码参考:
|
|
96
99
|
|
|
97
100
|
- 适配器选择:[transportAdapter.ts](./src/transport/transportAdapter.ts)
|
|
98
|
-
- WeChat:[wechatTransport.ts](./src/transport/wechatTransport.ts)
|
|
99
101
|
- Pixel Image:[pixelImageTransport.ts](./src/transport/pixelImageTransport.ts)
|
|
100
102
|
- Beacon:[beaconTransport.ts](./src/transport/beaconTransport.ts)
|
|
101
103
|
|
|
@@ -145,8 +147,8 @@ sdk.setStage('testing');
|
|
|
145
147
|
## 流程图
|
|
146
148
|
|
|
147
149
|
- 完整 Mermaid 流程图见文件:
|
|
148
|
-
- [logger-sdk.mermaid](./
|
|
149
|
-
- [logger-sdk.svg](./
|
|
150
|
+
- [logger-sdk.mermaid](./docs/logger-sdk.mermaid)
|
|
151
|
+
- [logger-sdk.svg](./docs/logger-sdk.svg)
|
|
150
152
|
|
|
151
153
|
## 类型与打包
|
|
152
154
|
|
|
@@ -173,17 +175,6 @@ sdk.setStage('testing');
|
|
|
173
175
|
|
|
174
176
|
## 编码细节(传输 × 压缩)
|
|
175
177
|
|
|
176
|
-
- WechatTransport(POST)
|
|
177
|
-
- 未压缩
|
|
178
|
-
- Body:JSON(safeStringify(payload))
|
|
179
|
-
- Header:Content-Type: application/json;token: <opts.token>
|
|
180
|
-
- items:字符串化后的数组(message/throwable 等为字符串)
|
|
181
|
-
- Endpoint:[config.getReportApi](.src/config/index.ts)
|
|
182
|
-
- 启用压缩
|
|
183
|
-
- items:safeStringify(items) 后使用 [gzipCompress](.src/compress/compression.ts) 生成 Base64
|
|
184
|
-
- Body:JSON(...payload, items: Base64)
|
|
185
|
-
- Header:Content-Type: application/json; charset=utf-8;token: <opts.token>
|
|
186
|
-
|
|
187
178
|
- BeaconTransport(sendBeacon)
|
|
188
179
|
- 未压缩
|
|
189
180
|
- Body:JSON(safeStringify(payload))
|
|
@@ -21,6 +21,7 @@ var LoggerSDK = class {
|
|
|
21
21
|
this.closed = false;
|
|
22
22
|
/** 预收集的环境信息 tags(仅用于初始化上报) */
|
|
23
23
|
this.envTags = {};
|
|
24
|
+
this.envTagsStr = "";
|
|
24
25
|
/** 是否已初始化 */
|
|
25
26
|
this.initialized = false;
|
|
26
27
|
/** 是否正在上报 */
|
|
@@ -46,6 +47,8 @@ var LoggerSDK = class {
|
|
|
46
47
|
// endpoint: options.endpoint,
|
|
47
48
|
appId: `${options.appId}`,
|
|
48
49
|
// 统一与 zadig 部署应用 APPID 保持一致
|
|
50
|
+
/** 应用版本 */
|
|
51
|
+
appVersion: options.appVersion || "unknown",
|
|
49
52
|
env: options.env || "develop",
|
|
50
53
|
/** 日志环境 */
|
|
51
54
|
logStage: options.logStage,
|
|
@@ -64,7 +67,7 @@ var LoggerSDK = class {
|
|
|
64
67
|
// 持久化存储配置
|
|
65
68
|
enableStorage: options.enableBatch === true || options.enableStorage !== false,
|
|
66
69
|
// 默认启用
|
|
67
|
-
storagePrefix: options.storagePrefix ||
|
|
70
|
+
storagePrefix: options.storagePrefix || `${options.appId}_logger_sdk`,
|
|
68
71
|
// 重试配置
|
|
69
72
|
enableRetry: options.enableRetry !== false,
|
|
70
73
|
// 默认启用
|
|
@@ -79,19 +82,19 @@ var LoggerSDK = class {
|
|
|
79
82
|
token: options.token,
|
|
80
83
|
sampleRate: typeof options.sampleRate === "number" ? options.sampleRate : 1,
|
|
81
84
|
levelSampleRate: options.levelSampleRate || {
|
|
82
|
-
ERROR:
|
|
83
|
-
WARN:
|
|
84
|
-
INFO:
|
|
85
|
-
FATAL:
|
|
85
|
+
ERROR: 80,
|
|
86
|
+
WARN: 50,
|
|
87
|
+
INFO: 10,
|
|
88
|
+
FATAL: 100
|
|
86
89
|
},
|
|
87
90
|
sourceSampleRate: options.sourceSampleRate || {
|
|
88
|
-
js:
|
|
89
|
-
promise:
|
|
90
|
-
resource:
|
|
91
|
-
custom:
|
|
91
|
+
js: 80,
|
|
92
|
+
promise: 50,
|
|
93
|
+
resource: 20,
|
|
94
|
+
custom: 100
|
|
92
95
|
},
|
|
93
96
|
pathSampleRate: options.pathSampleRate || {
|
|
94
|
-
"/":
|
|
97
|
+
"/": 100
|
|
95
98
|
},
|
|
96
99
|
enableAutoCapture: options.enableAutoCapture !== false,
|
|
97
100
|
// 默认启用
|
|
@@ -120,6 +123,7 @@ var LoggerSDK = class {
|
|
|
120
123
|
this.transporter = void 0;
|
|
121
124
|
this.sessionId = getSessionId();
|
|
122
125
|
this.envTags = collectEnvironmentTags();
|
|
126
|
+
this.envTagsStr = this.flattenEnvTags();
|
|
123
127
|
this.initSDK(this.opts, (data) => {
|
|
124
128
|
var _a, _b, _c, _d, _e, _f;
|
|
125
129
|
this.collectSwitch = data.collectSwitch;
|
|
@@ -136,16 +140,10 @@ var LoggerSDK = class {
|
|
|
136
140
|
this.offJs = registerJsErrorCapture(!!((_d = this.opts) == null ? void 0 : _d.debug), this.trackInner.bind(this));
|
|
137
141
|
}
|
|
138
142
|
if (enableAuto && ac.promise !== false) {
|
|
139
|
-
this.offPromise = registerPromiseErrorCapture(
|
|
140
|
-
!!((_e = this.opts) == null ? void 0 : _e.debug),
|
|
141
|
-
this.trackInner.bind(this)
|
|
142
|
-
);
|
|
143
|
+
this.offPromise = registerPromiseErrorCapture(!!((_e = this.opts) == null ? void 0 : _e.debug), this.trackInner.bind(this));
|
|
143
144
|
}
|
|
144
145
|
if (enableAuto && ac.resource !== false) {
|
|
145
|
-
this.offResource = registerResourceErrorCapture(
|
|
146
|
-
!!((_f = this.opts) == null ? void 0 : _f.debug),
|
|
147
|
-
this.trackInner.bind(this)
|
|
148
|
-
);
|
|
146
|
+
this.offResource = registerResourceErrorCapture(!!((_f = this.opts) == null ? void 0 : _f.debug), this.trackInner.bind(this));
|
|
149
147
|
}
|
|
150
148
|
});
|
|
151
149
|
}
|
|
@@ -190,6 +188,10 @@ var LoggerSDK = class {
|
|
|
190
188
|
flattenEnvTags() {
|
|
191
189
|
return Object.entries(this.envTags || {}).map(([k, v]) => `${k}=${v}`).join(",");
|
|
192
190
|
}
|
|
191
|
+
buildLogMessage(logType, message) {
|
|
192
|
+
var _a, _b, _c;
|
|
193
|
+
return `[logType]: ${logType}; [appVersion]: ${((_a = this.opts) == null ? void 0 : _a.appVersion) || "unknown"}; [userId]: ${((_b = this.opts) == null ? void 0 : _b.userId) || "unknown"}; [storeCode]: ${((_c = this.opts) == null ? void 0 : _c.storeCode) || "unknown"}; [message]: ${message}; [envTags]: ${this.envTagsStr}; [t]: ${now()}`;
|
|
194
|
+
}
|
|
193
195
|
async track({ message, error, traceId, logLevel = "INFO" }) {
|
|
194
196
|
var _a, _b, _c;
|
|
195
197
|
logDebug(!!((_a = this.opts) == null ? void 0 : _a.debug), "track", { message, error, traceId, logLevel });
|
|
@@ -236,7 +238,7 @@ var LoggerSDK = class {
|
|
|
236
238
|
url: getCurrentUrl(),
|
|
237
239
|
location: errorLocation,
|
|
238
240
|
/** 异常信息 */
|
|
239
|
-
message:
|
|
241
|
+
message: this.buildLogMessage("custom", message),
|
|
240
242
|
/** 可选:堆栈信息(长字符串) */
|
|
241
243
|
throwable: throwableStack,
|
|
242
244
|
/** 可选:用户 ID(脱敏) */
|
|
@@ -292,7 +294,7 @@ var LoggerSDK = class {
|
|
|
292
294
|
url: getCurrentUrl(),
|
|
293
295
|
location: errorLocation,
|
|
294
296
|
/** 异常信息 */
|
|
295
|
-
message:
|
|
297
|
+
message: this.buildLogMessage(error.type, error.message),
|
|
296
298
|
/** 可选:堆栈信息(长字符串) */
|
|
297
299
|
throwable: throwableStack,
|
|
298
300
|
/** 可选:用户 ID(脱敏) */
|
|
@@ -304,11 +306,24 @@ var LoggerSDK = class {
|
|
|
304
306
|
};
|
|
305
307
|
this.doTrack(logEvent);
|
|
306
308
|
}
|
|
309
|
+
getBaseRate(source) {
|
|
310
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
311
|
+
switch (source) {
|
|
312
|
+
case "js":
|
|
313
|
+
return ((_a = this.opts) == null ? void 0 : _a.sourceSampleRate.js) || ((_b = this.opts) == null ? void 0 : _b.sampleRate) || 100;
|
|
314
|
+
case "promise":
|
|
315
|
+
return ((_c = this.opts) == null ? void 0 : _c.sourceSampleRate.promise) || ((_d = this.opts) == null ? void 0 : _d.sampleRate) || 100;
|
|
316
|
+
case "resource":
|
|
317
|
+
return ((_e = this.opts) == null ? void 0 : _e.sourceSampleRate.resource) || ((_f = this.opts) == null ? void 0 : _f.sampleRate) || 100;
|
|
318
|
+
case "custom":
|
|
319
|
+
return ((_g = this.opts) == null ? void 0 : _g.sampleRate) || ((_h = this.opts) == null ? void 0 : _h.sourceSampleRate.custom) || 100;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
307
322
|
/**
|
|
308
323
|
* 判断是否应该发送事件
|
|
309
324
|
*/
|
|
310
325
|
shouldSend(level, source, url, traceId, message) {
|
|
311
|
-
var _a, _b, _c, _d, _e
|
|
326
|
+
var _a, _b, _c, _d, _e;
|
|
312
327
|
if (level === "FATAL")
|
|
313
328
|
return true;
|
|
314
329
|
if (this.collectSwitch === 0) {
|
|
@@ -319,37 +334,14 @@ var LoggerSDK = class {
|
|
|
319
334
|
logDebug(!!((_b = this.opts) == null ? void 0 : _b.debug), `Log level(${level}) not in collect log level, skip track`);
|
|
320
335
|
return false;
|
|
321
336
|
}
|
|
322
|
-
const
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
const pathMap = ((_g = this.opts) == null ? void 0 : _g.pathSampleRate) || {};
|
|
326
|
-
const prefixes = Object.keys(pathMap);
|
|
327
|
-
if (prefixes.length) {
|
|
328
|
-
let best = "";
|
|
329
|
-
for (let i = 0; i < prefixes.length; i += 1) {
|
|
330
|
-
const p2 = prefixes[i];
|
|
331
|
-
if (url.startsWith(p2) && p2.length >= best.length)
|
|
332
|
-
best = p2;
|
|
333
|
-
}
|
|
334
|
-
if (best)
|
|
335
|
-
pathRate = pathMap[best];
|
|
336
|
-
}
|
|
337
|
-
const baseRate = (() => {
|
|
338
|
-
var _a2;
|
|
339
|
-
if (typeof levelRate === "number")
|
|
340
|
-
return levelRate;
|
|
341
|
-
if (typeof sourceRate === "number")
|
|
342
|
-
return sourceRate;
|
|
343
|
-
if (typeof pathRate === "number")
|
|
344
|
-
return pathRate;
|
|
345
|
-
const sr = (_a2 = this.opts) == null ? void 0 : _a2.sampleRate;
|
|
346
|
-
return typeof sr === "number" ? sr : 1;
|
|
347
|
-
})();
|
|
348
|
-
const rate = Math.max(0, Math.min(1, baseRate));
|
|
349
|
-
if (rate >= 1)
|
|
337
|
+
const rate = Math.max(0, Math.min(100, this.getBaseRate(source)));
|
|
338
|
+
logDebug(!!((_c = this.opts) == null ? void 0 : _c.debug), "shouldSend rate: ", rate);
|
|
339
|
+
if (rate >= 100)
|
|
350
340
|
return true;
|
|
351
|
-
const seed = `${
|
|
352
|
-
|
|
341
|
+
const seed = normalizeMessage(`${url}${traceId || ""}${message || ""}`);
|
|
342
|
+
logDebug(!!((_d = this.opts) == null ? void 0 : _d.debug), "shouldSend seed: ", seed);
|
|
343
|
+
const p = hashToProb(seed) * 100;
|
|
344
|
+
logDebug(!!((_e = this.opts) == null ? void 0 : _e.debug), "shouldSend p: ", p);
|
|
353
345
|
return p < rate;
|
|
354
346
|
}
|
|
355
347
|
/**
|
package/dist/esm/index.js
CHANGED
|
@@ -11,6 +11,7 @@ export declare class LoggerSDK {
|
|
|
11
11
|
private closed;
|
|
12
12
|
/** 预收集的环境信息 tags(仅用于初始化上报) */
|
|
13
13
|
private envTags;
|
|
14
|
+
private envTagsStr;
|
|
14
15
|
/** 是否已初始化 */
|
|
15
16
|
private initialized;
|
|
16
17
|
/** 初始化时生成并锁定的会话标识 */
|
|
@@ -50,8 +51,10 @@ export declare class LoggerSDK {
|
|
|
50
51
|
/** 设置日志 stage(env) */
|
|
51
52
|
setStage(stage: Env): void;
|
|
52
53
|
private flattenEnvTags;
|
|
54
|
+
private buildLogMessage;
|
|
53
55
|
track({ message, error, traceId, logLevel }: TrackOptions): Promise<void>;
|
|
54
56
|
private trackInner;
|
|
57
|
+
private getBaseRate;
|
|
55
58
|
/**
|
|
56
59
|
* 判断是否应该发送事件
|
|
57
60
|
*/
|
package/dist/types/index.d.ts
CHANGED
|
@@ -14,13 +14,13 @@ export interface SDKOptions {
|
|
|
14
14
|
debug?: boolean;
|
|
15
15
|
/** 是否启用批量上报,默认 true */
|
|
16
16
|
enableBatch?: boolean;
|
|
17
|
-
/** 批量上报最大数量,默认
|
|
17
|
+
/** 批量上报最大数量,默认 20 */
|
|
18
18
|
batchSize?: number;
|
|
19
19
|
/** 是否启用 gzip 压缩,默认 true (仅在批量模式下有效) */
|
|
20
20
|
enableGzip?: boolean;
|
|
21
|
-
/** 最大像素图 URL 长度,默认
|
|
21
|
+
/** 最大像素图 URL 长度,默认 8192 */
|
|
22
22
|
maxPixelUrlLen?: number;
|
|
23
|
-
/** 批量上报时间间隔(毫秒),默认
|
|
23
|
+
/** 批量上报时间间隔(毫秒),默认 15000 */
|
|
24
24
|
batchInterval?: number;
|
|
25
25
|
/** 队列最大长度,默认 100 */
|
|
26
26
|
maxQueueSize?: number;
|
|
@@ -46,11 +46,11 @@ export interface SDKOptions {
|
|
|
46
46
|
token: string;
|
|
47
47
|
/** 全局采样率 0.0-1.0,默认 1.0(全量) */
|
|
48
48
|
sampleRate?: number;
|
|
49
|
-
/**
|
|
49
|
+
/** 分级采样率,优先于全局采样率。默认 { ERROR: 0.8, WARN: 0.5, INFO: 0.1, FATAL: 1 } */
|
|
50
50
|
levelSampleRate?: Partial<Record<LogEventLevel, number>>;
|
|
51
|
-
/** 事件来源采样率(js/promise/resource/custom
|
|
51
|
+
/** 事件来源采样率(js/promise/resource/custom),优先于全局采样率。默认 { js: 0.8, promise: 0.5, resource: 0.2, custom: 1 } */
|
|
52
52
|
sourceSampleRate?: Partial<Record<'js' | 'promise' | 'resource' | 'custom', number>>;
|
|
53
|
-
/** 页面路径采样率,key
|
|
53
|
+
/** 页面路径采样率,key 为路径前缀,优先于全局采样率。默认 { '/': 1 } */
|
|
54
54
|
pathSampleRate?: Record<string, number>;
|
|
55
55
|
autoCapture?: {
|
|
56
56
|
js?: boolean;
|
|
@@ -58,4 +58,5 @@ export interface SDKOptions {
|
|
|
58
58
|
resource?: boolean;
|
|
59
59
|
};
|
|
60
60
|
enableAutoCapture?: boolean;
|
|
61
|
+
appVersion?: string;
|
|
61
62
|
}
|