@be-link/cls-logger 1.0.1-beta.4 → 1.0.1-beta.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/dist/index.umd.js CHANGED
@@ -1,8 +1,8 @@
1
1
  (function (global, factory) {
2
- typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('tencentcloud-cls-sdk-js-web')) :
3
- typeof define === 'function' && define.amd ? define(['exports', 'tencentcloud-cls-sdk-js-web'], factory) :
4
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.BeLinkClsLogger = {}, global.tencentcloudClsSdkJsWeb));
5
- })(this, (function (exports, tencentcloudClsSdkJsWeb) { 'use strict';
2
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
3
+ typeof define === 'function' && define.amd ? define(['exports'], factory) :
4
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.BeLinkClsLogger = {}));
5
+ })(this, (function (exports) { 'use strict';
6
6
 
7
7
  function isPlainObject(value) {
8
8
  return Object.prototype.toString.call(value) === '[object Object]';
@@ -1909,9 +1909,20 @@
1909
1909
  g.__beLinkClsLoggerSendingCount__ = cur > 0 ? cur - 1 : 0;
1910
1910
  };
1911
1911
  }
1912
- class ClsLogger {
1912
+ /**
1913
+ * CLS Logger 核心基类
1914
+ * - 负责所有上报、队列、监控逻辑
1915
+ * - 不包含具体的 SDK 加载实现(由子类负责)
1916
+ * - 这样可以把 web/mini 的 SDK 依赖彻底解耦到子入口
1917
+ */
1918
+ class ClsLoggerCore {
1913
1919
  constructor() {
1920
+ this.sdk = null;
1921
+ this.sdkPromise = null;
1922
+ this.sdkOverride = null;
1923
+ this.sdkLoaderOverride = null;
1914
1924
  this.client = null;
1925
+ this.clientPromise = null;
1915
1926
  this.topicId = null;
1916
1927
  this.endpoint = 'ap-shanghai.cls.tencentcs.com';
1917
1928
  this.retryTimes = 10;
@@ -1942,6 +1953,15 @@
1942
1953
  this.behaviorMonitorStarted = false;
1943
1954
  this.behaviorMonitorCleanup = null;
1944
1955
  }
1956
+ /**
1957
+ * 子类可按需重写(默认检测 wx)
1958
+ */
1959
+ detectEnvType() {
1960
+ const wxAny = globalThis.wx;
1961
+ if (wxAny && typeof wxAny.getSystemInfoSync === 'function')
1962
+ return 'miniprogram';
1963
+ return 'browser';
1964
+ }
1945
1965
  init(options) {
1946
1966
  this.initTs = Date.now();
1947
1967
  const topicId = options?.tencentCloud?.topicID ?? options?.topic_id ?? options?.topicID ?? this.topicId ?? null;
@@ -1953,6 +1973,19 @@
1953
1973
  console.warn('ClsLogger.init 没有传 topicID/topic_id');
1954
1974
  return;
1955
1975
  }
1976
+ const nextEnvType = options.envType ?? this.detectEnvType();
1977
+ // envType/endpoint/retryTimes 变化时:重置 client(以及可能的 sdk)
1978
+ const envChanged = nextEnvType !== this.envType;
1979
+ const endpointChanged = endpoint !== this.endpoint;
1980
+ const retryChanged = retryTimes !== this.retryTimes;
1981
+ if (envChanged || endpointChanged || retryChanged) {
1982
+ this.client = null;
1983
+ this.clientPromise = null;
1984
+ }
1985
+ if (envChanged) {
1986
+ this.sdk = null;
1987
+ this.sdkPromise = null;
1988
+ }
1956
1989
  this.topicId = topicId;
1957
1990
  this.endpoint = endpoint;
1958
1991
  this.retryTimes = retryTimes;
@@ -1963,7 +1996,10 @@
1963
1996
  this.projectName = options.projectName ?? this.projectName;
1964
1997
  this.appId = options.appId ?? this.appId;
1965
1998
  this.appVersion = options.appVersion ?? this.appVersion;
1966
- this.envType = options.envType ?? this.detectEnvType();
1999
+ this.envType = nextEnvType;
2000
+ // 可选:外部注入 SDK(优先级:sdkLoader > sdk)
2001
+ this.sdkLoaderOverride = options.sdkLoader ?? this.sdkLoaderOverride;
2002
+ this.sdkOverride = options.sdk ?? this.sdkOverride;
1967
2003
  this.userGenerateBaseFields = options.generateBaseFields ?? this.userGenerateBaseFields;
1968
2004
  this.autoGenerateBaseFields = createAutoDeviceInfoBaseFields(this.envType, options.deviceInfo);
1969
2005
  this.storageKey = options.storageKey ?? this.storageKey;
@@ -1973,8 +2009,10 @@
1973
2009
  this.startupDelayMs = options.batch?.startupDelayMs ?? this.startupDelayMs;
1974
2010
  this.failedCacheKey = options.failedCacheKey ?? this.failedCacheKey;
1975
2011
  this.failedCacheMax = options.failedCacheMax ?? this.failedCacheMax;
1976
- // 预热 client
1977
- this.getInstance();
2012
+ // 预热(避免首条日志触发 import/初始化开销)
2013
+ void this.getInstance().catch(() => {
2014
+ // ignore
2015
+ });
1978
2016
  // 启动时尝试发送失败缓存
1979
2017
  this.flushFailed();
1980
2018
  // 初始化后立即启动请求监听
@@ -2078,20 +2116,29 @@
2078
2116
  this.behaviorMonitorCleanup = null;
2079
2117
  this.behaviorMonitorStarted = false;
2080
2118
  }
2081
- getInstance() {
2119
+ /**
2120
+ * 获取 CLS client(按环境懒加载 SDK)
2121
+ */
2122
+ async getInstance() {
2082
2123
  if (this.client)
2083
2124
  return this.client;
2084
- this.client = new tencentcloudClsSdkJsWeb.AsyncClient({
2085
- endpoint: this.endpoint,
2086
- retry_times: this.retryTimes,
2125
+ if (this.clientPromise)
2126
+ return this.clientPromise;
2127
+ this.clientPromise = this.loadSdk()
2128
+ .then(({ AsyncClient }) => {
2129
+ const client = new AsyncClient({
2130
+ endpoint: this.endpoint,
2131
+ retry_times: this.retryTimes,
2132
+ });
2133
+ this.client = client;
2134
+ return client;
2135
+ })
2136
+ .catch((err) => {
2137
+ // 失败后允许下次重试
2138
+ this.clientPromise = null;
2139
+ throw err;
2087
2140
  });
2088
- return this.client;
2089
- }
2090
- detectEnvType() {
2091
- const wxAny = globalThis.wx;
2092
- if (wxAny && typeof wxAny.getSystemInfoSync === 'function')
2093
- return 'miniprogram';
2094
- return 'browser';
2141
+ return this.clientPromise;
2095
2142
  }
2096
2143
  /**
2097
2144
  * 直接上报:埋点入参必须是一维(扁平)Object
@@ -2117,22 +2164,33 @@
2117
2164
  appVersion: this.appVersion || undefined,
2118
2165
  ...normalizedFields,
2119
2166
  });
2120
- const client = this.getInstance();
2121
- const logGroup = new tencentcloudClsSdkJsWeb.LogGroup('127.0.0.1');
2167
+ // 同步 API:内部异步发送,避免把网络异常冒泡到业务(尤其小程序)
2168
+ void this.putAsync(finalFields).catch(() => {
2169
+ // ignore
2170
+ });
2171
+ }
2172
+ async putAsync(finalFields) {
2173
+ if (!this.topicId)
2174
+ return;
2175
+ const sdk = await this.loadSdk();
2176
+ const client = await this.getInstance();
2177
+ const logGroup = new sdk.LogGroup('127.0.0.1');
2122
2178
  logGroup.setSource(this.source);
2123
- const log = new tencentcloudClsSdkJsWeb.Log(Date.now());
2179
+ const log = new sdk.Log(Date.now());
2124
2180
  for (const key of Object.keys(finalFields)) {
2125
2181
  log.addContent(key, stringifyLogValue(finalFields[key]));
2126
2182
  }
2127
2183
  logGroup.addLog(log);
2128
- const request = new tencentcloudClsSdkJsWeb.PutLogsRequest(this.topicId, logGroup);
2184
+ const request = new sdk.PutLogsRequest(this.topicId, logGroup);
2129
2185
  const exit = enterClsSendingGuard();
2186
+ let p;
2130
2187
  try {
2131
- client.PutLogs(request);
2188
+ p = client.PutLogs(request);
2132
2189
  }
2133
2190
  finally {
2134
2191
  exit();
2135
2192
  }
2193
+ await p;
2136
2194
  }
2137
2195
  /**
2138
2196
  * 直接上报:把 data 序列化后放入指定 key(默认 “日志内容”)
@@ -2191,11 +2249,19 @@
2191
2249
  console.warn('ClsLogger.putBatch:未初始化 topic_id');
2192
2250
  return;
2193
2251
  }
2194
- const client = this.getInstance();
2195
- const logGroup = new tencentcloudClsSdkJsWeb.LogGroup('127.0.0.1');
2252
+ void this.putBatchAsync(queue).catch(() => {
2253
+ // ignore
2254
+ });
2255
+ }
2256
+ async putBatchAsync(queue) {
2257
+ if (!this.topicId)
2258
+ return;
2259
+ const sdk = await this.loadSdk();
2260
+ const client = await this.getInstance();
2261
+ const logGroup = new sdk.LogGroup('127.0.0.1');
2196
2262
  logGroup.setSource(this.source);
2197
2263
  for (const item of queue) {
2198
- const log = new tencentcloudClsSdkJsWeb.Log(item.time);
2264
+ const log = new sdk.Log(item.time);
2199
2265
  const data = item.data ?? {};
2200
2266
  for (const key of Object.keys(data)) {
2201
2267
  log.addContent(key, stringifyLogValue(data[key]));
@@ -2204,14 +2270,16 @@
2204
2270
  }
2205
2271
  if (logGroup.getLogs().length === 0)
2206
2272
  return;
2207
- const request = new tencentcloudClsSdkJsWeb.PutLogsRequest(this.topicId, logGroup);
2273
+ const request = new sdk.PutLogsRequest(this.topicId, logGroup);
2208
2274
  const exit = enterClsSendingGuard();
2275
+ let p;
2209
2276
  try {
2210
- client.PutLogs(request);
2277
+ p = client.PutLogs(request);
2211
2278
  }
2212
2279
  finally {
2213
2280
  exit();
2214
2281
  }
2282
+ await p;
2215
2283
  }
2216
2284
  /**
2217
2285
  * 参考《一、概述》:统一上报入口(内存队列 + 批量发送)
@@ -2324,12 +2392,13 @@
2324
2392
  async sendReportLogs(logs) {
2325
2393
  if (!this.topicId)
2326
2394
  return;
2327
- const client = this.getInstance();
2328
- const logGroup = new tencentcloudClsSdkJsWeb.LogGroup('127.0.0.1');
2395
+ const sdk = await this.loadSdk();
2396
+ const client = await this.getInstance();
2397
+ const logGroup = new sdk.LogGroup('127.0.0.1');
2329
2398
  logGroup.setSource(this.source);
2330
2399
  for (const item of logs) {
2331
2400
  const fields = this.buildReportFields(item);
2332
- const log = new tencentcloudClsSdkJsWeb.Log(fields.timestamp);
2401
+ const log = new sdk.Log(fields.timestamp);
2333
2402
  for (const key of Object.keys(fields)) {
2334
2403
  if (key === 'timestamp')
2335
2404
  continue;
@@ -2337,7 +2406,7 @@
2337
2406
  }
2338
2407
  logGroup.addLog(log);
2339
2408
  }
2340
- const request = new tencentcloudClsSdkJsWeb.PutLogsRequest(this.topicId, logGroup);
2409
+ const request = new sdk.PutLogsRequest(this.topicId, logGroup);
2341
2410
  // 只在“发起网络请求”的同步阶段打标记,避免 requestMonitor 监控 CLS 上报请求导致递归
2342
2411
  const exit = enterClsSendingGuard();
2343
2412
  let p;
@@ -2413,6 +2482,122 @@
2413
2482
  }
2414
2483
  }
2415
2484
 
2485
+ function readGlobal(key) {
2486
+ try {
2487
+ const g = globalThis;
2488
+ return g[key] ?? null;
2489
+ }
2490
+ catch {
2491
+ return null;
2492
+ }
2493
+ }
2494
+ function tryRequire(moduleName) {
2495
+ try {
2496
+ // 说明:
2497
+ // - ESM 构建(exports.import/module)里通常不存在模块作用域的 require
2498
+ // - 一些小程序运行时/构建链路会把 require 挂到 globalThis 上
2499
+ // 因此这里同时探测“模块作用域 require”与 “globalThis.require”
2500
+ // eslint-disable-next-line @typescript-eslint/no-implied-eval
2501
+ const localReq = (typeof require === 'function' ? require : null);
2502
+ const globalReq = readGlobal('require');
2503
+ const candidates = [localReq, globalReq].filter((fn) => typeof fn === 'function');
2504
+ for (const fn of candidates) {
2505
+ try {
2506
+ return fn(moduleName);
2507
+ }
2508
+ catch {
2509
+ // continue
2510
+ }
2511
+ }
2512
+ return null;
2513
+ }
2514
+ catch {
2515
+ return null;
2516
+ }
2517
+ }
2518
+
2519
+ /**
2520
+ * 兼容版 ClsLogger(默认入口)
2521
+ * - 保留了自动识别环境 + 动态 import 的逻辑
2522
+ * - 如果业务想瘦身,建议改用 @be-link/cls-logger/web 或 /mini
2523
+ */
2524
+ class ClsLogger extends ClsLoggerCore {
2525
+ async loadSdk() {
2526
+ if (this.sdk)
2527
+ return this.sdk;
2528
+ if (this.sdkPromise)
2529
+ return this.sdkPromise;
2530
+ const normalizeSdk = (m) => {
2531
+ const mod = (m?.default && m.default.AsyncClient ? m.default : m);
2532
+ if (mod?.AsyncClient && mod?.Log && mod?.LogGroup && mod?.PutLogsRequest) {
2533
+ return {
2534
+ AsyncClient: mod.AsyncClient,
2535
+ Log: mod.Log,
2536
+ LogGroup: mod.LogGroup,
2537
+ PutLogsRequest: mod.PutLogsRequest,
2538
+ };
2539
+ }
2540
+ return null;
2541
+ };
2542
+ // 1) 外部注入的 loader(最高优先级)
2543
+ if (this.sdkLoaderOverride) {
2544
+ try {
2545
+ const loaded = await this.sdkLoaderOverride();
2546
+ const sdk = normalizeSdk(loaded);
2547
+ if (sdk) {
2548
+ this.sdk = sdk;
2549
+ return sdk;
2550
+ }
2551
+ }
2552
+ catch {
2553
+ // ignore and fallback
2554
+ }
2555
+ }
2556
+ // 2) 外部直接注入的 sdk
2557
+ if (this.sdkOverride) {
2558
+ const sdk = normalizeSdk(this.sdkOverride);
2559
+ if (sdk) {
2560
+ this.sdk = sdk;
2561
+ return sdk;
2562
+ }
2563
+ }
2564
+ const isMini = this.envType === 'miniprogram';
2565
+ // 静态字面量,方便打包器识别(但在此兼容入口里,仍然可能被一起分析)
2566
+ const WEB_SDK = 'tencentcloud-cls-sdk-js-web';
2567
+ const MINI_SDK = 'tencentcloud-cls-sdk-js-mini';
2568
+ // UMD(浏览器脚本)优先读全局变量
2569
+ if (!isMini) {
2570
+ const g = readGlobal('tencentcloudClsSdkJsWeb');
2571
+ const sdk = normalizeSdk(g);
2572
+ if (sdk) {
2573
+ this.sdk = sdk;
2574
+ return sdk;
2575
+ }
2576
+ }
2577
+ // 尽量同步 require
2578
+ const reqMod = tryRequire(isMini ? MINI_SDK : WEB_SDK);
2579
+ const reqSdk = normalizeSdk(reqMod);
2580
+ if (reqSdk) {
2581
+ this.sdk = reqSdk;
2582
+ return reqSdk;
2583
+ }
2584
+ // 动态 import
2585
+ this.sdkPromise = (isMini ? import(MINI_SDK) : import(WEB_SDK))
2586
+ .then((m) => {
2587
+ const sdk = normalizeSdk(m);
2588
+ if (!sdk)
2589
+ throw new Error(`ClsLogger.loadSdk: invalid sdk module for ${isMini ? MINI_SDK : WEB_SDK}`);
2590
+ this.sdk = sdk;
2591
+ return sdk;
2592
+ })
2593
+ .catch((err) => {
2594
+ this.sdkPromise = null;
2595
+ throw err;
2596
+ });
2597
+ return this.sdkPromise;
2598
+ }
2599
+ }
2600
+
2416
2601
  const clsLogger = new ClsLogger();
2417
2602
 
2418
2603
  exports.ClsLogger = ClsLogger;
package/dist/mini.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ import { ClsLoggerMini } from './ClsLoggerMini';
2
+ export * from './types';
3
+ export { ClsLoggerMini as ClsLogger };
4
+ export declare const clsLogger: ClsLoggerMini;
5
+ export default clsLogger;
6
+ //# sourceMappingURL=mini.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mini.d.ts","sourceRoot":"","sources":["../src/mini.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEhD,cAAc,SAAS,CAAC;AACxB,OAAO,EAAE,aAAa,IAAI,SAAS,EAAE,CAAC;AACtC,eAAO,MAAM,SAAS,eAAsB,CAAC;AAC7C,eAAe,SAAS,CAAC"}