@pluve/logger-sdk 0.0.6 → 0.0.8

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.
Files changed (105) hide show
  1. package/README.md +139 -432
  2. package/dist/cjs/capture/jsError.js +48 -0
  3. package/dist/cjs/capture/promiseError.js +49 -0
  4. package/dist/cjs/capture/resourceError.js +48 -0
  5. package/dist/cjs/capture/wechatError.js +95 -0
  6. package/dist/cjs/compress/compression.js +84 -0
  7. package/dist/cjs/config/index.js +55 -0
  8. package/dist/cjs/core/fingerprint.js +36 -0
  9. package/dist/cjs/core/httpClient.js +96 -0
  10. package/dist/cjs/core/loggerSDK.js +641 -0
  11. package/dist/cjs/core/queueManager.js +249 -0
  12. package/dist/cjs/core/retryManager.js +127 -0
  13. package/dist/cjs/index.js +29 -0
  14. package/dist/cjs/logger-sdk.mermaid +84 -0
  15. package/dist/cjs/logger-sdk.svg +1 -0
  16. package/dist/cjs/stack/stacktrace.js +48 -0
  17. package/dist/cjs/transport/beaconTransport.js +64 -0
  18. package/dist/cjs/transport/pixelImageTransport.js +100 -0
  19. package/dist/cjs/transport/transport.js +17 -0
  20. package/dist/cjs/transport/transportAdapter.js +56 -0
  21. package/dist/cjs/transport/wechatTransport.js +103 -0
  22. package/dist/cjs/types/api.js +17 -0
  23. package/dist/cjs/types/env.js +17 -0
  24. package/dist/cjs/types/external.d.ts +21 -0
  25. package/dist/cjs/types/logEvent.js +17 -0
  26. package/dist/cjs/types/logEventLevel.js +17 -0
  27. package/dist/cjs/types/sdkOptions.js +17 -0
  28. package/dist/cjs/types/trackOptions.js +17 -0
  29. package/dist/cjs/utils/environment.js +183 -0
  30. package/dist/cjs/utils/session.js +31 -0
  31. package/dist/cjs/utils/tools.js +82 -0
  32. package/dist/cjs/utils/uuid.js +35 -0
  33. package/dist/esm/capture/jsError.js +45 -0
  34. package/dist/esm/capture/promiseError.js +46 -0
  35. package/dist/esm/capture/resourceError.js +24 -0
  36. package/dist/esm/capture/wechatError.js +92 -0
  37. package/dist/esm/compress/compression.js +82 -0
  38. package/dist/esm/config/index.js +28 -0
  39. package/dist/esm/core/fingerprint.js +12 -0
  40. package/dist/esm/core/httpClient.js +95 -0
  41. package/dist/esm/core/loggerSDK.js +650 -0
  42. package/dist/esm/core/queueManager.js +269 -0
  43. package/dist/esm/core/retryManager.js +129 -0
  44. package/dist/esm/index.js +5 -0
  45. package/dist/esm/logger-sdk.mermaid +84 -0
  46. package/dist/esm/logger-sdk.svg +1 -0
  47. package/dist/esm/stack/stacktrace.js +37 -0
  48. package/dist/esm/transport/beaconTransport.js +81 -0
  49. package/dist/esm/transport/pixelImageTransport.js +99 -0
  50. package/dist/esm/transport/transport.js +0 -0
  51. package/dist/esm/transport/transportAdapter.js +32 -0
  52. package/dist/esm/transport/wechatTransport.js +120 -0
  53. package/dist/esm/types/api.js +0 -0
  54. package/dist/esm/types/env.js +0 -0
  55. package/dist/esm/types/external.d.ts +21 -0
  56. package/dist/esm/types/logEvent.js +0 -0
  57. package/dist/esm/types/logEventLevel.js +0 -0
  58. package/dist/esm/types/sdkOptions.js +0 -0
  59. package/dist/esm/types/trackOptions.js +0 -0
  60. package/dist/esm/utils/environment.js +154 -0
  61. package/dist/esm/utils/session.js +7 -0
  62. package/dist/esm/utils/tools.js +76 -0
  63. package/dist/esm/utils/uuid.js +11 -0
  64. package/dist/types/capture/jsError.d.ts +2 -0
  65. package/dist/types/capture/promiseError.d.ts +2 -0
  66. package/dist/types/capture/resourceError.d.ts +2 -0
  67. package/dist/types/capture/wechatError.d.ts +3 -0
  68. package/dist/types/compress/compression.d.ts +10 -0
  69. package/dist/types/config/index.d.ts +9 -0
  70. package/dist/types/core/fingerprint.d.ts +8 -0
  71. package/dist/types/core/httpClient.d.ts +11 -0
  72. package/dist/{loggerSDK.d.ts → types/core/loggerSDK.d.ts} +32 -14
  73. package/dist/{queueManager.d.ts → types/core/queueManager.d.ts} +9 -5
  74. package/dist/{retryManager.d.ts → types/core/retryManager.d.ts} +2 -6
  75. package/dist/types/index.d.ts +1 -0
  76. package/dist/types/stack/stacktrace.d.ts +2 -0
  77. package/dist/types/transport/beaconTransport.d.ts +11 -0
  78. package/dist/types/transport/pixelImageTransport.d.ts +11 -0
  79. package/dist/types/transport/transport.d.ts +14 -0
  80. package/dist/types/transport/transportAdapter.d.ts +10 -0
  81. package/dist/types/transport/wechatTransport.d.ts +11 -0
  82. package/dist/types/types/api.d.ts +12 -0
  83. package/dist/types/types/env.d.ts +14 -0
  84. package/dist/types/types/logEvent.d.ts +57 -0
  85. package/dist/types/types/logEventLevel.d.ts +2 -0
  86. package/dist/types/types/sdkOptions.d.ts +61 -0
  87. package/dist/types/types/trackOptions.d.ts +7 -0
  88. package/dist/types/utils/environment.d.ts +21 -0
  89. package/dist/types/utils/session.d.ts +1 -0
  90. package/dist/types/utils/tools.d.ts +12 -0
  91. package/dist/types/utils/uuid.d.ts +7 -0
  92. package/dist/umd/logger-sdk.min.js +1 -0
  93. package/package.json +30 -6
  94. package/README.html +0 -982
  95. package/dist/index.d.ts +0 -10
  96. package/dist/index.js +0 -13
  97. package/dist/loggerSDK.js +0 -573
  98. package/dist/queueManager.js +0 -201
  99. package/dist/retryManager.js +0 -223
  100. package/dist/transportAdapter.d.ts +0 -51
  101. package/dist/transportAdapter.js +0 -315
  102. package/dist/types.d.ts +0 -70
  103. package/dist/types.js +0 -1
  104. package/dist/utils.d.ts +0 -47
  105. package/dist/utils.js +0 -306
package/README.html DELETED
@@ -1,982 +0,0 @@
1
- <!DOCTYPE html>
2
- <html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"><head>
3
-
4
- <meta charset="utf-8">
5
- <meta name="generator" content="quarto-1.8.26">
6
-
7
- <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
8
-
9
-
10
- <title>readme</title>
11
- <style>
12
- code{white-space: pre-wrap;}
13
- span.smallcaps{font-variant: small-caps;}
14
- div.columns{display: flex; gap: min(4vw, 1.5em);}
15
- div.column{flex: auto; overflow-x: auto;}
16
- div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
17
- ul.task-list{list-style: none;}
18
- ul.task-list li input[type="checkbox"] {
19
- width: 0.8em;
20
- margin: 0 0.8em 0.2em -1em; /* quarto-specific, see https://github.com/quarto-dev/quarto-cli/issues/4556 */
21
- vertical-align: middle;
22
- }
23
- /* CSS for syntax highlighting */
24
- html { -webkit-text-size-adjust: 100%; }
25
- pre > code.sourceCode { white-space: pre; position: relative; }
26
- pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
27
- pre > code.sourceCode > span:empty { height: 1.2em; }
28
- .sourceCode { overflow: visible; }
29
- code.sourceCode > span { color: inherit; text-decoration: inherit; }
30
- div.sourceCode { margin: 1em 0; }
31
- pre.sourceCode { margin: 0; }
32
- @media screen {
33
- div.sourceCode { overflow: auto; }
34
- }
35
- @media print {
36
- pre > code.sourceCode { white-space: pre-wrap; }
37
- pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
38
- }
39
- pre.numberSource code
40
- { counter-reset: source-line 0; }
41
- pre.numberSource code > span
42
- { position: relative; left: -4em; counter-increment: source-line; }
43
- pre.numberSource code > span > a:first-child::before
44
- { content: counter(source-line);
45
- position: relative; left: -1em; text-align: right; vertical-align: baseline;
46
- border: none; display: inline-block;
47
- -webkit-touch-callout: none; -webkit-user-select: none;
48
- -khtml-user-select: none; -moz-user-select: none;
49
- -ms-user-select: none; user-select: none;
50
- padding: 0 4px; width: 4em;
51
- }
52
- pre.numberSource { margin-left: 3em; padding-left: 4px; }
53
- div.sourceCode
54
- { }
55
- @media screen {
56
- pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
57
- }
58
- </style>
59
-
60
-
61
- <script src="README_files/libs/clipboard/clipboard.min.js"></script>
62
- <script src="README_files/libs/quarto-html/quarto.js" type="module"></script>
63
- <script src="README_files/libs/quarto-html/tabsets/tabsets.js" type="module"></script>
64
- <script src="README_files/libs/quarto-html/axe/axe-check.js" type="module"></script>
65
- <script src="README_files/libs/quarto-html/popper.min.js"></script>
66
- <script src="README_files/libs/quarto-html/tippy.umd.min.js"></script>
67
- <script src="README_files/libs/quarto-html/anchor.min.js"></script>
68
- <link href="README_files/libs/quarto-html/tippy.css" rel="stylesheet">
69
- <link href="README_files/libs/quarto-html/quarto-syntax-highlighting-587c61ba64f3a5504c4d52d930310e48.css" rel="stylesheet" id="quarto-text-highlighting-styles">
70
- <script src="README_files/libs/bootstrap/bootstrap.min.js"></script>
71
- <link href="README_files/libs/bootstrap/bootstrap-icons.css" rel="stylesheet">
72
- <link href="README_files/libs/bootstrap/bootstrap-d6a003b94517c951b2d65075d42fb01b.min.css" rel="stylesheet" append-hash="true" id="quarto-bootstrap" data-mode="light">
73
-
74
-
75
- </head>
76
-
77
- <body class="fullcontent quarto-light">
78
-
79
- <div id="quarto-content" class="page-columns page-rows-contents page-layout-article">
80
-
81
- <main class="content" id="quarto-document-content">
82
-
83
-
84
-
85
-
86
- <section id="pluvelogger-sdk-使用说明" class="level1">
87
- <h1><span class="citation" data-cites="pluve/logger-sdk">@pluve/logger-sdk</span> 使用说明</h1>
88
- <section id="简介" class="level2">
89
- <h2 class="anchored" data-anchor-id="简介">简介</h2>
90
- <ul>
91
- <li>轻量级前端日志采集 SDK,支持 H5 浏览器环境和微信小程序</li>
92
- <li>使用 Beacon 和像素图(Image)方式上报,确保兼容性与可靠性</li>
93
- <li>标准化日志格式,易于分析和处理</li>
94
- <li>支持批量上报、异常重试、持久化存储等高级功能</li>
95
- </ul>
96
- </section>
97
- <section id="安装与引入" class="level2">
98
- <h2 class="anchored" data-anchor-id="安装与引入">安装与引入</h2>
99
- <ul>
100
- <li>安装:<code>pnpm add @pluve/logger-sdk</code></li>
101
- <li>ESM:<code>import LoggerSDK from '@pluve/logger-sdk'</code></li>
102
- <li>UMD:构建后全局为 <code>LoggerSDK</code></li>
103
- </ul>
104
- </section>
105
- <section id="快速开始单例-init" class="level2">
106
- <h2 class="anchored" data-anchor-id="快速开始单例-init">快速开始(单例 + init)</h2>
107
- <div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb1"><pre class="sourceCode ts code-with-copy"><code class="sourceCode typescript"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> LoggerSDK <span class="im">from</span> <span class="st">'@pluve/logger-sdk'</span><span class="op">;</span></span>
108
- <span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a></span>
109
- <span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="co">// 推荐使用单例</span></span>
110
- <span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a><span class="kw">const</span> sdk <span class="op">=</span> LoggerSDK<span class="op">.</span><span class="fu">getInstance</span>()<span class="op">;</span></span>
111
- <span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a>sdk<span class="op">.</span><span class="fu">init</span>({</span>
112
- <span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a> endpoint<span class="op">:</span> <span class="st">'/api/log'</span><span class="op">,</span></span>
113
- <span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a> appId<span class="op">:</span> <span class="st">'web-shop'</span><span class="op">,</span></span>
114
- <span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a> env<span class="op">:</span> <span class="st">'prod'</span><span class="op">,</span> <span class="co">// prod/stage/dev</span></span>
115
- <span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a> debug<span class="op">:</span> <span class="kw">true</span><span class="op">,</span></span>
116
- <span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a> <span class="co">// 默认批量关闭,如需开启:</span></span>
117
- <span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a> <span class="co">// enableBatch: true,</span></span>
118
- <span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span></span>
119
- <span id="cb1-13"><a href="#cb1-13" aria-hidden="true" tabindex="-1"></a></span>
120
- <span id="cb1-14"><a href="#cb1-14" aria-hidden="true" tabindex="-1"></a><span class="co">// init 会自动上报一次 'session_start',携带环境信息(UA/OS/屏幕等)</span></span>
121
- <span id="cb1-15"><a href="#cb1-15" aria-hidden="true" tabindex="-1"></a><span class="co">// 后续事件默认不再合并环境标签(节省流量)</span></span>
122
- <span id="cb1-16"><a href="#cb1-16" aria-hidden="true" tabindex="-1"></a></span>
123
- <span id="cb1-17"><a href="#cb1-17" aria-hidden="true" tabindex="-1"></a><span class="co">// 记录错误日志</span></span>
124
- <span id="cb1-18"><a href="#cb1-18" aria-hidden="true" tabindex="-1"></a><span class="cf">await</span> sdk<span class="op">.</span><span class="fu">track</span>(<span class="st">'error'</span><span class="op">,</span> <span class="st">'TypeError: Cannot read property'</span><span class="op">,</span> <span class="kw">undefined</span><span class="op">,</span> {</span>
125
- <span id="cb1-19"><a href="#cb1-19" aria-hidden="true" tabindex="-1"></a> level<span class="op">:</span> <span class="st">'error'</span><span class="op">,</span></span>
126
- <span id="cb1-20"><a href="#cb1-20" aria-hidden="true" tabindex="-1"></a> stack<span class="op">:</span> <span class="st">'Error stack trace...'</span><span class="op">,</span></span>
127
- <span id="cb1-21"><a href="#cb1-21" aria-hidden="true" tabindex="-1"></a> userId<span class="op">:</span> <span class="st">'123'</span><span class="op">,</span></span>
128
- <span id="cb1-22"><a href="#cb1-22" aria-hidden="true" tabindex="-1"></a> tags<span class="op">:</span> {</span>
129
- <span id="cb1-23"><a href="#cb1-23" aria-hidden="true" tabindex="-1"></a> component<span class="op">:</span> <span class="st">'checkout'</span><span class="op">,</span></span>
130
- <span id="cb1-24"><a href="#cb1-24" aria-hidden="true" tabindex="-1"></a> }<span class="op">,</span></span>
131
- <span id="cb1-25"><a href="#cb1-25" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span></span>
132
- <span id="cb1-26"><a href="#cb1-26" aria-hidden="true" tabindex="-1"></a></span>
133
- <span id="cb1-27"><a href="#cb1-27" aria-hidden="true" tabindex="-1"></a><span class="co">// 记录带 traceId 的错误(用于关联多个日志)</span></span>
134
- <span id="cb1-28"><a href="#cb1-28" aria-hidden="true" tabindex="-1"></a><span class="cf">await</span> sdk<span class="op">.</span><span class="fu">track</span>(<span class="st">'error'</span><span class="op">,</span> <span class="st">'Request failed'</span><span class="op">,</span> <span class="st">'trace-abc-123'</span><span class="op">,</span> {</span>
135
- <span id="cb1-29"><a href="#cb1-29" aria-hidden="true" tabindex="-1"></a> level<span class="op">:</span> <span class="st">'error'</span><span class="op">,</span></span>
136
- <span id="cb1-30"><a href="#cb1-30" aria-hidden="true" tabindex="-1"></a> tags<span class="op">:</span> { api<span class="op">:</span> <span class="st">'/api/user'</span> }<span class="op">,</span></span>
137
- <span id="cb1-31"><a href="#cb1-31" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span></span>
138
- <span id="cb1-32"><a href="#cb1-32" aria-hidden="true" tabindex="-1"></a></span>
139
- <span id="cb1-33"><a href="#cb1-33" aria-hidden="true" tabindex="-1"></a><span class="co">// 记录自定义事件</span></span>
140
- <span id="cb1-34"><a href="#cb1-34" aria-hidden="true" tabindex="-1"></a><span class="cf">await</span> sdk<span class="op">.</span><span class="fu">track</span>(<span class="st">'custom'</span><span class="op">,</span> <span class="st">'User clicked button'</span><span class="op">,</span> <span class="kw">undefined</span><span class="op">,</span> {</span>
141
- <span id="cb1-35"><a href="#cb1-35" aria-hidden="true" tabindex="-1"></a> level<span class="op">:</span> <span class="st">'info'</span><span class="op">,</span></span>
142
- <span id="cb1-36"><a href="#cb1-36" aria-hidden="true" tabindex="-1"></a> tags<span class="op">:</span> { action<span class="op">:</span> <span class="st">'click'</span><span class="op">,</span> button<span class="op">:</span> <span class="st">'submit'</span> }<span class="op">,</span></span>
143
- <span id="cb1-37"><a href="#cb1-37" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span></span></code></pre></div><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></div>
144
- </section>
145
- <section id="标准化日志格式" class="level2">
146
- <h2 class="anchored" data-anchor-id="标准化日志格式">标准化日志格式</h2>
147
- <p>上报的日志数据遵循以下标准格式:</p>
148
- <div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb2"><pre class="sourceCode typescript code-with-copy"><code class="sourceCode typescript"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a>{</span>
149
- <span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a> <span class="st">"logId"</span><span class="op">:</span> <span class="st">"550e8400-e29b-41d4-a716-446655440000"</span><span class="op">,</span> <span class="co">// 日志 ID(UUID v4)</span></span>
150
- <span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a> <span class="st">"traceId"</span><span class="op">:</span> <span class="st">"trace-123456"</span><span class="op">,</span> <span class="co">// 可选:追踪 ID(用于关联多个日志)</span></span>
151
- <span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a> <span class="st">"eventType"</span><span class="op">:</span> <span class="st">"error"</span><span class="op">,</span> <span class="co">// 固定:error/crash/pageview/custom/session_start</span></span>
152
- <span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a> <span class="st">"ts"</span><span class="op">:</span> <span class="dv">1690000000000</span><span class="op">,</span> <span class="co">// 毫秒时间戳</span></span>
153
- <span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a> <span class="st">"appId"</span><span class="op">:</span> <span class="st">"web-shop"</span><span class="op">,</span> <span class="co">// 应用标识</span></span>
154
- <span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a> <span class="st">"env"</span><span class="op">:</span> <span class="st">"prod"</span><span class="op">,</span> <span class="co">// prod/stage/dev</span></span>
155
- <span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a> <span class="st">"level"</span><span class="op">:</span> <span class="st">"error"</span><span class="op">,</span> <span class="co">// info/warn/error/fatal</span></span>
156
- <span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a> <span class="st">"message"</span><span class="op">:</span> <span class="st">"TypeError: ..."</span><span class="op">,</span> <span class="co">// 摘要</span></span>
157
- <span id="cb2-10"><a href="#cb2-10" aria-hidden="true" tabindex="-1"></a> <span class="st">"stack"</span><span class="op">:</span> <span class="st">"..."</span><span class="op">,</span> <span class="co">// 可选:长字符串</span></span>
158
- <span id="cb2-11"><a href="#cb2-11" aria-hidden="true" tabindex="-1"></a> <span class="st">"url"</span><span class="op">:</span> <span class="st">"https://..."</span><span class="op">,</span> <span class="co">// 发生页面</span></span>
159
- <span id="cb2-12"><a href="#cb2-12" aria-hidden="true" tabindex="-1"></a> <span class="st">"userId"</span><span class="op">:</span> <span class="st">"123"</span><span class="op">,</span> <span class="co">// 可选:用户ID(脱敏)</span></span>
160
- <span id="cb2-13"><a href="#cb2-13" aria-hidden="true" tabindex="-1"></a> <span class="st">"sessionId"</span><span class="op">:</span> <span class="st">"..."</span><span class="op">,</span> <span class="co">// 会话标识</span></span>
161
- <span id="cb2-14"><a href="#cb2-14" aria-hidden="true" tabindex="-1"></a> <span class="st">"tags"</span><span class="op">:</span> { <span class="co">// 可选的结构化额外信息</span></span>
162
- <span id="cb2-15"><a href="#cb2-15" aria-hidden="true" tabindex="-1"></a> <span class="st">"component"</span><span class="op">:</span> <span class="st">"checkout"</span><span class="op">,</span></span>
163
- <span id="cb2-16"><a href="#cb2-16" aria-hidden="true" tabindex="-1"></a> <span class="st">"browser"</span><span class="op">:</span> <span class="st">"Chrome 120"</span></span>
164
- <span id="cb2-17"><a href="#cb2-17" aria-hidden="true" tabindex="-1"></a> }</span>
165
- <span id="cb2-18"><a href="#cb2-18" aria-hidden="true" tabindex="-1"></a>}</span></code></pre></div><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></div>
166
- </section>
167
- <section id="api" class="level2">
168
- <h2 class="anchored" data-anchor-id="api">API</h2>
169
- <section id="单例与初始化" class="level3">
170
- <h3 class="anchored" data-anchor-id="单例与初始化">单例与初始化</h3>
171
- <section id="loggersdk.getinstance-loggersdk" class="level4">
172
- <h4 class="anchored" data-anchor-id="loggersdk.getinstance-loggersdk"><code>LoggerSDK.getInstance(): LoggerSDK</code></h4>
173
- <p>获取全局唯一实例。</p>
174
- </section>
175
- <section id="initoptions-sdkoptions" class="level4">
176
- <h4 class="anchored" data-anchor-id="initoptions-sdkoptions"><code>init(options: SDKOptions)</code></h4>
177
- <p>初始化 SDK、生成并锁定 sessionId、收集环境信息并上报一次 <code>session_start</code>。 注意:锁定的 sessionId 将在销毁前用于所有日志上报。</p>
178
- </section>
179
- <section id="new-loggersdkoptions-sdkoptions" class="level4">
180
- <h4 class="anchored" data-anchor-id="new-loggersdkoptions-sdkoptions"><code>new LoggerSDK(options?: SDKOptions)</code></h4>
181
- <p>创建 SDK 实例;如果传入 <code>options</code>,会在构造时自动调用 <code>init(options)</code>。</p>
182
- <p><strong>配置项:</strong></p>
183
- <div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb3"><pre class="sourceCode typescript code-with-copy"><code class="sourceCode typescript"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="kw">interface</span> SDKOptions {</span>
184
- <span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a> endpoint<span class="op">:</span> <span class="dt">string</span><span class="op">;</span> <span class="co">// 上报端点 URL(必选)</span></span>
185
- <span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a> appId<span class="op">:</span> <span class="dt">string</span><span class="op">;</span> <span class="co">// 应用 ID(必选)</span></span>
186
- <span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a> env<span class="op">?:</span> Env<span class="op">;</span> <span class="co">// 环境标识:prod/stage/dev,默认 'dev'</span></span>
187
- <span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a> debug<span class="op">?:</span> <span class="dt">boolean</span><span class="op">;</span> <span class="co">// 是否开启调试模式</span></span>
188
- <span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a> pixelParam<span class="op">?:</span> <span class="dt">string</span><span class="op">;</span> <span class="co">// 像素上报参数名,默认 'data'</span></span>
189
- <span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a> maxPixelUrlLen<span class="op">?:</span> <span class="dt">number</span><span class="op">;</span> <span class="co">// 像素上报 URL 最大长度,默认 1900</span></span>
190
- <span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a> enableGzip<span class="op">?:</span> <span class="dt">boolean</span><span class="op">;</span> <span class="co">// 是否启用 gzip(传输适配器内部处理)</span></span>
191
- <span id="cb3-9"><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a> </span>
192
- <span id="cb3-10"><a href="#cb3-10" aria-hidden="true" tabindex="-1"></a> <span class="co">// 批量上报配置</span></span>
193
- <span id="cb3-11"><a href="#cb3-11" aria-hidden="true" tabindex="-1"></a> enableBatch<span class="op">?:</span> <span class="dt">boolean</span><span class="op">;</span> <span class="co">// 是否启用批量上报,默认 false(需显式开启)</span></span>
194
- <span id="cb3-12"><a href="#cb3-12" aria-hidden="true" tabindex="-1"></a> batchSize<span class="op">?:</span> <span class="dt">number</span><span class="op">;</span> <span class="co">// 批量上报最大数量,默认 10</span></span>
195
- <span id="cb3-13"><a href="#cb3-13" aria-hidden="true" tabindex="-1"></a> batchInterval<span class="op">?:</span> <span class="dt">number</span><span class="op">;</span> <span class="co">// 批量上报时间间隔(毫秒),默认 5000</span></span>
196
- <span id="cb3-14"><a href="#cb3-14" aria-hidden="true" tabindex="-1"></a> maxQueueSize<span class="op">?:</span> <span class="dt">number</span><span class="op">;</span> <span class="co">// 队列最大长度,默认 100</span></span>
197
- <span id="cb3-15"><a href="#cb3-15" aria-hidden="true" tabindex="-1"></a> </span>
198
- <span id="cb3-16"><a href="#cb3-16" aria-hidden="true" tabindex="-1"></a> <span class="co">// 持久化存储配置</span></span>
199
- <span id="cb3-17"><a href="#cb3-17" aria-hidden="true" tabindex="-1"></a> enableStorage<span class="op">?:</span> <span class="dt">boolean</span><span class="op">;</span> <span class="co">// 是否启用持久化存储,默认 true</span></span>
200
- <span id="cb3-18"><a href="#cb3-18" aria-hidden="true" tabindex="-1"></a> storagePrefix<span class="op">?:</span> <span class="dt">string</span><span class="op">;</span> <span class="co">// 持久化存储的 key 前缀,默认 'logger_sdk'</span></span>
201
- <span id="cb3-19"><a href="#cb3-19" aria-hidden="true" tabindex="-1"></a> </span>
202
- <span id="cb3-20"><a href="#cb3-20" aria-hidden="true" tabindex="-1"></a> <span class="co">// 重试配置</span></span>
203
- <span id="cb3-21"><a href="#cb3-21" aria-hidden="true" tabindex="-1"></a> enableRetry<span class="op">?:</span> <span class="dt">boolean</span><span class="op">;</span> <span class="co">// 是否启用重试,默认 true</span></span>
204
- <span id="cb3-22"><a href="#cb3-22" aria-hidden="true" tabindex="-1"></a> maxRetries<span class="op">?:</span> <span class="dt">number</span><span class="op">;</span> <span class="co">// 最大重试次数,默认 3</span></span>
205
- <span id="cb3-23"><a href="#cb3-23" aria-hidden="true" tabindex="-1"></a> retryDelay<span class="op">?:</span> <span class="dt">number</span><span class="op">;</span> <span class="co">// 重试基础延迟时间(毫秒),默认 1000</span></span>
206
- <span id="cb3-24"><a href="#cb3-24" aria-hidden="true" tabindex="-1"></a> retryBackoff<span class="op">?:</span> <span class="dt">boolean</span><span class="op">;</span> <span class="co">// 是否使用指数退避策略,默认 true</span></span>
207
- <span id="cb3-25"><a href="#cb3-25" aria-hidden="true" tabindex="-1"></a>}</span></code></pre></div><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></div>
208
- </section>
209
- </section>
210
- <section id="trackeventtype-message-traceid-options" class="level3">
211
- <h3 class="anchored" data-anchor-id="trackeventtype-message-traceid-options"><code>track(eventType, message, traceId?, options?)</code></h3>
212
- <p>记录事件日志。每条日志会自动生成唯一的 <code>logId</code>(UUID v4 格式)。</p>
213
- <p><strong>参数:</strong></p>
214
- <ul>
215
- <li><code>eventType</code>: 事件类型(<code>'error' | 'crash' | 'pageview' | 'custom'</code>)</li>
216
- <li><code>message</code>: 摘要信息</li>
217
- <li><code>traceId</code>: (可选) 追踪 ID,用于关联多个相关日志</li>
218
- <li><code>options</code>: 可选配置
219
- <ul>
220
- <li><code>level?</code>: 日志级别(<code>'info' | 'warn' | 'error' | 'fatal'</code>),默认 <code>'info'</code></li>
221
- <li><code>stack?</code>: 堆栈信息</li>
222
- <li><code>userId?</code>: 用户 ID</li>
223
- <li><code>tags?</code>: 额外的结构化信息</li>
224
- </ul></li>
225
- </ul>
226
- <p><strong>示例:</strong></p>
227
- <div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb4"><pre class="sourceCode typescript code-with-copy"><code class="sourceCode typescript"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="co">// 记录错误</span></span>
228
- <span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a>sdk<span class="op">.</span><span class="fu">track</span>(<span class="st">'error'</span><span class="op">,</span> <span class="st">'Failed to load resource'</span><span class="op">,</span> {</span>
229
- <span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a> level<span class="op">:</span> <span class="st">'error'</span><span class="op">,</span></span>
230
- <span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a> stack<span class="op">:</span> error<span class="op">.</span><span class="at">stack</span><span class="op">,</span></span>
231
- <span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a> userId<span class="op">:</span> <span class="st">'123'</span><span class="op">,</span></span>
232
- <span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a> tags<span class="op">:</span> { resource<span class="op">:</span> <span class="st">'image.png'</span> }<span class="op">,</span></span>
233
- <span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span></span>
234
- <span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a></span>
235
- <span id="cb4-9"><a href="#cb4-9" aria-hidden="true" tabindex="-1"></a><span class="co">// 记录带 traceId 的错误(用于关联多个日志)</span></span>
236
- <span id="cb4-10"><a href="#cb4-10" aria-hidden="true" tabindex="-1"></a>sdk<span class="op">.</span><span class="fu">track</span>(<span class="st">'error'</span><span class="op">,</span> <span class="st">'API request failed'</span><span class="op">,</span> <span class="st">'trace-xyz-789'</span><span class="op">,</span> {</span>
237
- <span id="cb4-11"><a href="#cb4-11" aria-hidden="true" tabindex="-1"></a> level<span class="op">:</span> <span class="st">'error'</span><span class="op">,</span></span>
238
- <span id="cb4-12"><a href="#cb4-12" aria-hidden="true" tabindex="-1"></a> tags<span class="op">:</span> { endpoint<span class="op">:</span> <span class="st">'/api/users'</span><span class="op">,</span> statusCode<span class="op">:</span> <span class="dv">500</span> }<span class="op">,</span></span>
239
- <span id="cb4-13"><a href="#cb4-13" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span></span>
240
- <span id="cb4-14"><a href="#cb4-14" aria-hidden="true" tabindex="-1"></a></span>
241
- <span id="cb4-15"><a href="#cb4-15" aria-hidden="true" tabindex="-1"></a><span class="co">// 记录页面浏览</span></span>
242
- <span id="cb4-16"><a href="#cb4-16" aria-hidden="true" tabindex="-1"></a>sdk<span class="op">.</span><span class="fu">track</span>(<span class="st">'pageview'</span><span class="op">,</span> <span class="st">'User viewed product page'</span><span class="op">,</span> {</span>
243
- <span id="cb4-17"><a href="#cb4-17" aria-hidden="true" tabindex="-1"></a> level<span class="op">:</span> <span class="st">'info'</span><span class="op">,</span></span>
244
- <span id="cb4-18"><a href="#cb4-18" aria-hidden="true" tabindex="-1"></a> tags<span class="op">:</span> { productId<span class="op">:</span> <span class="st">'P123'</span> }<span class="op">,</span></span>
245
- <span id="cb4-19"><a href="#cb4-19" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span></span></code></pre></div><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></div>
246
- </section>
247
- <section id="日志-id-和追踪-id" class="level3">
248
- <h3 class="anchored" data-anchor-id="日志-id-和追踪-id">日志 ID 和追踪 ID</h3>
249
- <ul>
250
- <li><strong>logId</strong>: 每条日志自动生成的唯一标识符(UUID v4 格式),用于全局唯一定位日志</li>
251
- <li><strong>traceId</strong>: 可选的追踪 ID,用于关联同一请求链路上的多个日志(例如:前端请求、后端处理、数据库查询等)</li>
252
- </ul>
253
- <div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb5"><pre class="sourceCode typescript code-with-copy"><code class="sourceCode typescript"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="co">// 在请求开始时生成 traceId</span></span>
254
- <span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a><span class="kw">const</span> traceId <span class="op">=</span> <span class="vs">`trace-</span><span class="sc">${</span><span class="bu">Date</span><span class="op">.</span><span class="fu">now</span>()<span class="sc">}</span><span class="vs">-</span><span class="sc">${</span><span class="bu">Math</span><span class="op">.</span><span class="fu">random</span>()<span class="op">.</span><span class="fu">toString</span>(<span class="dv">36</span>)<span class="op">.</span><span class="fu">substr</span>(<span class="dv">2</span><span class="op">,</span> <span class="dv">9</span>)<span class="sc">}</span><span class="vs">`</span><span class="op">;</span></span>
255
- <span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a></span>
256
- <span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a><span class="co">// 在请求的不同阶段使用相同的 traceId</span></span>
257
- <span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a>sdk<span class="op">.</span><span class="fu">track</span>(<span class="st">'custom'</span><span class="op">,</span> <span class="st">'Request started'</span><span class="op">,</span> traceId<span class="op">,</span> {</span>
258
- <span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a> level<span class="op">:</span> <span class="st">'info'</span><span class="op">,</span></span>
259
- <span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a> tags<span class="op">:</span> { endpoint<span class="op">:</span> <span class="st">'/api/checkout'</span> }<span class="op">,</span></span>
260
- <span id="cb5-8"><a href="#cb5-8" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span></span>
261
- <span id="cb5-9"><a href="#cb5-9" aria-hidden="true" tabindex="-1"></a></span>
262
- <span id="cb5-10"><a href="#cb5-10" aria-hidden="true" tabindex="-1"></a><span class="co">// ... 请求处理 ...</span></span>
263
- <span id="cb5-11"><a href="#cb5-11" aria-hidden="true" tabindex="-1"></a></span>
264
- <span id="cb5-12"><a href="#cb5-12" aria-hidden="true" tabindex="-1"></a>sdk<span class="op">.</span><span class="fu">track</span>(<span class="st">'error'</span><span class="op">,</span> <span class="st">'Request failed'</span><span class="op">,</span> traceId<span class="op">,</span> {</span>
265
- <span id="cb5-13"><a href="#cb5-13" aria-hidden="true" tabindex="-1"></a> level<span class="op">:</span> <span class="st">'error'</span><span class="op">,</span></span>
266
- <span id="cb5-14"><a href="#cb5-14" aria-hidden="true" tabindex="-1"></a> tags<span class="op">:</span> { endpoint<span class="op">:</span> <span class="st">'/api/checkout'</span><span class="op">,</span> error<span class="op">:</span> <span class="st">'Network timeout'</span> }<span class="op">,</span></span>
267
- <span id="cb5-15"><a href="#cb5-15" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span></span></code></pre></div><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></div>
268
- </section>
269
- <section id="identifyuserid-string" class="level3">
270
- <h3 class="anchored" data-anchor-id="identifyuserid-string"><code>identify(userId: string)</code></h3>
271
- <p>设置用户 ID(用于后续日志关联)。</p>
272
- </section>
273
- <section id="traceerror-error-string-options" class="level3">
274
- <h3 class="anchored" data-anchor-id="traceerror-error-string-options"><code>trace(error: Error | string, options?)</code></h3>
275
- <p>错误专用方法,自动设置 <code>eventType: 'error'</code> 与 <code>level: 'error'</code>,不合并环境标签。</p>
276
- <div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb6"><pre class="sourceCode ts code-with-copy"><code class="sourceCode typescript"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="cf">try</span> {</span>
277
- <span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a> <span class="cf">throw</span> <span class="kw">new</span> <span class="bu">Error</span>(<span class="st">'Network Error'</span>)<span class="op">;</span></span>
278
- <span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a>} <span class="cf">catch</span> (e) {</span>
279
- <span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a> <span class="cf">await</span> sdk<span class="op">.</span><span class="fu">trace</span>(e <span class="im">as</span> <span class="bu">Error</span><span class="op">,</span> { userId<span class="op">:</span> <span class="st">'u123'</span><span class="op">,</span> traceId<span class="op">:</span> <span class="st">'req-1'</span> })<span class="op">;</span></span>
280
- <span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a>}</span></code></pre></div><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></div>
281
- </section>
282
- <section id="destroy" class="level3">
283
- <h3 class="anchored" data-anchor-id="destroy"><code>destroy()</code></h3>
284
- <p>销毁 SDK 实例,停止所有监听。</p>
285
- <p>销毁后单例会被清理;如需新会话,重新调用 <code>init()</code> 即可生成新的 sessionId。</p>
286
- </section>
287
- <section id="会话管理" class="level3">
288
- <h3 class="anchored" data-anchor-id="会话管理">会话管理</h3>
289
- <ul>
290
- <li>在 <code>init()</code> 时生成并锁定 <code>sessionId</code></li>
291
- <li>在调用 <code>destroy()</code> 前,所有日志都会使用该 <code>sessionId</code></li>
292
- <li>重新 <code>init()</code> 将开始新的会话(新的 <code>sessionId</code>)</li>
293
- </ul>
294
- </section>
295
- </section>
296
- <section id="自动采集" class="level2">
297
- <h2 class="anchored" data-anchor-id="自动采集">自动采集</h2>
298
- <p>SDK 会自动监听以下页面事件并上报:</p>
299
- <ul>
300
- <li><strong>页面隐藏</strong>(<code>visibilitychange</code>):当页面变为隐藏状态时</li>
301
- <li><strong>页面卸载</strong>(<code>pagehide</code>, <code>beforeunload</code>):当用户离开页面时</li>
302
- </ul>
303
- <p>这些事件使用 Beacon API 上报,确保在页面关闭时仍能成功发送。</p>
304
- </section>
305
- <section id="上报方式" class="level2">
306
- <h2 class="anchored" data-anchor-id="上报方式">上报方式</h2>
307
- <p>SDK 使用两种上报方式,按优先级自动选择:</p>
308
- <section id="beacon-api优先" class="level3">
309
- <h3 class="anchored" data-anchor-id="beacon-api优先">1. Beacon API(优先)</h3>
310
- <ul>
311
- <li><strong>兼容性</strong>:Chrome 39+, Firefox 31+, Edge 14+</li>
312
- <li><strong>特点</strong>:页面卸载时可靠传输,不阻塞页面关闭</li>
313
- <li><strong>限制</strong>:无法获取响应,队列大小限制(通常 64KB)</li>
314
- </ul>
315
- </section>
316
- <section id="image-像素上报降级" class="level3">
317
- <h3 class="anchored" data-anchor-id="image-像素上报降级">2. Image 像素上报(降级)</h3>
318
- <ul>
319
- <li><strong>兼容性</strong>:所有浏览器</li>
320
- <li><strong>特点</strong>:轻量级,无跨域限制</li>
321
- <li><strong>限制</strong>:URL 长度限制(默认 1900 字符)</li>
322
- </ul>
323
- <p>如果 Beacon 失败,会自动降级到 Image 方式。</p>
324
- </section>
325
- <section id="微信小程序适配器" class="level3">
326
- <h3 class="anchored" data-anchor-id="微信小程序适配器">微信小程序适配器</h3>
327
- <ul>
328
- <li>环境:微信小程序</li>
329
- <li>支持:请求成功/失败/非 2xx 状态码与超时控制</li>
330
- <li>Header Content-Type 会根据是否启用 gzip 自动调整</li>
331
- </ul>
332
- </section>
333
- </section>
334
- <section id="使用场景" class="level2">
335
- <h2 class="anchored" data-anchor-id="使用场景">使用场景</h2>
336
- <section id="错误监控" class="level3">
337
- <h3 class="anchored" data-anchor-id="错误监控">错误监控</h3>
338
- <div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb7"><pre class="sourceCode typescript code-with-copy"><code class="sourceCode typescript"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="co">// 全局错误捕获</span></span>
339
- <span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a><span class="bu">window</span><span class="op">.</span><span class="fu">addEventListener</span>(<span class="st">'error'</span><span class="op">,</span> (<span class="bu">event</span>) <span class="kw">=&gt;</span> {</span>
340
- <span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a> sdk<span class="op">.</span><span class="fu">track</span>(<span class="st">'error'</span><span class="op">,</span> <span class="bu">event</span><span class="op">.</span><span class="at">message</span><span class="op">,</span> {</span>
341
- <span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a> level<span class="op">:</span> <span class="st">'error'</span><span class="op">,</span></span>
342
- <span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a> stack<span class="op">:</span> <span class="bu">event</span><span class="op">.</span><span class="at">error</span><span class="op">?.</span><span class="at">stack</span><span class="op">,</span></span>
343
- <span id="cb7-6"><a href="#cb7-6" aria-hidden="true" tabindex="-1"></a> tags<span class="op">:</span> {</span>
344
- <span id="cb7-7"><a href="#cb7-7" aria-hidden="true" tabindex="-1"></a> filename<span class="op">:</span> <span class="bu">event</span><span class="op">.</span><span class="at">filename</span><span class="op">,</span></span>
345
- <span id="cb7-8"><a href="#cb7-8" aria-hidden="true" tabindex="-1"></a> lineno<span class="op">:</span> <span class="bu">event</span><span class="op">.</span><span class="at">lineno</span><span class="op">,</span></span>
346
- <span id="cb7-9"><a href="#cb7-9" aria-hidden="true" tabindex="-1"></a> colno<span class="op">:</span> <span class="bu">event</span><span class="op">.</span><span class="at">colno</span><span class="op">,</span></span>
347
- <span id="cb7-10"><a href="#cb7-10" aria-hidden="true" tabindex="-1"></a> }<span class="op">,</span></span>
348
- <span id="cb7-11"><a href="#cb7-11" aria-hidden="true" tabindex="-1"></a> })<span class="op">;</span></span>
349
- <span id="cb7-12"><a href="#cb7-12" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span></span>
350
- <span id="cb7-13"><a href="#cb7-13" aria-hidden="true" tabindex="-1"></a></span>
351
- <span id="cb7-14"><a href="#cb7-14" aria-hidden="true" tabindex="-1"></a><span class="co">// Promise 错误捕获</span></span>
352
- <span id="cb7-15"><a href="#cb7-15" aria-hidden="true" tabindex="-1"></a><span class="bu">window</span><span class="op">.</span><span class="fu">addEventListener</span>(<span class="st">'unhandledrejection'</span><span class="op">,</span> (<span class="bu">event</span>) <span class="kw">=&gt;</span> {</span>
353
- <span id="cb7-16"><a href="#cb7-16" aria-hidden="true" tabindex="-1"></a> sdk<span class="op">.</span><span class="fu">track</span>(<span class="st">'error'</span><span class="op">,</span> <span class="st">'Unhandled Promise Rejection'</span><span class="op">,</span> {</span>
354
- <span id="cb7-17"><a href="#cb7-17" aria-hidden="true" tabindex="-1"></a> level<span class="op">:</span> <span class="st">'error'</span><span class="op">,</span></span>
355
- <span id="cb7-18"><a href="#cb7-18" aria-hidden="true" tabindex="-1"></a> stack<span class="op">:</span> <span class="bu">event</span><span class="op">.</span><span class="at">reason</span><span class="op">?.</span><span class="at">stack</span> <span class="op">||</span> <span class="bu">String</span>(<span class="bu">event</span><span class="op">.</span><span class="at">reason</span>)<span class="op">,</span></span>
356
- <span id="cb7-19"><a href="#cb7-19" aria-hidden="true" tabindex="-1"></a> })<span class="op">;</span></span>
357
- <span id="cb7-20"><a href="#cb7-20" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span></span></code></pre></div><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></div>
358
- </section>
359
- <section id="页面浏览统计" class="level3">
360
- <h3 class="anchored" data-anchor-id="页面浏览统计">页面浏览统计</h3>
361
- <div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb8"><pre class="sourceCode typescript code-with-copy"><code class="sourceCode typescript"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="co">// 记录页面浏览</span></span>
362
- <span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a>sdk<span class="op">.</span><span class="fu">track</span>(<span class="st">'pageview'</span><span class="op">,</span> <span class="bu">document</span><span class="op">.</span><span class="at">title</span><span class="op">,</span> {</span>
363
- <span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a> level<span class="op">:</span> <span class="st">'info'</span><span class="op">,</span></span>
364
- <span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a> tags<span class="op">:</span> {</span>
365
- <span id="cb8-5"><a href="#cb8-5" aria-hidden="true" tabindex="-1"></a> path<span class="op">:</span> <span class="bu">window</span><span class="op">.</span><span class="at">location</span><span class="op">.</span><span class="at">pathname</span><span class="op">,</span></span>
366
- <span id="cb8-6"><a href="#cb8-6" aria-hidden="true" tabindex="-1"></a> referrer<span class="op">:</span> <span class="bu">document</span><span class="op">.</span><span class="at">referrer</span><span class="op">,</span></span>
367
- <span id="cb8-7"><a href="#cb8-7" aria-hidden="true" tabindex="-1"></a> }<span class="op">,</span></span>
368
- <span id="cb8-8"><a href="#cb8-8" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span></span></code></pre></div><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></div>
369
- </section>
370
- <section id="用户行为追踪" class="level3">
371
- <h3 class="anchored" data-anchor-id="用户行为追踪">用户行为追踪</h3>
372
- <div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb9"><pre class="sourceCode typescript code-with-copy"><code class="sourceCode typescript"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a><span class="co">// 记录用户点击</span></span>
373
- <span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a>button<span class="op">.</span><span class="fu">addEventListener</span>(<span class="st">'click'</span><span class="op">,</span> () <span class="kw">=&gt;</span> {</span>
374
- <span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a> sdk<span class="op">.</span><span class="fu">track</span>(<span class="st">'custom'</span><span class="op">,</span> <span class="st">'Button clicked'</span><span class="op">,</span> {</span>
375
- <span id="cb9-4"><a href="#cb9-4" aria-hidden="true" tabindex="-1"></a> level<span class="op">:</span> <span class="st">'info'</span><span class="op">,</span></span>
376
- <span id="cb9-5"><a href="#cb9-5" aria-hidden="true" tabindex="-1"></a> userId<span class="op">:</span> currentUserId<span class="op">,</span></span>
377
- <span id="cb9-6"><a href="#cb9-6" aria-hidden="true" tabindex="-1"></a> tags<span class="op">:</span> {</span>
378
- <span id="cb9-7"><a href="#cb9-7" aria-hidden="true" tabindex="-1"></a> buttonId<span class="op">:</span> button<span class="op">.</span><span class="at">id</span><span class="op">,</span></span>
379
- <span id="cb9-8"><a href="#cb9-8" aria-hidden="true" tabindex="-1"></a> page<span class="op">:</span> <span class="st">'checkout'</span><span class="op">,</span></span>
380
- <span id="cb9-9"><a href="#cb9-9" aria-hidden="true" tabindex="-1"></a> }<span class="op">,</span></span>
381
- <span id="cb9-10"><a href="#cb9-10" aria-hidden="true" tabindex="-1"></a> })<span class="op">;</span></span>
382
- <span id="cb9-11"><a href="#cb9-11" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span></span></code></pre></div><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></div>
383
- </section>
384
- </section>
385
- <section id="最佳实践" class="level2">
386
- <h2 class="anchored" data-anchor-id="最佳实践">最佳实践</h2>
387
- <ol type="1">
388
- <li><strong>设置正确的环境标识</strong>:确保在不同环境(prod/stage/dev)使用正确的配置</li>
389
- <li><strong>使用有意义的 message</strong>:摘要信息应简洁明了,便于快速定位问题</li>
390
- <li><strong>合理使用 tags</strong>:将额外的结构化信息放在 tags 中,便于分析和过滤</li>
391
- <li><strong>用户隐私保护</strong>:userId 应该经过脱敏处理</li>
392
- <li><strong>控制日志量</strong>:避免在高频操作中记录过多日志</li>
393
- </ol>
394
- </section>
395
- <section id="调试" class="level2">
396
- <h2 class="anchored" data-anchor-id="调试">调试</h2>
397
- <p>开启调试模式查看控制台输出:</p>
398
- <div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb10"><pre class="sourceCode typescript code-with-copy"><code class="sourceCode typescript"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="kw">const</span> sdk <span class="op">=</span> <span class="kw">new</span> <span class="fu">LoggerSDK</span>({</span>
399
- <span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a> endpoint<span class="op">:</span> <span class="st">'/api/log'</span><span class="op">,</span></span>
400
- <span id="cb10-3"><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a> debug<span class="op">:</span> <span class="kw">true</span><span class="op">,</span> <span class="co">// 开启调试</span></span>
401
- <span id="cb10-4"><a href="#cb10-4" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span></span></code></pre></div><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></div>
402
- </section>
403
- <section id="高级功能" class="level2">
404
- <h2 class="anchored" data-anchor-id="高级功能">高级功能</h2>
405
- <section id="批量上报" class="level3">
406
- <h3 class="anchored" data-anchor-id="批量上报">批量上报</h3>
407
- <p>SDK 支持批量上报功能,可以显著减少网络请求次数,提高性能:</p>
408
- <ul>
409
- <li><strong>队列缓存</strong>:日志先缓存到内存队列,达到条件后批量发送</li>
410
- <li><strong>智能触发</strong>:支持按数量阈值和时间间隔触发上报</li>
411
- <li><strong>手动控制</strong>:提供 <code>flush()</code> 方法可手动立即上报</li>
412
- </ul>
413
- </section>
414
- <section id="异常重试" class="level3">
415
- <h3 class="anchored" data-anchor-id="异常重试">异常重试</h3>
416
- <p>为确保日志可靠上报,SDK 提供了自动重试机制:</p>
417
- <ul>
418
- <li><strong>自动重试</strong>:上报失败时自动重试(默认最多3次)</li>
419
- <li><strong>指数退避</strong>:采用指数退避策略(1秒→2秒→4秒…)</li>
420
- <li><strong>随机抖动</strong>:添加0-30%随机延迟避免惊群效应</li>
421
- </ul>
422
- </section>
423
- <section id="持久化存储" class="level3">
424
- <h3 class="anchored" data-anchor-id="持久化存储">持久化存储</h3>
425
- <p>为了防止因页面意外关闭导致日志丢失,SDK 支持持久化存储:</p>
426
- <ul>
427
- <li><strong>自动保存</strong>:队列数据自动保存到 localStorage(浏览器)或 Storage(微信小程序)</li>
428
- <li><strong>自动恢复</strong>:页面刷新后自动恢复未上报的日志</li>
429
- </ul>
430
- </section>
431
- </section>
432
- <section id="核心流程" class="level2">
433
- <h2 class="anchored" data-anchor-id="核心流程">核心流程</h2>
434
- <pre class="mermaid"><code>graph TB
435
- A[用户调用 track() 方法] --&gt; B{SDK是否已销毁?}
436
- B -- 是 --&gt; C[直接返回,不处理]
437
- B -- 否 --&gt; D[生成标准化日志对象]
438
- D --&gt; E{是否启用批量上报?}
439
- E -- 否 --&gt; F[直接上报日志]
440
- E -- 是 --&gt; G[将日志加入队列]
441
- G --&gt; H{队列大小是否达到批处理阈值?}
442
- H -- 是 --&gt; I[立即触发批量上报]
443
- H -- 否 --&gt; J[等待下次触发]
444
-
445
- F --&gt; K{是否启用重试机制?}
446
- K -- 是 --&gt; L[使用重试管理器发送]
447
- K -- 否 --&gt; M[直接发送请求]
448
-
449
- L --&gt; N[重试管理器执行发送任务]
450
- N --&gt; O{发送是否成功?}
451
- O -- 是 --&gt; P[上报完成]
452
- O -- 否 --&gt; Q[是否达到最大重试次数?]
453
- Q -- 否 --&gt; R[计算延迟时间&lt;br/&gt;(指数退避+随机抖动)]
454
- R --&gt; S[等待后重试]
455
- S --&gt; N
456
- Q -- 是 --&gt; T[上报失败,记录错误]
457
-
458
- M --&gt; U{发送是否成功?}
459
- U -- 是 --&gt; P
460
- U -- 否 --&gt; T
461
-
462
- I --&gt; V[执行批量上报]
463
- V --&gt; W[从队列获取待发送日志]
464
- W --&gt; X[调用重试管理器发送批量日志]
465
- X --&gt; Y{批量发送是否成功?}
466
- Y -- 是 --&gt; Z[从队列中移除已发送日志]
467
- Y -- 否 --&gt; AA[保留队列中的日志,下次重试]
468
-
469
- J --&gt; AB{定时器是否触发?}
470
- AB -- 是 --&gt; AC[执行批量上报]
471
- AB -- 否 --&gt; AD{页面是否卸载?}
472
- AD -- 是 --&gt; AE[调用 flush() 方法上报所有日志]
473
-
474
- subgraph 队列管理
475
- AF[内存队列]
476
- AG[持久化存储&lt;br/&gt;localStorage/Storage]
477
- AF &lt;--&gt; AG
478
- end
479
-
480
- subgraph 传输适配器
481
- AH[Beacon 适配器]
482
- AI[Image 像素适配器]
483
- AJ[微信小程序适配器]
484
- AK[自动选择最佳适配器]
485
- AH --&gt; AK
486
- AI --&gt; AK
487
- AJ --&gt; AK
488
- end
489
-
490
- Z --&gt; P
491
- AA --&gt; P
492
- AC --&gt; V
493
- AE --&gt; V</code></pre>
494
- </section>
495
- <section id="使用示例" class="level2">
496
- <h2 class="anchored" data-anchor-id="使用示例">使用示例</h2>
497
- <section id="基础使用默认启用所有高级功能" class="level3">
498
- <h3 class="anchored" data-anchor-id="基础使用默认启用所有高级功能">基础使用(默认启用所有高级功能)</h3>
499
- <div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb12"><pre class="sourceCode typescript code-with-copy"><code class="sourceCode typescript"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> { LoggerSDK } <span class="im">from</span> <span class="st">'@pluve/logger-sdk'</span><span class="op">;</span></span>
500
- <span id="cb12-2"><a href="#cb12-2" aria-hidden="true" tabindex="-1"></a></span>
501
- <span id="cb12-3"><a href="#cb12-3" aria-hidden="true" tabindex="-1"></a><span class="kw">const</span> sdk <span class="op">=</span> <span class="kw">new</span> <span class="fu">LoggerSDK</span>({</span>
502
- <span id="cb12-4"><a href="#cb12-4" aria-hidden="true" tabindex="-1"></a> endpoint<span class="op">:</span> <span class="st">'https://your-api.com/logs'</span><span class="op">,</span></span>
503
- <span id="cb12-5"><a href="#cb12-5" aria-hidden="true" tabindex="-1"></a> appId<span class="op">:</span> <span class="st">'my-app'</span><span class="op">,</span></span>
504
- <span id="cb12-6"><a href="#cb12-6" aria-hidden="true" tabindex="-1"></a> env<span class="op">:</span> <span class="st">'prod'</span><span class="op">,</span></span>
505
- <span id="cb12-7"><a href="#cb12-7" aria-hidden="true" tabindex="-1"></a> debug<span class="op">:</span> <span class="kw">false</span><span class="op">,</span></span>
506
- <span id="cb12-8"><a href="#cb12-8" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span></span>
507
- <span id="cb12-9"><a href="#cb12-9" aria-hidden="true" tabindex="-1"></a></span>
508
- <span id="cb12-10"><a href="#cb12-10" aria-hidden="true" tabindex="-1"></a><span class="co">// 记录错误日志</span></span>
509
- <span id="cb12-11"><a href="#cb12-11" aria-hidden="true" tabindex="-1"></a>sdk<span class="op">.</span><span class="fu">track</span>(<span class="st">'error'</span><span class="op">,</span> <span class="st">'TypeError: Cannot read property'</span><span class="op">,</span> {</span>
510
- <span id="cb12-12"><a href="#cb12-12" aria-hidden="true" tabindex="-1"></a> level<span class="op">:</span> <span class="st">'error'</span><span class="op">,</span></span>
511
- <span id="cb12-13"><a href="#cb12-13" aria-hidden="true" tabindex="-1"></a> stack<span class="op">:</span> <span class="st">'Error stack trace...'</span><span class="op">,</span></span>
512
- <span id="cb12-14"><a href="#cb12-14" aria-hidden="true" tabindex="-1"></a> userId<span class="op">:</span> <span class="st">'123'</span><span class="op">,</span></span>
513
- <span id="cb12-15"><a href="#cb12-15" aria-hidden="true" tabindex="-1"></a> tags<span class="op">:</span> {</span>
514
- <span id="cb12-16"><a href="#cb12-16" aria-hidden="true" tabindex="-1"></a> component<span class="op">:</span> <span class="st">'checkout'</span><span class="op">,</span></span>
515
- <span id="cb12-17"><a href="#cb12-17" aria-hidden="true" tabindex="-1"></a> browser<span class="op">:</span> <span class="st">'Chrome 120'</span><span class="op">,</span></span>
516
- <span id="cb12-18"><a href="#cb12-18" aria-hidden="true" tabindex="-1"></a> }<span class="op">,</span></span>
517
- <span id="cb12-19"><a href="#cb12-19" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span></span></code></pre></div><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></div>
518
- </section>
519
- <section id="批量上报配置" class="level3">
520
- <h3 class="anchored" data-anchor-id="批量上报配置">批量上报配置</h3>
521
- <div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb13"><pre class="sourceCode typescript code-with-copy"><code class="sourceCode typescript"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true" tabindex="-1"></a><span class="kw">const</span> sdk <span class="op">=</span> <span class="kw">new</span> <span class="fu">LoggerSDK</span>({</span>
522
- <span id="cb13-2"><a href="#cb13-2" aria-hidden="true" tabindex="-1"></a> endpoint<span class="op">:</span> <span class="st">'https://your-api.com/logs'</span><span class="op">,</span></span>
523
- <span id="cb13-3"><a href="#cb13-3" aria-hidden="true" tabindex="-1"></a> appId<span class="op">:</span> <span class="st">'my-app'</span><span class="op">,</span></span>
524
- <span id="cb13-4"><a href="#cb13-4" aria-hidden="true" tabindex="-1"></a> </span>
525
- <span id="cb13-5"><a href="#cb13-5" aria-hidden="true" tabindex="-1"></a> <span class="co">// 批量上报配置</span></span>
526
- <span id="cb13-6"><a href="#cb13-6" aria-hidden="true" tabindex="-1"></a> enableBatch<span class="op">:</span> <span class="kw">true</span><span class="op">,</span> <span class="co">// 启用批量上报</span></span>
527
- <span id="cb13-7"><a href="#cb13-7" aria-hidden="true" tabindex="-1"></a> batchSize<span class="op">:</span> <span class="dv">20</span><span class="op">,</span> <span class="co">// 队列达到20条时批量上报</span></span>
528
- <span id="cb13-8"><a href="#cb13-8" aria-hidden="true" tabindex="-1"></a> batchInterval<span class="op">:</span> <span class="dv">10000</span><span class="op">,</span> <span class="co">// 或每隔10秒上报一次</span></span>
529
- <span id="cb13-9"><a href="#cb13-9" aria-hidden="true" tabindex="-1"></a> maxQueueSize<span class="op">:</span> <span class="dv">100</span><span class="op">,</span> <span class="co">// 队列最大100条日志</span></span>
530
- <span id="cb13-10"><a href="#cb13-10" aria-hidden="true" tabindex="-1"></a> </span>
531
- <span id="cb13-11"><a href="#cb13-11" aria-hidden="true" tabindex="-1"></a> <span class="co">// 持久化存储配置</span></span>
532
- <span id="cb13-12"><a href="#cb13-12" aria-hidden="true" tabindex="-1"></a> enableStorage<span class="op">:</span> <span class="kw">true</span><span class="op">,</span> <span class="co">// 启用持久化存储</span></span>
533
- <span id="cb13-13"><a href="#cb13-13" aria-hidden="true" tabindex="-1"></a> storagePrefix<span class="op">:</span> <span class="st">'my_app_logger'</span><span class="op">,</span></span>
534
- <span id="cb13-14"><a href="#cb13-14" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span></span></code></pre></div><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></div>
535
- </section>
536
- <section id="重试机制配置" class="level3">
537
- <h3 class="anchored" data-anchor-id="重试机制配置">重试机制配置</h3>
538
- <div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb14"><pre class="sourceCode typescript code-with-copy"><code class="sourceCode typescript"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true" tabindex="-1"></a><span class="kw">const</span> sdk <span class="op">=</span> <span class="kw">new</span> <span class="fu">LoggerSDK</span>({</span>
539
- <span id="cb14-2"><a href="#cb14-2" aria-hidden="true" tabindex="-1"></a> endpoint<span class="op">:</span> <span class="st">'https://your-api.com/logs'</span><span class="op">,</span></span>
540
- <span id="cb14-3"><a href="#cb14-3" aria-hidden="true" tabindex="-1"></a> appId<span class="op">:</span> <span class="st">'my-app'</span><span class="op">,</span></span>
541
- <span id="cb14-4"><a href="#cb14-4" aria-hidden="true" tabindex="-1"></a> </span>
542
- <span id="cb14-5"><a href="#cb14-5" aria-hidden="true" tabindex="-1"></a> <span class="co">// 重试配置</span></span>
543
- <span id="cb14-6"><a href="#cb14-6" aria-hidden="true" tabindex="-1"></a> enableRetry<span class="op">:</span> <span class="kw">true</span><span class="op">,</span> <span class="co">// 启用重试机制</span></span>
544
- <span id="cb14-7"><a href="#cb14-7" aria-hidden="true" tabindex="-1"></a> maxRetries<span class="op">:</span> <span class="dv">5</span><span class="op">,</span> <span class="co">// 最多重试5次</span></span>
545
- <span id="cb14-8"><a href="#cb14-8" aria-hidden="true" tabindex="-1"></a> retryDelay<span class="op">:</span> <span class="dv">2000</span><span class="op">,</span> <span class="co">// 基础延迟2秒</span></span>
546
- <span id="cb14-9"><a href="#cb14-9" aria-hidden="true" tabindex="-1"></a> retryBackoff<span class="op">:</span> <span class="kw">true</span><span class="op">,</span> <span class="co">// 启用指数退避</span></span>
547
- <span id="cb14-10"><a href="#cb14-10" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span></span></code></pre></div><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></div>
548
- </section>
549
- <section id="手动刷新队列" class="level3">
550
- <h3 class="anchored" data-anchor-id="手动刷新队列">手动刷新队列</h3>
551
- <div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb15"><pre class="sourceCode typescript code-with-copy"><code class="sourceCode typescript"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true" tabindex="-1"></a><span class="co">// 立即上报所有待发送日志</span></span>
552
- <span id="cb15-2"><a href="#cb15-2" aria-hidden="true" tabindex="-1"></a><span class="cf">await</span> sdk<span class="op">.</span><span class="fu">flush</span>()<span class="op">;</span></span></code></pre></div><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></div>
553
- </section>
554
- <section id="禁用批量上报实时上报" class="level3">
555
- <h3 class="anchored" data-anchor-id="禁用批量上报实时上报">禁用批量上报(实时上报)</h3>
556
- <div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb16"><pre class="sourceCode typescript code-with-copy"><code class="sourceCode typescript"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true" tabindex="-1"></a><span class="kw">const</span> sdk <span class="op">=</span> LoggerSDK<span class="op">.</span><span class="fu">getInstance</span>()<span class="op">;</span></span>
557
- <span id="cb16-2"><a href="#cb16-2" aria-hidden="true" tabindex="-1"></a>sdk<span class="op">.</span><span class="fu">init</span>({</span>
558
- <span id="cb16-3"><a href="#cb16-3" aria-hidden="true" tabindex="-1"></a> endpoint<span class="op">:</span> <span class="st">'https://your-api.com/logs'</span><span class="op">,</span></span>
559
- <span id="cb16-4"><a href="#cb16-4" aria-hidden="true" tabindex="-1"></a> appId<span class="op">:</span> <span class="st">'my-app'</span><span class="op">,</span></span>
560
- <span id="cb16-5"><a href="#cb16-5" aria-hidden="true" tabindex="-1"></a> enableBatch<span class="op">:</span> <span class="kw">false</span><span class="op">,</span> <span class="co">// 默认即为 false;每条日志立即发送</span></span>
561
- <span id="cb16-6"><a href="#cb16-6" aria-hidden="true" tabindex="-1"></a> enableRetry<span class="op">:</span> <span class="kw">true</span><span class="op">,</span></span>
562
- <span id="cb16-7"><a href="#cb16-7" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span></span></code></pre></div><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></div>
563
- </section>
564
- </section>
565
- <section id="重要说明" class="level2">
566
- <h2 class="anchored" data-anchor-id="重要说明">重要说明</h2>
567
- <ul>
568
- <li><code>init()</code> 会自动上报 <code>session_start</code>,携带环境信息标签(仅此一次)</li>
569
- <li>普通事件默认不合并环境标签,建议仅在必要时通过 <code>tags</code> 传入额外信息</li>
570
- <li>推荐使用 <code>LoggerSDK.getInstance()</code> 获取单例,避免多实例重复初始化</li>
571
- </ul>
572
- </section>
573
- </section>
574
-
575
- </main>
576
- <!-- /main column -->
577
- <script id="quarto-html-after-body" type="application/javascript">
578
- window.document.addEventListener("DOMContentLoaded", function (event) {
579
- const icon = "";
580
- const anchorJS = new window.AnchorJS();
581
- anchorJS.options = {
582
- placement: 'right',
583
- icon: icon
584
- };
585
- anchorJS.add('.anchored');
586
- const isCodeAnnotation = (el) => {
587
- for (const clz of el.classList) {
588
- if (clz.startsWith('code-annotation-')) {
589
- return true;
590
- }
591
- }
592
- return false;
593
- }
594
- const onCopySuccess = function(e) {
595
- // button target
596
- const button = e.trigger;
597
- // don't keep focus
598
- button.blur();
599
- // flash "checked"
600
- button.classList.add('code-copy-button-checked');
601
- var currentTitle = button.getAttribute("title");
602
- button.setAttribute("title", "Copied!");
603
- let tooltip;
604
- if (window.bootstrap) {
605
- button.setAttribute("data-bs-toggle", "tooltip");
606
- button.setAttribute("data-bs-placement", "left");
607
- button.setAttribute("data-bs-title", "Copied!");
608
- tooltip = new bootstrap.Tooltip(button,
609
- { trigger: "manual",
610
- customClass: "code-copy-button-tooltip",
611
- offset: [0, -8]});
612
- tooltip.show();
613
- }
614
- setTimeout(function() {
615
- if (tooltip) {
616
- tooltip.hide();
617
- button.removeAttribute("data-bs-title");
618
- button.removeAttribute("data-bs-toggle");
619
- button.removeAttribute("data-bs-placement");
620
- }
621
- button.setAttribute("title", currentTitle);
622
- button.classList.remove('code-copy-button-checked');
623
- }, 1000);
624
- // clear code selection
625
- e.clearSelection();
626
- }
627
- const getTextToCopy = function(trigger) {
628
- const outerScaffold = trigger.parentElement.cloneNode(true);
629
- const codeEl = outerScaffold.querySelector('code');
630
- for (const childEl of codeEl.children) {
631
- if (isCodeAnnotation(childEl)) {
632
- childEl.remove();
633
- }
634
- }
635
- return codeEl.innerText;
636
- }
637
- const clipboard = new window.ClipboardJS('.code-copy-button:not([data-in-quarto-modal])', {
638
- text: getTextToCopy
639
- });
640
- clipboard.on('success', onCopySuccess);
641
- if (window.document.getElementById('quarto-embedded-source-code-modal')) {
642
- const clipboardModal = new window.ClipboardJS('.code-copy-button[data-in-quarto-modal]', {
643
- text: getTextToCopy,
644
- container: window.document.getElementById('quarto-embedded-source-code-modal')
645
- });
646
- clipboardModal.on('success', onCopySuccess);
647
- }
648
- var localhostRegex = new RegExp(/^(?:http|https):\/\/localhost\:?[0-9]*\//);
649
- var mailtoRegex = new RegExp(/^mailto:/);
650
- var filterRegex = new RegExp('/' + window.location.host + '/');
651
- var isInternal = (href) => {
652
- return filterRegex.test(href) || localhostRegex.test(href) || mailtoRegex.test(href);
653
- }
654
- // Inspect non-navigation links and adorn them if external
655
- var links = window.document.querySelectorAll('a[href]:not(.nav-link):not(.navbar-brand):not(.toc-action):not(.sidebar-link):not(.sidebar-item-toggle):not(.pagination-link):not(.no-external):not([aria-hidden]):not(.dropdown-item):not(.quarto-navigation-tool):not(.about-link)');
656
- for (var i=0; i<links.length; i++) {
657
- const link = links[i];
658
- if (!isInternal(link.href)) {
659
- // undo the damage that might have been done by quarto-nav.js in the case of
660
- // links that we want to consider external
661
- if (link.dataset.originalHref !== undefined) {
662
- link.href = link.dataset.originalHref;
663
- }
664
- }
665
- }
666
- function tippyHover(el, contentFn, onTriggerFn, onUntriggerFn) {
667
- const config = {
668
- allowHTML: true,
669
- maxWidth: 500,
670
- delay: 100,
671
- arrow: false,
672
- appendTo: function(el) {
673
- return el.parentElement;
674
- },
675
- interactive: true,
676
- interactiveBorder: 10,
677
- theme: 'quarto',
678
- placement: 'bottom-start',
679
- };
680
- if (contentFn) {
681
- config.content = contentFn;
682
- }
683
- if (onTriggerFn) {
684
- config.onTrigger = onTriggerFn;
685
- }
686
- if (onUntriggerFn) {
687
- config.onUntrigger = onUntriggerFn;
688
- }
689
- window.tippy(el, config);
690
- }
691
- const noterefs = window.document.querySelectorAll('a[role="doc-noteref"]');
692
- for (var i=0; i<noterefs.length; i++) {
693
- const ref = noterefs[i];
694
- tippyHover(ref, function() {
695
- // use id or data attribute instead here
696
- let href = ref.getAttribute('data-footnote-href') || ref.getAttribute('href');
697
- try { href = new URL(href).hash; } catch {}
698
- const id = href.replace(/^#\/?/, "");
699
- const note = window.document.getElementById(id);
700
- if (note) {
701
- return note.innerHTML;
702
- } else {
703
- return "";
704
- }
705
- });
706
- }
707
- const xrefs = window.document.querySelectorAll('a.quarto-xref');
708
- const processXRef = (id, note) => {
709
- // Strip column container classes
710
- const stripColumnClz = (el) => {
711
- el.classList.remove("page-full", "page-columns");
712
- if (el.children) {
713
- for (const child of el.children) {
714
- stripColumnClz(child);
715
- }
716
- }
717
- }
718
- stripColumnClz(note)
719
- if (id === null || id.startsWith('sec-')) {
720
- // Special case sections, only their first couple elements
721
- const container = document.createElement("div");
722
- if (note.children && note.children.length > 2) {
723
- container.appendChild(note.children[0].cloneNode(true));
724
- for (let i = 1; i < note.children.length; i++) {
725
- const child = note.children[i];
726
- if (child.tagName === "P" && child.innerText === "") {
727
- continue;
728
- } else {
729
- container.appendChild(child.cloneNode(true));
730
- break;
731
- }
732
- }
733
- if (window.Quarto?.typesetMath) {
734
- window.Quarto.typesetMath(container);
735
- }
736
- return container.innerHTML
737
- } else {
738
- if (window.Quarto?.typesetMath) {
739
- window.Quarto.typesetMath(note);
740
- }
741
- return note.innerHTML;
742
- }
743
- } else {
744
- // Remove any anchor links if they are present
745
- const anchorLink = note.querySelector('a.anchorjs-link');
746
- if (anchorLink) {
747
- anchorLink.remove();
748
- }
749
- if (window.Quarto?.typesetMath) {
750
- window.Quarto.typesetMath(note);
751
- }
752
- if (note.classList.contains("callout")) {
753
- return note.outerHTML;
754
- } else {
755
- return note.innerHTML;
756
- }
757
- }
758
- }
759
- for (var i=0; i<xrefs.length; i++) {
760
- const xref = xrefs[i];
761
- tippyHover(xref, undefined, function(instance) {
762
- instance.disable();
763
- let url = xref.getAttribute('href');
764
- let hash = undefined;
765
- if (url.startsWith('#')) {
766
- hash = url;
767
- } else {
768
- try { hash = new URL(url).hash; } catch {}
769
- }
770
- if (hash) {
771
- const id = hash.replace(/^#\/?/, "");
772
- const note = window.document.getElementById(id);
773
- if (note !== null) {
774
- try {
775
- const html = processXRef(id, note.cloneNode(true));
776
- instance.setContent(html);
777
- } finally {
778
- instance.enable();
779
- instance.show();
780
- }
781
- } else {
782
- // See if we can fetch this
783
- fetch(url.split('#')[0])
784
- .then(res => res.text())
785
- .then(html => {
786
- const parser = new DOMParser();
787
- const htmlDoc = parser.parseFromString(html, "text/html");
788
- const note = htmlDoc.getElementById(id);
789
- if (note !== null) {
790
- const html = processXRef(id, note);
791
- instance.setContent(html);
792
- }
793
- }).finally(() => {
794
- instance.enable();
795
- instance.show();
796
- });
797
- }
798
- } else {
799
- // See if we can fetch a full url (with no hash to target)
800
- // This is a special case and we should probably do some content thinning / targeting
801
- fetch(url)
802
- .then(res => res.text())
803
- .then(html => {
804
- const parser = new DOMParser();
805
- const htmlDoc = parser.parseFromString(html, "text/html");
806
- const note = htmlDoc.querySelector('main.content');
807
- if (note !== null) {
808
- // This should only happen for chapter cross references
809
- // (since there is no id in the URL)
810
- // remove the first header
811
- if (note.children.length > 0 && note.children[0].tagName === "HEADER") {
812
- note.children[0].remove();
813
- }
814
- const html = processXRef(null, note);
815
- instance.setContent(html);
816
- }
817
- }).finally(() => {
818
- instance.enable();
819
- instance.show();
820
- });
821
- }
822
- }, function(instance) {
823
- });
824
- }
825
- let selectedAnnoteEl;
826
- const selectorForAnnotation = ( cell, annotation) => {
827
- let cellAttr = 'data-code-cell="' + cell + '"';
828
- let lineAttr = 'data-code-annotation="' + annotation + '"';
829
- const selector = 'span[' + cellAttr + '][' + lineAttr + ']';
830
- return selector;
831
- }
832
- const selectCodeLines = (annoteEl) => {
833
- const doc = window.document;
834
- const targetCell = annoteEl.getAttribute("data-target-cell");
835
- const targetAnnotation = annoteEl.getAttribute("data-target-annotation");
836
- const annoteSpan = window.document.querySelector(selectorForAnnotation(targetCell, targetAnnotation));
837
- const lines = annoteSpan.getAttribute("data-code-lines").split(",");
838
- const lineIds = lines.map((line) => {
839
- return targetCell + "-" + line;
840
- })
841
- let top = null;
842
- let height = null;
843
- let parent = null;
844
- if (lineIds.length > 0) {
845
- //compute the position of the single el (top and bottom and make a div)
846
- const el = window.document.getElementById(lineIds[0]);
847
- top = el.offsetTop;
848
- height = el.offsetHeight;
849
- parent = el.parentElement.parentElement;
850
- if (lineIds.length > 1) {
851
- const lastEl = window.document.getElementById(lineIds[lineIds.length - 1]);
852
- const bottom = lastEl.offsetTop + lastEl.offsetHeight;
853
- height = bottom - top;
854
- }
855
- if (top !== null && height !== null && parent !== null) {
856
- // cook up a div (if necessary) and position it
857
- let div = window.document.getElementById("code-annotation-line-highlight");
858
- if (div === null) {
859
- div = window.document.createElement("div");
860
- div.setAttribute("id", "code-annotation-line-highlight");
861
- div.style.position = 'absolute';
862
- parent.appendChild(div);
863
- }
864
- div.style.top = top - 2 + "px";
865
- div.style.height = height + 4 + "px";
866
- div.style.left = 0;
867
- let gutterDiv = window.document.getElementById("code-annotation-line-highlight-gutter");
868
- if (gutterDiv === null) {
869
- gutterDiv = window.document.createElement("div");
870
- gutterDiv.setAttribute("id", "code-annotation-line-highlight-gutter");
871
- gutterDiv.style.position = 'absolute';
872
- const codeCell = window.document.getElementById(targetCell);
873
- const gutter = codeCell.querySelector('.code-annotation-gutter');
874
- gutter.appendChild(gutterDiv);
875
- }
876
- gutterDiv.style.top = top - 2 + "px";
877
- gutterDiv.style.height = height + 4 + "px";
878
- }
879
- selectedAnnoteEl = annoteEl;
880
- }
881
- };
882
- const unselectCodeLines = () => {
883
- const elementsIds = ["code-annotation-line-highlight", "code-annotation-line-highlight-gutter"];
884
- elementsIds.forEach((elId) => {
885
- const div = window.document.getElementById(elId);
886
- if (div) {
887
- div.remove();
888
- }
889
- });
890
- selectedAnnoteEl = undefined;
891
- };
892
- // Handle positioning of the toggle
893
- window.addEventListener(
894
- "resize",
895
- throttle(() => {
896
- elRect = undefined;
897
- if (selectedAnnoteEl) {
898
- selectCodeLines(selectedAnnoteEl);
899
- }
900
- }, 10)
901
- );
902
- function throttle(fn, ms) {
903
- let throttle = false;
904
- let timer;
905
- return (...args) => {
906
- if(!throttle) { // first call gets through
907
- fn.apply(this, args);
908
- throttle = true;
909
- } else { // all the others get throttled
910
- if(timer) clearTimeout(timer); // cancel #2
911
- timer = setTimeout(() => {
912
- fn.apply(this, args);
913
- timer = throttle = false;
914
- }, ms);
915
- }
916
- };
917
- }
918
- // Attach click handler to the DT
919
- const annoteDls = window.document.querySelectorAll('dt[data-target-cell]');
920
- for (const annoteDlNode of annoteDls) {
921
- annoteDlNode.addEventListener('click', (event) => {
922
- const clickedEl = event.target;
923
- if (clickedEl !== selectedAnnoteEl) {
924
- unselectCodeLines();
925
- const activeEl = window.document.querySelector('dt[data-target-cell].code-annotation-active');
926
- if (activeEl) {
927
- activeEl.classList.remove('code-annotation-active');
928
- }
929
- selectCodeLines(clickedEl);
930
- clickedEl.classList.add('code-annotation-active');
931
- } else {
932
- // Unselect the line
933
- unselectCodeLines();
934
- clickedEl.classList.remove('code-annotation-active');
935
- }
936
- });
937
- }
938
- const findCites = (el) => {
939
- const parentEl = el.parentElement;
940
- if (parentEl) {
941
- const cites = parentEl.dataset.cites;
942
- if (cites) {
943
- return {
944
- el,
945
- cites: cites.split(' ')
946
- };
947
- } else {
948
- return findCites(el.parentElement)
949
- }
950
- } else {
951
- return undefined;
952
- }
953
- };
954
- var bibliorefs = window.document.querySelectorAll('a[role="doc-biblioref"]');
955
- for (var i=0; i<bibliorefs.length; i++) {
956
- const ref = bibliorefs[i];
957
- const citeInfo = findCites(ref);
958
- if (citeInfo) {
959
- tippyHover(citeInfo.el, function() {
960
- var popup = window.document.createElement('div');
961
- citeInfo.cites.forEach(function(cite) {
962
- var citeDiv = window.document.createElement('div');
963
- citeDiv.classList.add('hanging-indent');
964
- citeDiv.classList.add('csl-entry');
965
- var biblioDiv = window.document.getElementById('ref-' + cite);
966
- if (biblioDiv) {
967
- citeDiv.innerHTML = biblioDiv.innerHTML;
968
- }
969
- popup.appendChild(citeDiv);
970
- });
971
- return popup.innerHTML;
972
- });
973
- }
974
- }
975
- });
976
- </script>
977
- </div> <!-- /content -->
978
-
979
-
980
-
981
-
982
- </body></html>