@pluve/logger-sdk 0.0.5 → 0.0.6

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
@@ -10,40 +10,46 @@
10
10
  ## 安装与引入
11
11
 
12
12
  - 安装:`pnpm add @pluve/logger-sdk`
13
- - ESM:`import { LoggerSDK } from '@pluve/logger-sdk'`
13
+ - ESM:`import LoggerSDK from '@pluve/logger-sdk'`
14
14
  - UMD:构建后全局为 `LoggerSDK`
15
15
 
16
- ## 快速开始
16
+ ## 快速开始(单例 + init)
17
17
 
18
18
  ```ts
19
- import { LoggerSDK } from '@pluve/logger-sdk';
19
+ import LoggerSDK from '@pluve/logger-sdk';
20
20
 
21
- const sdk = new LoggerSDK({
21
+ // 推荐使用单例
22
+ const sdk = LoggerSDK.getInstance();
23
+ sdk.init({
22
24
  endpoint: '/api/log',
23
25
  appId: 'web-shop',
24
26
  env: 'prod', // prod/stage/dev
25
27
  debug: true,
28
+ // 默认批量关闭,如需开启:
29
+ // enableBatch: true,
26
30
  });
27
31
 
32
+ // init 会自动上报一次 'session_start',携带环境信息(UA/OS/屏幕等)
33
+ // 后续事件默认不再合并环境标签(节省流量)
34
+
28
35
  // 记录错误日志
29
- sdk.track('error', 'TypeError: Cannot read property', {
36
+ await sdk.track('error', 'TypeError: Cannot read property', undefined, {
30
37
  level: 'error',
31
38
  stack: 'Error stack trace...',
32
39
  userId: '123',
33
40
  tags: {
34
41
  component: 'checkout',
35
- browser: 'Chrome 120',
36
42
  },
37
43
  });
38
44
 
39
45
  // 记录带 traceId 的错误(用于关联多个日志)
40
- sdk.track('error', 'Request failed', 'trace-abc-123', {
46
+ await sdk.track('error', 'Request failed', 'trace-abc-123', {
41
47
  level: 'error',
42
48
  tags: { api: '/api/user' },
43
49
  });
44
50
 
45
51
  // 记录自定义事件
46
- sdk.track('custom', 'User clicked button', {
52
+ await sdk.track('custom', 'User clicked button', undefined, {
47
53
  level: 'info',
48
54
  tags: { action: 'click', button: 'submit' },
49
55
  });
@@ -57,7 +63,7 @@ sdk.track('custom', 'User clicked button', {
57
63
  {
58
64
  "logId": "550e8400-e29b-41d4-a716-446655440000", // 日志 ID(UUID v4)
59
65
  "traceId": "trace-123456", // 可选:追踪 ID(用于关联多个日志)
60
- "eventType": "error", // 固定:error/crash/pageview/custom
66
+ "eventType": "error", // 固定:error/crash/pageview/custom/session_start
61
67
  "ts": 1690000000000, // 毫秒时间戳
62
68
  "appId": "web-shop", // 应用标识
63
69
  "env": "prod", // prod/stage/dev
@@ -76,23 +82,35 @@ sdk.track('custom', 'User clicked button', {
76
82
 
77
83
  ## API
78
84
 
79
- ### `new LoggerSDK(options: SDKOptions)`
85
+ ### 单例与初始化
86
+
87
+ #### `LoggerSDK.getInstance(): LoggerSDK`
80
88
 
81
- 创建 SDK 实例。
89
+ 获取全局唯一实例。
90
+
91
+ #### `init(options: SDKOptions)`
92
+
93
+ 初始化 SDK、生成并锁定 sessionId、收集环境信息并上报一次 `session_start`。
94
+ 注意:锁定的 sessionId 将在销毁前用于所有日志上报。
95
+
96
+ #### `new LoggerSDK(options?: SDKOptions)`
97
+
98
+ 创建 SDK 实例;如果传入 `options`,会在构造时自动调用 `init(options)`。
82
99
 
83
100
  **配置项:**
84
101
 
85
102
  ```typescript
86
103
  interface SDKOptions {
87
104
  endpoint: string; // 上报端点 URL(必选)
88
- appId?: string; // 应用 ID,默认 'unknown'
105
+ appId: string; // 应用 ID(必选)
89
106
  env?: Env; // 环境标识:prod/stage/dev,默认 'dev'
90
107
  debug?: boolean; // 是否开启调试模式
91
108
  pixelParam?: string; // 像素上报参数名,默认 'data'
92
109
  maxPixelUrlLen?: number; // 像素上报 URL 最大长度,默认 1900
110
+ enableGzip?: boolean; // 是否启用 gzip(传输适配器内部处理)
93
111
 
94
112
  // 批量上报配置
95
- enableBatch?: boolean; // 是否启用批量上报,默认 true
113
+ enableBatch?: boolean; // 是否启用批量上报,默认 false(需显式开启)
96
114
  batchSize?: number; // 批量上报最大数量,默认 10
97
115
  batchInterval?: number; // 批量上报时间间隔(毫秒),默认 5000
98
116
  maxQueueSize?: number; // 队列最大长度,默认 100
@@ -175,10 +193,30 @@ sdk.track('error', 'Request failed', traceId, {
175
193
 
176
194
  设置用户 ID(用于后续日志关联)。
177
195
 
196
+ ### `trace(error: Error | string, options?)`
197
+
198
+ 错误专用方法,自动设置 `eventType: 'error'` 与 `level: 'error'`,不合并环境标签。
199
+
200
+ ```ts
201
+ try {
202
+ throw new Error('Network Error');
203
+ } catch (e) {
204
+ await sdk.trace(e as Error, { userId: 'u123', traceId: 'req-1' });
205
+ }
206
+ ```
207
+
178
208
  ### `destroy()`
179
209
 
180
210
  销毁 SDK 实例,停止所有监听。
181
211
 
212
+ 销毁后单例会被清理;如需新会话,重新调用 `init()` 即可生成新的 sessionId。
213
+
214
+ ### 会话管理
215
+
216
+ - 在 `init()` 时生成并锁定 `sessionId`
217
+ - 在调用 `destroy()` 前,所有日志都会使用该 `sessionId`
218
+ - 重新 `init()` 将开始新的会话(新的 `sessionId`)
219
+
182
220
  ## 自动采集
183
221
 
184
222
  SDK 会自动监听以下页面事件并上报:
@@ -206,6 +244,12 @@ SDK 使用两种上报方式,按优先级自动选择:
206
244
 
207
245
  如果 Beacon 失败,会自动降级到 Image 方式。
208
246
 
247
+ ### 微信小程序适配器
248
+
249
+ - 环境:微信小程序
250
+ - 支持:请求成功/失败/非 2xx 状态码与超时控制
251
+ - Header Content-Type 会根据是否启用 gzip 自动调整
252
+
209
253
  ## 使用场景
210
254
 
211
255
  ### 错误监控
@@ -441,10 +485,17 @@ await sdk.flush();
441
485
  ### 禁用批量上报(实时上报)
442
486
 
443
487
  ```typescript
444
- const sdk = new LoggerSDK({
488
+ const sdk = LoggerSDK.getInstance();
489
+ sdk.init({
445
490
  endpoint: 'https://your-api.com/logs',
446
491
  appId: 'my-app',
447
- enableBatch: false, // 禁用批量上报,每条日志立即发送
448
- enableRetry: true, // 仍然启用重试
492
+ enableBatch: false, // 默认即为 false;每条日志立即发送
493
+ enableRetry: true,
449
494
  });
450
495
  ```
496
+
497
+ ## 重要说明
498
+
499
+ - `init()` 会自动上报 `session_start`,携带环境信息标签(仅此一次)
500
+ - 普通事件默认不合并环境标签,建议仅在必要时通过 `tags` 传入额外信息
501
+ - 推荐使用 `LoggerSDK.getInstance()` 获取单例,避免多实例重复初始化
@@ -1,10 +1,15 @@
1
1
  import { SDKOptions, LogEventType, LogEventLevel } from './types';
2
2
  export declare class LoggerSDK {
3
- private opts;
3
+ private static instance;
4
+ private opts?;
4
5
  private seq;
5
6
  private closed;
6
- /** 预收集的环境信息 tags */
7
+ /** 预收集的环境信息 tags(仅用于初始化上报) */
7
8
  private envTags;
9
+ /** 是否已初始化 */
10
+ private initialized;
11
+ /** 初始化时生成并锁定的会话标识 */
12
+ private sessionId;
8
13
  /** 队列管理器 */
9
14
  private queueManager?;
10
15
  /** 重试管理器 */
@@ -13,7 +18,12 @@ export declare class LoggerSDK {
13
18
  private batchTimer?;
14
19
  /** 是否正在上报 */
15
20
  private isSending;
16
- constructor(options: SDKOptions);
21
+ constructor(options?: SDKOptions);
22
+ static getInstance(): LoggerSDK;
23
+ /**
24
+ * 初始化:配置参数、收集环境信息与 UA,并生成 sessionId,完成初始上报
25
+ */
26
+ init(options: SDKOptions): void;
17
27
  private logDebug;
18
28
  /**
19
29
  * 收集环境信息并生成 tags(仅在初始化时执行一次)
package/dist/loggerSDK.js CHANGED
@@ -1,7 +1,5 @@
1
1
  function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
2
2
  function _regeneratorRuntime() { "use strict"; /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */ _regeneratorRuntime = function _regeneratorRuntime() { return e; }; var t, e = {}, r = Object.prototype, n = r.hasOwnProperty, o = Object.defineProperty || function (t, e, r) { t[e] = r.value; }, i = "function" == typeof Symbol ? Symbol : {}, a = i.iterator || "@@iterator", c = i.asyncIterator || "@@asyncIterator", u = i.toStringTag || "@@toStringTag"; function define(t, e, r) { return Object.defineProperty(t, e, { value: r, enumerable: !0, configurable: !0, writable: !0 }), t[e]; } try { define({}, ""); } catch (t) { define = function define(t, e, r) { return t[e] = r; }; } function wrap(t, e, r, n) { var i = e && e.prototype instanceof Generator ? e : Generator, a = Object.create(i.prototype), c = new Context(n || []); return o(a, "_invoke", { value: makeInvokeMethod(t, r, c) }), a; } function tryCatch(t, e, r) { try { return { type: "normal", arg: t.call(e, r) }; } catch (t) { return { type: "throw", arg: t }; } } e.wrap = wrap; var h = "suspendedStart", l = "suspendedYield", f = "executing", s = "completed", y = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} var p = {}; define(p, a, function () { return this; }); var d = Object.getPrototypeOf, v = d && d(d(values([]))); v && v !== r && n.call(v, a) && (p = v); var g = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(p); function defineIteratorMethods(t) { ["next", "throw", "return"].forEach(function (e) { define(t, e, function (t) { return this._invoke(e, t); }); }); } function AsyncIterator(t, e) { function invoke(r, o, i, a) { var c = tryCatch(t[r], t, o); if ("throw" !== c.type) { var u = c.arg, h = u.value; return h && "object" == _typeof(h) && n.call(h, "__await") ? e.resolve(h.__await).then(function (t) { invoke("next", t, i, a); }, function (t) { invoke("throw", t, i, a); }) : e.resolve(h).then(function (t) { u.value = t, i(u); }, function (t) { return invoke("throw", t, i, a); }); } a(c.arg); } var r; o(this, "_invoke", { value: function value(t, n) { function callInvokeWithMethodAndArg() { return new e(function (e, r) { invoke(t, n, e, r); }); } return r = r ? r.then(callInvokeWithMethodAndArg, callInvokeWithMethodAndArg) : callInvokeWithMethodAndArg(); } }); } function makeInvokeMethod(e, r, n) { var o = h; return function (i, a) { if (o === f) throw new Error("Generator is already running"); if (o === s) { if ("throw" === i) throw a; return { value: t, done: !0 }; } for (n.method = i, n.arg = a;;) { var c = n.delegate; if (c) { var u = maybeInvokeDelegate(c, n); if (u) { if (u === y) continue; return u; } } if ("next" === n.method) n.sent = n._sent = n.arg;else if ("throw" === n.method) { if (o === h) throw o = s, n.arg; n.dispatchException(n.arg); } else "return" === n.method && n.abrupt("return", n.arg); o = f; var p = tryCatch(e, r, n); if ("normal" === p.type) { if (o = n.done ? s : l, p.arg === y) continue; return { value: p.arg, done: n.done }; } "throw" === p.type && (o = s, n.method = "throw", n.arg = p.arg); } }; } function maybeInvokeDelegate(e, r) { var n = r.method, o = e.iterator[n]; if (o === t) return r.delegate = null, "throw" === n && e.iterator.return && (r.method = "return", r.arg = t, maybeInvokeDelegate(e, r), "throw" === r.method) || "return" !== n && (r.method = "throw", r.arg = new TypeError("The iterator does not provide a '" + n + "' method")), y; var i = tryCatch(o, e.iterator, r.arg); if ("throw" === i.type) return r.method = "throw", r.arg = i.arg, r.delegate = null, y; var a = i.arg; return a ? a.done ? (r[e.resultName] = a.value, r.next = e.nextLoc, "return" !== r.method && (r.method = "next", r.arg = t), r.delegate = null, y) : a : (r.method = "throw", r.arg = new TypeError("iterator result is not an object"), r.delegate = null, y); } function pushTryEntry(t) { var e = { tryLoc: t[0] }; 1 in t && (e.catchLoc = t[1]), 2 in t && (e.finallyLoc = t[2], e.afterLoc = t[3]), this.tryEntries.push(e); } function resetTryEntry(t) { var e = t.completion || {}; e.type = "normal", delete e.arg, t.completion = e; } function Context(t) { this.tryEntries = [{ tryLoc: "root" }], t.forEach(pushTryEntry, this), this.reset(!0); } function values(e) { if (e || "" === e) { var r = e[a]; if (r) return r.call(e); if ("function" == typeof e.next) return e; if (!isNaN(e.length)) { var o = -1, i = function next() { for (; ++o < e.length;) if (n.call(e, o)) return next.value = e[o], next.done = !1, next; return next.value = t, next.done = !0, next; }; return i.next = i; } } throw new TypeError(_typeof(e) + " is not iterable"); } return GeneratorFunction.prototype = GeneratorFunctionPrototype, o(g, "constructor", { value: GeneratorFunctionPrototype, configurable: !0 }), o(GeneratorFunctionPrototype, "constructor", { value: GeneratorFunction, configurable: !0 }), GeneratorFunction.displayName = define(GeneratorFunctionPrototype, u, "GeneratorFunction"), e.isGeneratorFunction = function (t) { var e = "function" == typeof t && t.constructor; return !!e && (e === GeneratorFunction || "GeneratorFunction" === (e.displayName || e.name)); }, e.mark = function (t) { return Object.setPrototypeOf ? Object.setPrototypeOf(t, GeneratorFunctionPrototype) : (t.__proto__ = GeneratorFunctionPrototype, define(t, u, "GeneratorFunction")), t.prototype = Object.create(g), t; }, e.awrap = function (t) { return { __await: t }; }, defineIteratorMethods(AsyncIterator.prototype), define(AsyncIterator.prototype, c, function () { return this; }), e.AsyncIterator = AsyncIterator, e.async = function (t, r, n, o, i) { void 0 === i && (i = Promise); var a = new AsyncIterator(wrap(t, r, n, o), i); return e.isGeneratorFunction(r) ? a : a.next().then(function (t) { return t.done ? t.value : a.next(); }); }, defineIteratorMethods(g), define(g, u, "Generator"), define(g, a, function () { return this; }), define(g, "toString", function () { return "[object Generator]"; }), e.keys = function (t) { var e = Object(t), r = []; for (var n in e) r.push(n); return r.reverse(), function next() { for (; r.length;) { var t = r.pop(); if (t in e) return next.value = t, next.done = !1, next; } return next.done = !0, next; }; }, e.values = values, Context.prototype = { constructor: Context, reset: function reset(e) { if (this.prev = 0, this.next = 0, this.sent = this._sent = t, this.done = !1, this.delegate = null, this.method = "next", this.arg = t, this.tryEntries.forEach(resetTryEntry), !e) for (var r in this) "t" === r.charAt(0) && n.call(this, r) && !isNaN(+r.slice(1)) && (this[r] = t); }, stop: function stop() { this.done = !0; var t = this.tryEntries[0].completion; if ("throw" === t.type) throw t.arg; return this.rval; }, dispatchException: function dispatchException(e) { if (this.done) throw e; var r = this; function handle(n, o) { return a.type = "throw", a.arg = e, r.next = n, o && (r.method = "next", r.arg = t), !!o; } for (var o = this.tryEntries.length - 1; o >= 0; --o) { var i = this.tryEntries[o], a = i.completion; if ("root" === i.tryLoc) return handle("end"); if (i.tryLoc <= this.prev) { var c = n.call(i, "catchLoc"), u = n.call(i, "finallyLoc"); if (c && u) { if (this.prev < i.catchLoc) return handle(i.catchLoc, !0); if (this.prev < i.finallyLoc) return handle(i.finallyLoc); } else if (c) { if (this.prev < i.catchLoc) return handle(i.catchLoc, !0); } else { if (!u) throw new Error("try statement without catch or finally"); if (this.prev < i.finallyLoc) return handle(i.finallyLoc); } } } }, abrupt: function abrupt(t, e) { for (var r = this.tryEntries.length - 1; r >= 0; --r) { var o = this.tryEntries[r]; if (o.tryLoc <= this.prev && n.call(o, "finallyLoc") && this.prev < o.finallyLoc) { var i = o; break; } } i && ("break" === t || "continue" === t) && i.tryLoc <= e && e <= i.finallyLoc && (i = null); var a = i ? i.completion : {}; return a.type = t, a.arg = e, i ? (this.method = "next", this.next = i.finallyLoc, y) : this.complete(a); }, complete: function complete(t, e) { if ("throw" === t.type) throw t.arg; return "break" === t.type || "continue" === t.type ? this.next = t.arg : "return" === t.type ? (this.rval = this.arg = t.arg, this.method = "return", this.next = "end") : "normal" === t.type && e && (this.next = e), y; }, finish: function finish(t) { for (var e = this.tryEntries.length - 1; e >= 0; --e) { var r = this.tryEntries[e]; if (r.finallyLoc === t) return this.complete(r.completion, r.afterLoc), resetTryEntry(r), y; } }, catch: function _catch(t) { for (var e = this.tryEntries.length - 1; e >= 0; --e) { var r = this.tryEntries[e]; if (r.tryLoc === t) { var n = r.completion; if ("throw" === n.type) { var o = n.arg; resetTryEntry(r); } return o; } } throw new Error("illegal catch attempt"); }, delegateYield: function delegateYield(e, r, n) { return this.delegate = { iterator: values(e), resultName: r, nextLoc: n }, "next" === this.method && (this.arg = t), y; } }, e; }
3
- function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
4
- function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
5
3
  function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
6
4
  function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
7
5
  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
@@ -20,8 +18,12 @@ export var LoggerSDK = /*#__PURE__*/function () {
20
18
  _defineProperty(this, "opts", void 0);
21
19
  _defineProperty(this, "seq", 0);
22
20
  _defineProperty(this, "closed", false);
23
- /** 预收集的环境信息 tags */
24
- _defineProperty(this, "envTags", void 0);
21
+ /** 预收集的环境信息 tags(仅用于初始化上报) */
22
+ _defineProperty(this, "envTags", {});
23
+ /** 是否已初始化 */
24
+ _defineProperty(this, "initialized", false);
25
+ /** 初始化时生成并锁定的会话标识 */
26
+ _defineProperty(this, "sessionId", void 0);
25
27
  /** 队列管理器 */
26
28
  _defineProperty(this, "queueManager", void 0);
27
29
  /** 重试管理器 */
@@ -30,71 +32,92 @@ export var LoggerSDK = /*#__PURE__*/function () {
30
32
  _defineProperty(this, "batchTimer", void 0);
31
33
  /** 是否正在上报 */
32
34
  _defineProperty(this, "isSending", false);
33
- this.opts = {
34
- endpoint: options.endpoint,
35
- appId: options.appId || 'unknown',
36
- env: options.env || 'dev',
37
- debug: !!options.debug,
38
- pixelParam: options.pixelParam || 'data',
39
- maxPixelUrlLen: options.maxPixelUrlLen || 1900,
40
- enableGzip: !!options.enableGzip,
41
- // 批量上报配置
42
- enableBatch: options.enableBatch !== false,
43
- // 默认启用
44
- batchSize: options.batchSize || 10,
45
- batchInterval: options.batchInterval || 5000,
46
- maxQueueSize: options.maxQueueSize || 100,
47
- // 持久化存储配置
48
- enableStorage: options.enableStorage !== false,
49
- // 默认启用
50
- storagePrefix: options.storagePrefix || 'logger_sdk',
51
- // 重试配置
52
- enableRetry: options.enableRetry !== false,
53
- // 默认启用
54
- maxRetries: options.maxRetries || 3,
55
- retryDelay: options.retryDelay || 1000,
56
- retryBackoff: options.retryBackoff !== false // 默认启用
57
- };
35
+ if (options) {
36
+ this.init(options);
37
+ }
38
+ }
39
+ _createClass(LoggerSDK, [{
40
+ key: "init",
41
+ value:
42
+ /**
43
+ * 初始化:配置参数、收集环境信息与 UA,并生成 sessionId,完成初始上报
44
+ */
45
+ function init(options) {
46
+ var _this = this;
47
+ this.opts = {
48
+ endpoint: options.endpoint,
49
+ appId: options.appId || 'unknown',
50
+ env: options.env || 'dev',
51
+ debug: !!options.debug,
52
+ pixelParam: options.pixelParam || 'data',
53
+ maxPixelUrlLen: options.maxPixelUrlLen || 1900,
54
+ enableGzip: !!options.enableGzip,
55
+ // 批量上报配置(默认关闭,需显式开启)
56
+ enableBatch: options.enableBatch === true,
57
+ batchSize: options.batchSize || 10,
58
+ batchInterval: options.batchInterval || 5000,
59
+ maxQueueSize: options.maxQueueSize || 100,
60
+ // 持久化存储配置
61
+ enableStorage: options.enableStorage !== false,
62
+ // 默认启用
63
+ storagePrefix: options.storagePrefix || 'logger_sdk',
64
+ // 重试配置
65
+ enableRetry: options.enableRetry !== false,
66
+ // 默认启用
67
+ maxRetries: options.maxRetries || 3,
68
+ retryDelay: options.retryDelay || 1000,
69
+ retryBackoff: options.retryBackoff !== false // 默认启用
70
+ };
58
71
 
59
- // 初始化时收集环境信息
60
- this.envTags = this.collectEnvironmentTags();
72
+ // 初始化队列管理器
73
+ if (this.opts.enableBatch) {
74
+ this.queueManager = new QueueManager({
75
+ maxSize: this.opts.maxQueueSize,
76
+ enableStorage: this.opts.enableStorage,
77
+ storagePrefix: this.opts.storagePrefix,
78
+ debug: this.opts.debug
79
+ });
80
+ }
61
81
 
62
- // 初始化队列管理器
63
- if (this.opts.enableBatch) {
64
- this.queueManager = new QueueManager({
65
- maxSize: this.opts.maxQueueSize,
66
- enableStorage: this.opts.enableStorage,
67
- storagePrefix: this.opts.storagePrefix,
68
- debug: this.opts.debug
69
- });
70
- }
82
+ // 初始化重试管理器
83
+ if (this.opts.enableRetry) {
84
+ this.retryManager = new RetryManager({
85
+ maxRetries: this.opts.maxRetries,
86
+ baseDelay: this.opts.retryDelay,
87
+ useBackoff: this.opts.retryBackoff,
88
+ debug: this.opts.debug
89
+ });
90
+ }
91
+
92
+ // 监听页面卸载事件
93
+ this.attachUnloadHandlers();
94
+
95
+ // 生成并缓存 sessionId(便于 UV 统计),并锁定到实例生命周期
96
+ this.sessionId = getSessionId();
71
97
 
72
- // 初始化重试管理器
73
- if (this.opts.enableRetry) {
74
- this.retryManager = new RetryManager({
75
- maxRetries: this.opts.maxRetries,
76
- baseDelay: this.opts.retryDelay,
77
- useBackoff: this.opts.retryBackoff,
78
- debug: this.opts.debug
98
+ // 收集 UA / 环境信息并完成初始上报(便于后端统计 UA 和 UV)
99
+ this.envTags = this.collectEnvironmentTags();
100
+ this.track('session_start', 'Session started', undefined, {
101
+ level: 'info',
102
+ tags: this.envTags
103
+ }).catch(function (err) {
104
+ _this.logDebug('Failed to report session_start', err);
79
105
  });
80
- }
81
106
 
82
- // 启动批量上报定时器
83
- if (this.opts.enableBatch) {
84
- this.startBatchTimer();
107
+ // 启动批量上报定时器
108
+ if (this.opts.enableBatch) {
109
+ this.startBatchTimer();
110
+ }
111
+ this.initialized = true;
85
112
  }
86
-
87
- // 监听页面卸载事件
88
- this.attachUnloadHandlers();
89
- }
90
- _createClass(LoggerSDK, [{
113
+ }, {
91
114
  key: "logDebug",
92
115
  value: function logDebug() {
93
- var _console;
116
+ var _this$opts, _console;
94
117
  for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
95
118
  args[_key] = arguments[_key];
96
119
  }
97
- if (this.opts.debug) (_console = console).debug.apply(_console, ['[LoggerSDK]'].concat(args));
120
+ if ((_this$opts = this.opts) !== null && _this$opts !== void 0 && _this$opts.debug) (_console = console).debug.apply(_console, ['[LoggerSDK]'].concat(args));
98
121
  }
99
122
 
100
123
  /**
@@ -151,6 +174,13 @@ export var LoggerSDK = /*#__PURE__*/function () {
151
174
  }
152
175
  return _context.abrupt("return");
153
176
  case 2:
177
+ if (this.initialized) {
178
+ _context.next = 5;
179
+ break;
180
+ }
181
+ this.logDebug('SDK not initialized, skip track');
182
+ return _context.abrupt("return");
183
+ case 5:
154
184
  this.seq += 1;
155
185
 
156
186
  // 构建标准化日志格式
@@ -167,46 +197,46 @@ export var LoggerSDK = /*#__PURE__*/function () {
167
197
  stack: options === null || options === void 0 ? void 0 : options.stack,
168
198
  url: getCurrentUrl(),
169
199
  userId: options === null || options === void 0 ? void 0 : options.userId,
170
- sessionId: getSessionId(),
171
- // 合并预收集的环境 tags 和用户自定义 tags
172
- tags: _objectSpread(_objectSpread({}, this.envTags), (options === null || options === void 0 ? void 0 : options.tags) || {})
200
+ sessionId: this.sessionId,
201
+ // 单次事件只携带自定义 tags,不再包含环境数据,节省流量
202
+ tags: (options === null || options === void 0 ? void 0 : options.tags) || {}
173
203
  };
174
204
  this.logDebug('track', logEvent);
175
205
 
176
206
  // 如果启用批量上报,添加到队列
177
207
  if (!(this.opts.enableBatch && this.queueManager)) {
178
- _context.next = 13;
208
+ _context.next = 16;
179
209
  break;
180
210
  }
181
211
  this.queueManager.enqueue(logEvent);
182
212
  // 如果队列已满,立即上报
183
213
  if (!(this.queueManager.size() >= this.opts.batchSize)) {
184
- _context.next = 11;
214
+ _context.next = 14;
185
215
  break;
186
216
  }
187
217
  this.logDebug('Queue size reached batch size, flushing immediately');
188
- _context.next = 11;
218
+ _context.next = 14;
189
219
  return this.flush();
190
- case 11:
191
- _context.next = 21;
220
+ case 14:
221
+ _context.next = 24;
192
222
  break;
193
- case 13:
194
- _context.prev = 13;
195
- _context.next = 16;
196
- return this.sendEvent(logEvent);
197
223
  case 16:
198
- _context.next = 21;
224
+ _context.prev = 16;
225
+ _context.next = 19;
226
+ return this.sendEvent(logEvent);
227
+ case 19:
228
+ _context.next = 24;
199
229
  break;
200
- case 18:
201
- _context.prev = 18;
202
- _context.t0 = _context["catch"](13);
230
+ case 21:
231
+ _context.prev = 21;
232
+ _context.t0 = _context["catch"](16);
203
233
  this.logDebug('track failed', _context.t0);
204
234
  // 静默失败,不影响主流程
205
- case 21:
235
+ case 24:
206
236
  case "end":
207
237
  return _context.stop();
208
238
  }
209
- }, _callee, this, [[13, 18]]);
239
+ }, _callee, this, [[16, 21]]);
210
240
  }));
211
241
  function track(_x, _x2, _x3, _x4) {
212
242
  return _track.apply(this, arguments);
@@ -317,7 +347,10 @@ export var LoggerSDK = /*#__PURE__*/function () {
317
347
  if (this.retryManager) {
318
348
  this.retryManager.clear();
319
349
  }
320
- case 6:
350
+ this.initialized = false;
351
+ this.sessionId = undefined;
352
+ LoggerSDK.instance = undefined;
353
+ case 9:
321
354
  case "end":
322
355
  return _context3.stop();
323
356
  }
@@ -336,7 +369,7 @@ export var LoggerSDK = /*#__PURE__*/function () {
336
369
  key: "sendEvent",
337
370
  value: function () {
338
371
  var _sendEvent = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee5(event) {
339
- var _this = this;
372
+ var _this2 = this;
340
373
  var sendFn;
341
374
  return _regeneratorRuntime().wrap(function _callee5$(_context5) {
342
375
  while (1) switch (_context5.prev = _context5.next) {
@@ -347,7 +380,7 @@ export var LoggerSDK = /*#__PURE__*/function () {
347
380
  while (1) switch (_context4.prev = _context4.next) {
348
381
  case 0:
349
382
  _context4.next = 2;
350
- return defaultTransport(event, _this.opts);
383
+ return defaultTransport(event, _this2.opts);
351
384
  case 2:
352
385
  case "end":
353
386
  return _context4.stop();
@@ -388,7 +421,7 @@ export var LoggerSDK = /*#__PURE__*/function () {
388
421
  key: "sendBatch",
389
422
  value: (function () {
390
423
  var _sendBatch = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee7(events) {
391
- var _this2 = this;
424
+ var _this3 = this;
392
425
  var batchId, sendFn;
393
426
  return _regeneratorRuntime().wrap(function _callee7$(_context7) {
394
427
  while (1) switch (_context7.prev = _context7.next) {
@@ -407,7 +440,7 @@ export var LoggerSDK = /*#__PURE__*/function () {
407
440
  while (1) switch (_context6.prev = _context6.next) {
408
441
  case 0:
409
442
  _context6.next = 2;
410
- return defaultTransport(events, _this2.opts);
443
+ return defaultTransport(events, _this3.opts);
411
444
  case 2:
412
445
  case "end":
413
446
  return _context6.stop();
@@ -448,13 +481,13 @@ export var LoggerSDK = /*#__PURE__*/function () {
448
481
  }, {
449
482
  key: "startBatchTimer",
450
483
  value: function startBatchTimer() {
451
- var _this3 = this;
484
+ var _this4 = this;
452
485
  if (this.batchTimer) return;
453
486
  this.batchTimer = setInterval(function () {
454
- if (!_this3.closed && _this3.queueManager && _this3.queueManager.size() > 0) {
455
- _this3.logDebug('Batch timer triggered, flushing queue');
456
- _this3.flush().catch(function (error) {
457
- _this3.logDebug('Batch timer flush failed', error);
487
+ if (!_this4.closed && _this4.queueManager && _this4.queueManager.size() > 0) {
488
+ _this4.logDebug('Batch timer triggered, flushing queue');
489
+ _this4.flush().catch(function (error) {
490
+ _this4.logDebug('Batch timer flush failed', error);
458
491
  });
459
492
  }
460
493
  }, this.opts.batchInterval);
@@ -469,7 +502,7 @@ export var LoggerSDK = /*#__PURE__*/function () {
469
502
  }, {
470
503
  key: "attachUnloadHandlers",
471
504
  value: function attachUnloadHandlers() {
472
- var _this4 = this;
505
+ var _this5 = this;
473
506
  // 微信小程序环境
474
507
  if (isWeChatMiniProgram()) {
475
508
  // 微信小程序使用 App 生命周期
@@ -487,7 +520,7 @@ export var LoggerSDK = /*#__PURE__*/function () {
487
520
  try {
488
521
  if (document.visibilityState === 'hidden') {
489
522
  // 页面隐藏时刷新队列
490
- _this4.flush().catch(function () {
523
+ _this5.flush().catch(function () {
491
524
  // 忽略错误
492
525
  });
493
526
  console.log('Page hidden, flushed queue');
@@ -501,7 +534,7 @@ export var LoggerSDK = /*#__PURE__*/function () {
501
534
  win.addEventListener && win.addEventListener('pagehide', function () {
502
535
  try {
503
536
  // 页面隐藏时刷新队列(同步)
504
- _this4.flush().catch(function () {
537
+ _this5.flush().catch(function () {
505
538
  // 忽略错误
506
539
  });
507
540
  console.log('Page hide, flushed queue');
@@ -514,7 +547,7 @@ export var LoggerSDK = /*#__PURE__*/function () {
514
547
  win.addEventListener && win.addEventListener('beforeunload', function () {
515
548
  try {
516
549
  // 页面卸载前刷新队列(同步)
517
- _this4.flush().catch(function () {
550
+ _this5.flush().catch(function () {
518
551
  // 忽略错误
519
552
  });
520
553
  console.log('Page unload, flushed queue');
@@ -523,9 +556,18 @@ export var LoggerSDK = /*#__PURE__*/function () {
523
556
  }
524
557
  });
525
558
  }
559
+ }], [{
560
+ key: "getInstance",
561
+ value: function getInstance() {
562
+ if (!LoggerSDK.instance) {
563
+ LoggerSDK.instance = new LoggerSDK();
564
+ }
565
+ return LoggerSDK.instance;
566
+ }
526
567
  }]);
527
568
  return LoggerSDK;
528
569
  }();
529
570
 
530
571
  // 兼容导出(UMD/ESM)
572
+ _defineProperty(LoggerSDK, "instance", void 0);
531
573
  export default LoggerSDK;
package/dist/types.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  /** 环境类型 */
2
2
  export type Env = 'prod' | 'stage' | 'dev';
3
3
  /** 事件类型 */
4
- export type LogEventType = 'error' | 'crash' | 'pageview' | 'custom';
4
+ export type LogEventType = 'error' | 'crash' | 'pageview' | 'custom' | 'session_start';
5
5
  /** 日志级别 */
6
6
  export type LogEventLevel = 'info' | 'warn' | 'error' | 'fatal';
7
7
  /** SDK 配置选项 */
@@ -45,7 +45,7 @@ export interface SDKOptions {
45
45
  export interface LogEvent {
46
46
  logId: string;
47
47
  traceId?: string;
48
- /** 事件类型:error/crash/pageview/custom */
48
+ /** 事件类型:error/crash/pageview/custom/session_start */
49
49
  eventType: LogEventType;
50
50
  /** 毫秒时间戳 */
51
51
  ts: number;