@hd-front-end/jsbridge-sdk 1.0.2 → 1.0.4
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 +4 -2
- package/dist/index.d.ts +201 -2
- package/dist/index.esm.js +444 -1
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +446 -0
- package/dist/index.js.map +1 -1
- package/dist/index.umd.js +446 -0
- package/dist/index.umd.js.map +1 -1
- package/docs/00-/351/241/271/347/233/256/346/246/202/350/247/210.md +282 -0
- package/docs/01-/346/236/266/346/236/204/350/256/276/350/256/241.md +623 -0
- package/docs/02-/346/212/200/346/234/257/345/256/236/347/216/260.md +867 -0
- package/docs/03-API/344/275/277/347/224/250/346/226/207/346/241/243.md +1104 -0
- package/docs/04-/346/265/213/350/257/225/346/226/271/346/241/210.md +360 -0
- package/docs/05-/350/277/201/347/247/273/346/214/207/345/215/227.md +181 -0
- package/docs/06-/346/236/266/346/236/204/345/233/276/351/233/206.md +738 -0
- package/docs/07-/346/226/260/346/241/245/346/216/245/346/226/271/346/263/225/346/211/251/345/261/225/350/257/264/346/230/216.md +139 -0
- package/docs/CODE_REVIEW.md +65 -0
- package/docs/EVALUATION.md +72 -0
- package/docs/README.md +258 -0
- package/docs//345/205/263/351/224/256/351/227/256/351/242/230/350/247/243/347/255/224.md +495 -0
- package/docs//346/226/207/346/241/243/346/225/264/345/220/210/350/257/264/346/230/216.md +265 -0
- package/docs//346/233/264/346/226/260/346/227/245/345/277/227.md +669 -0
- package/docs//347/224/237/344/272/247/347/272/247-/345/277/253/351/200/237/345/274/200/345/247/213-v2.md +673 -0
- package/docs//347/224/237/344/272/247/347/272/247-/346/236/266/346/236/204/350/256/276/350/256/241-v2.md +730 -0
- package/docs//350/256/276/350/256/241/347/220/206/345/277/265/350/257/264/346/230/216.md +438 -0
- package/package.json +3 -2
|
@@ -0,0 +1,730 @@
|
|
|
1
|
+
# JSBridge SDK 生产级架构设计 v2
|
|
2
|
+
|
|
3
|
+
> **核心理念**:SDK 聚焦于 bridge API,通过原生 log 能力实现监控和日志上报
|
|
4
|
+
|
|
5
|
+
## 🎯 设计原则
|
|
6
|
+
|
|
7
|
+
### SDK 的核心职责
|
|
8
|
+
|
|
9
|
+
✅ **必须做的**:
|
|
10
|
+
1. 封装原生 WebView 通信(核心)
|
|
11
|
+
2. **兼容 Android 和 iOS**(必需)
|
|
12
|
+
3. **通过原生 log 能力实现监控**(生产必需)
|
|
13
|
+
4. **提供 log API 给子应用**(功能必需)
|
|
14
|
+
5. **提供 debug 模式**(开发必需)
|
|
15
|
+
6. 完整的类型定义
|
|
16
|
+
|
|
17
|
+
❌ **不做的**:
|
|
18
|
+
1. uni API 适配层(子应用决定)
|
|
19
|
+
2. 框架特定封装(Vue/React)
|
|
20
|
+
3. 业务逻辑
|
|
21
|
+
|
|
22
|
+
## 🏗️ 架构设计
|
|
23
|
+
|
|
24
|
+
### 1. 整体架构
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
┌─────────────────────────────────────┐
|
|
28
|
+
│ 子应用 │
|
|
29
|
+
│ - 直接使用 SDK API │
|
|
30
|
+
│ - 可选封装适配层 │
|
|
31
|
+
│ - 调用 log API 上传业务日志 │
|
|
32
|
+
└─────────────────────────────────────┘
|
|
33
|
+
↓
|
|
34
|
+
┌─────────────────────────────────────┐
|
|
35
|
+
│ @hd-front-end/jsbridge-sdk │
|
|
36
|
+
│ │
|
|
37
|
+
│ ├── 核心通信层(Bridge) │
|
|
38
|
+
│ │ ├── Android 兼容 │
|
|
39
|
+
│ │ └── iOS 兼容 │
|
|
40
|
+
│ │ │
|
|
41
|
+
│ ├── API 层 │
|
|
42
|
+
│ │ ├── device.ts │
|
|
43
|
+
│ │ ├── media.ts │
|
|
44
|
+
│ │ ├── log.ts ← 新增 │
|
|
45
|
+
│ │ └── ... │
|
|
46
|
+
│ │ │
|
|
47
|
+
│ ├── 监控层(Monitor) │
|
|
48
|
+
│ │ ├── 性能监控 │
|
|
49
|
+
│ │ ├── 错误监控 │
|
|
50
|
+
│ │ ├── 调用统计 │
|
|
51
|
+
│ │ └── 通过原生 log 上报 ← 修改 │
|
|
52
|
+
│ │ │
|
|
53
|
+
│ └── 调试层(Debug) │
|
|
54
|
+
│ ├── vconsole 集成 │
|
|
55
|
+
│ ├── 日志输出 │
|
|
56
|
+
│ └── 调用追踪 │
|
|
57
|
+
└─────────────────────────────────────┘
|
|
58
|
+
↓
|
|
59
|
+
┌─────────────────────────────────────┐
|
|
60
|
+
│ 原生 WebView │
|
|
61
|
+
│ ├── Android (InjectJavascript) │
|
|
62
|
+
│ │ └── LogUtil (SLF4J+Logback) │
|
|
63
|
+
│ │ └── 写入日志文件 │
|
|
64
|
+
│ │ │
|
|
65
|
+
│ └── iOS (WKWebView) │
|
|
66
|
+
│ └── LogUtil (OSLog) │
|
|
67
|
+
│ └── 写入日志文件 │
|
|
68
|
+
└─────────────────────────────────────┘
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## 💻 核心实现
|
|
72
|
+
|
|
73
|
+
### 1. Log API(新增)
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
// src/api/log.ts
|
|
77
|
+
import { bridge } from '../core/Bridge'
|
|
78
|
+
import { monitor } from '../monitor/Monitor'
|
|
79
|
+
import { debug } from '../debug/Debug'
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* 日志级别
|
|
83
|
+
*/
|
|
84
|
+
export enum LogLevel {
|
|
85
|
+
DEBUG = 'DEBUG',
|
|
86
|
+
INFO = 'INFO',
|
|
87
|
+
WARN = 'WARN',
|
|
88
|
+
ERROR = 'ERROR'
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* 日志请求参数
|
|
93
|
+
*/
|
|
94
|
+
export interface LogRequest {
|
|
95
|
+
tag?: string // TAG 标识(可选)
|
|
96
|
+
level: LogLevel // 日志等级
|
|
97
|
+
message: string // 日志内容
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* 写日志到原生
|
|
102
|
+
* 日志会通过原生的 LogUtil 写入文件
|
|
103
|
+
*/
|
|
104
|
+
export async function writeLog(request: LogRequest): Promise<void> {
|
|
105
|
+
// 记录监控
|
|
106
|
+
const record = monitor.recordStart('log', bridge.getPlatform(), request)
|
|
107
|
+
|
|
108
|
+
try {
|
|
109
|
+
// 调用原生 log handler
|
|
110
|
+
await bridge.call('log', {
|
|
111
|
+
tag: request.tag || 'JSBridge',
|
|
112
|
+
level: request.level,
|
|
113
|
+
message: request.message
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
// 记录成功
|
|
117
|
+
monitor.recordEnd(record, true)
|
|
118
|
+
debug.log('debug', `日志已写入: [${request.level}] ${request.message}`)
|
|
119
|
+
|
|
120
|
+
} catch (error) {
|
|
121
|
+
// 记录失败
|
|
122
|
+
monitor.recordEnd(record, false, undefined, (error as Error).message)
|
|
123
|
+
debug.log('error', `写入日志失败: ${(error as Error).message}`)
|
|
124
|
+
throw error
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* 便捷方法
|
|
130
|
+
*/
|
|
131
|
+
export const log = {
|
|
132
|
+
debug: (message: string, tag?: string) =>
|
|
133
|
+
writeLog({ level: LogLevel.DEBUG, message, tag }),
|
|
134
|
+
|
|
135
|
+
info: (message: string, tag?: string) =>
|
|
136
|
+
writeLog({ level: LogLevel.INFO, message, tag }),
|
|
137
|
+
|
|
138
|
+
warn: (message: string, tag?: string) =>
|
|
139
|
+
writeLog({ level: LogLevel.WARN, message, tag }),
|
|
140
|
+
|
|
141
|
+
error: (message: string, tag?: string) =>
|
|
142
|
+
writeLog({ level: LogLevel.ERROR, message, tag })
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### 2. 监控层(改为通过 log 上报)
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
// src/monitor/Monitor.ts
|
|
150
|
+
/**
|
|
151
|
+
* 监控配置
|
|
152
|
+
*/
|
|
153
|
+
export interface MonitorConfig {
|
|
154
|
+
enabled: boolean
|
|
155
|
+
autoReport?: boolean // 自动上报
|
|
156
|
+
reportInterval?: number // 上报间隔(ms)
|
|
157
|
+
maxCacheSize?: number // 最大缓存条数
|
|
158
|
+
logTag?: string // 日志 TAG,默认 'JSBridge-Monitor'
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* 监控类
|
|
163
|
+
* 通过原生 log 能力上报监控数据
|
|
164
|
+
*/
|
|
165
|
+
class Monitor {
|
|
166
|
+
private config: MonitorConfig
|
|
167
|
+
private records: ApiCallRecord[] = []
|
|
168
|
+
private stats: Map<string, PerformanceStats> = new Map()
|
|
169
|
+
private reportTimer?: NodeJS.Timeout
|
|
170
|
+
|
|
171
|
+
constructor(config: Partial<MonitorConfig> = {}) {
|
|
172
|
+
this.config = {
|
|
173
|
+
enabled: true,
|
|
174
|
+
reportInterval: 60000, // 默认 1 分钟
|
|
175
|
+
maxCacheSize: 100, // 默认缓存 100 条
|
|
176
|
+
autoReport: false, // 默认不自动上报
|
|
177
|
+
logTag: 'JSBridge-Monitor',
|
|
178
|
+
...config
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (this.config.autoReport) {
|
|
182
|
+
this.startAutoReport()
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* 上报监控数据
|
|
188
|
+
* 通过原生 log 能力写入日志文件
|
|
189
|
+
*/
|
|
190
|
+
async report(): Promise<void> {
|
|
191
|
+
if (this.records.length === 0) {
|
|
192
|
+
return
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
try {
|
|
196
|
+
const data = {
|
|
197
|
+
records: this.records,
|
|
198
|
+
stats: Array.from(this.stats.entries()).map(([api, stats]) => ({
|
|
199
|
+
api,
|
|
200
|
+
...stats
|
|
201
|
+
})),
|
|
202
|
+
timestamp: Date.now()
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// 通过原生 log 能力上报
|
|
206
|
+
await bridge.call('log', {
|
|
207
|
+
tag: this.config.logTag,
|
|
208
|
+
level: 'INFO',
|
|
209
|
+
message: `[MONITOR] ${JSON.stringify(data)}`
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
console.log('[Monitor] 监控数据已上报到原生日志')
|
|
213
|
+
this.clear()
|
|
214
|
+
} catch (error) {
|
|
215
|
+
console.error('[Monitor] 上报失败', error)
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* 上报错误信息
|
|
221
|
+
* 立即上报,不等待定时任务
|
|
222
|
+
*/
|
|
223
|
+
async reportError(api: string, error: string, detail?: any): Promise<void> {
|
|
224
|
+
if (!this.config.enabled) {
|
|
225
|
+
return
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
try {
|
|
229
|
+
const errorData = {
|
|
230
|
+
type: 'error',
|
|
231
|
+
api,
|
|
232
|
+
error,
|
|
233
|
+
detail,
|
|
234
|
+
platform: bridge.getPlatform(),
|
|
235
|
+
timestamp: Date.now()
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// 立即通过原生 log 能力上报错误
|
|
239
|
+
await bridge.call('log', {
|
|
240
|
+
tag: this.config.logTag,
|
|
241
|
+
level: 'ERROR',
|
|
242
|
+
message: `[ERROR] ${JSON.stringify(errorData)}`
|
|
243
|
+
})
|
|
244
|
+
|
|
245
|
+
console.error('[Monitor] 错误已上报:', errorData)
|
|
246
|
+
} catch (e) {
|
|
247
|
+
console.error('[Monitor] 上报错误失败', e)
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// ... 其他方法保持不变
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
export const monitor = new Monitor()
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### 3. Bridge 层集成 Log
|
|
258
|
+
|
|
259
|
+
```typescript
|
|
260
|
+
// src/core/Bridge.ts
|
|
261
|
+
class Bridge {
|
|
262
|
+
// ... 其他代码
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* 调用原生方法(增强版)
|
|
266
|
+
*/
|
|
267
|
+
async call<T = any>(method: string, data?: any): Promise<T> {
|
|
268
|
+
if (this.platform === Platform.Unknown) {
|
|
269
|
+
return Promise.reject(new Error('不在原生 App 环境中'))
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (!this.isInitialized) {
|
|
273
|
+
return Promise.reject(new Error('JSBridge 未初始化'))
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Debug: 追踪调用
|
|
277
|
+
debug.trace(method, data)
|
|
278
|
+
|
|
279
|
+
// Monitor: 开始记录
|
|
280
|
+
const record = monitor.recordStart(method, this.platform, data)
|
|
281
|
+
|
|
282
|
+
console.log(`[JSBridge] [${this.platform}] 调用: ${method}`, data)
|
|
283
|
+
|
|
284
|
+
return new Promise((resolve, reject) => {
|
|
285
|
+
try {
|
|
286
|
+
this.bridge.callHandler(method, data, (result: any) => {
|
|
287
|
+
console.log(`[JSBridge] [${this.platform}] 返回: ${method}`, result)
|
|
288
|
+
|
|
289
|
+
// Monitor: 记录成功
|
|
290
|
+
monitor.recordEnd(record, true, result)
|
|
291
|
+
|
|
292
|
+
// Debug: 记录日志
|
|
293
|
+
debug.log('info', `API 调用成功: ${method}`, { params: data, result })
|
|
294
|
+
|
|
295
|
+
resolve(result)
|
|
296
|
+
})
|
|
297
|
+
} catch (error) {
|
|
298
|
+
console.error(`[JSBridge] [${this.platform}] 调用失败: ${method}`, error)
|
|
299
|
+
|
|
300
|
+
// Monitor: 记录失败
|
|
301
|
+
monitor.recordEnd(record, false, undefined, (error as Error).message)
|
|
302
|
+
|
|
303
|
+
// Monitor: 立即上报错误(通过 log)
|
|
304
|
+
monitor.reportError(method, (error as Error).message, { params: data })
|
|
305
|
+
|
|
306
|
+
// Debug: 记录错误
|
|
307
|
+
debug.log('error', `API 调用失败: ${method}`, { params: data, error })
|
|
308
|
+
|
|
309
|
+
reject(error)
|
|
310
|
+
}
|
|
311
|
+
})
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
### 4. 统一导出
|
|
317
|
+
|
|
318
|
+
```typescript
|
|
319
|
+
// src/index.ts
|
|
320
|
+
import { bridge, Platform } from './core/Bridge'
|
|
321
|
+
import { monitor, MonitorConfig } from './monitor/Monitor'
|
|
322
|
+
import { debug, DebugConfig } from './debug/Debug'
|
|
323
|
+
import * as device from './api/device'
|
|
324
|
+
import * as media from './api/media'
|
|
325
|
+
import * as router from './api/router'
|
|
326
|
+
import * as storage from './api/storage'
|
|
327
|
+
import * as system from './api/system'
|
|
328
|
+
import { writeLog, log, LogLevel, LogRequest } from './api/log' // 新增
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* SDK 配置
|
|
332
|
+
*/
|
|
333
|
+
export interface JSBridgeConfig {
|
|
334
|
+
debug?: Partial<DebugConfig>
|
|
335
|
+
monitor?: Partial<MonitorConfig>
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* 初始化 JSBridge
|
|
340
|
+
*/
|
|
341
|
+
export async function init(config?: JSBridgeConfig): Promise<boolean> {
|
|
342
|
+
try {
|
|
343
|
+
// 配置 debug
|
|
344
|
+
if (config?.debug) {
|
|
345
|
+
debug.updateConfig(config.debug)
|
|
346
|
+
if (config.debug.useVConsole) {
|
|
347
|
+
await debug.initVConsole()
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// 配置 monitor
|
|
352
|
+
if (config?.monitor) {
|
|
353
|
+
monitor.updateConfig(config.monitor)
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// 初始化 bridge
|
|
357
|
+
const success = await bridge.init()
|
|
358
|
+
|
|
359
|
+
// 保存平台信息
|
|
360
|
+
(window as any).__jsbridgePlatform = bridge.getPlatform()
|
|
361
|
+
|
|
362
|
+
// 通过 log API 记录初始化成功
|
|
363
|
+
if (success) {
|
|
364
|
+
await writeLog({
|
|
365
|
+
level: LogLevel.INFO,
|
|
366
|
+
message: `JSBridge 初始化成功 [${bridge.getPlatform()}]`
|
|
367
|
+
})
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
return success
|
|
371
|
+
} catch (error) {
|
|
372
|
+
console.error('[JSBridge] 初始化失败', error)
|
|
373
|
+
debug.log('error', '初始化失败', error)
|
|
374
|
+
|
|
375
|
+
// 尝试上报错误
|
|
376
|
+
try {
|
|
377
|
+
await writeLog({
|
|
378
|
+
level: LogLevel.ERROR,
|
|
379
|
+
message: `JSBridge 初始化失败: ${(error as Error).message}`
|
|
380
|
+
})
|
|
381
|
+
} catch (e) {
|
|
382
|
+
// 忽略上报失败
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
return false
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* JSBridge API
|
|
391
|
+
*/
|
|
392
|
+
export const JSBridge = {
|
|
393
|
+
// 设备相关
|
|
394
|
+
scan: device.scan,
|
|
395
|
+
vibrate: device.vibrate,
|
|
396
|
+
getDeviceInfo: device.getDeviceInfo,
|
|
397
|
+
getNetworkType: device.getNetworkType,
|
|
398
|
+
|
|
399
|
+
// 媒体相关
|
|
400
|
+
chooseMedia: media.chooseMedia,
|
|
401
|
+
uploadFile: media.uploadFile,
|
|
402
|
+
previewImage: media.previewImage,
|
|
403
|
+
getImageInfo: media.getImageInfo,
|
|
404
|
+
bluetoothPrint: media.bluetoothPrint,
|
|
405
|
+
|
|
406
|
+
// 路由相关
|
|
407
|
+
setNavigationBar: router.setNavigationBar,
|
|
408
|
+
closeWebView: router.closeWebView,
|
|
409
|
+
onRoute: router.onRoute,
|
|
410
|
+
onGetRouteInfo: router.onGetRouteInfo,
|
|
411
|
+
notifyRouteChange: router.notifyRouteChange,
|
|
412
|
+
|
|
413
|
+
// 存储相关
|
|
414
|
+
getStorage: storage.getStorage,
|
|
415
|
+
setStorage: storage.setStorage,
|
|
416
|
+
getAppInfo: storage.getAppInfo,
|
|
417
|
+
onRefreshStore: storage.onRefreshStore,
|
|
418
|
+
notifyAppOnload: storage.notifyAppOnload,
|
|
419
|
+
|
|
420
|
+
// 系统相关
|
|
421
|
+
makePhoneCall: system.makePhoneCall,
|
|
422
|
+
onPdaScan: system.onPdaScan,
|
|
423
|
+
|
|
424
|
+
// 日志相关(新增)
|
|
425
|
+
writeLog, // 通用 log 方法
|
|
426
|
+
log // 便捷方法 { debug, info, warn, error }
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* 监控 API
|
|
431
|
+
*/
|
|
432
|
+
export const Monitor = {
|
|
433
|
+
getStats: monitor.getStats.bind(monitor),
|
|
434
|
+
getRecords: monitor.getRecords.bind(monitor),
|
|
435
|
+
clear: monitor.clear.bind(monitor),
|
|
436
|
+
report: monitor.report.bind(monitor), // 手动上报(通过 log)
|
|
437
|
+
reportError: monitor.reportError.bind(monitor) // 上报错误(通过 log)
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* Debug API
|
|
442
|
+
*/
|
|
443
|
+
export const Debug = {
|
|
444
|
+
trace: debug.trace.bind(debug),
|
|
445
|
+
getLogs: debug.getLogs.bind(debug),
|
|
446
|
+
clearLogs: debug.clearLogs.bind(debug)
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// 导出类型
|
|
450
|
+
export * from './types'
|
|
451
|
+
export { Platform, MonitorConfig, DebugConfig, LogLevel, LogRequest }
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
## 📚 使用示例
|
|
455
|
+
|
|
456
|
+
### 1. 子应用使用 log API
|
|
457
|
+
|
|
458
|
+
```typescript
|
|
459
|
+
import { JSBridge, LogLevel } from '@hd-front-end/jsbridge-sdk'
|
|
460
|
+
|
|
461
|
+
// 方式 1:使用通用方法
|
|
462
|
+
await JSBridge.writeLog({
|
|
463
|
+
level: LogLevel.INFO,
|
|
464
|
+
message: '用户点击了购买按钮',
|
|
465
|
+
tag: 'ShoppingCart'
|
|
466
|
+
})
|
|
467
|
+
|
|
468
|
+
// 方式 2:使用便捷方法
|
|
469
|
+
await JSBridge.log.info('用户点击了购买按钮', 'ShoppingCart')
|
|
470
|
+
await JSBridge.log.error('支付失败', 'Payment')
|
|
471
|
+
await JSBridge.log.warn('库存不足', 'Inventory')
|
|
472
|
+
await JSBridge.log.debug('调试信息', 'Debug')
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
### 2. 监控数据通过 log 上报
|
|
476
|
+
|
|
477
|
+
```typescript
|
|
478
|
+
import { init, Monitor } from '@hd-front-end/jsbridge-sdk'
|
|
479
|
+
|
|
480
|
+
// 初始化时配置监控
|
|
481
|
+
await init({
|
|
482
|
+
monitor: {
|
|
483
|
+
enabled: true,
|
|
484
|
+
autoReport: true, // 自动上报
|
|
485
|
+
reportInterval: 60000, // 1 分钟上报一次
|
|
486
|
+
logTag: 'JSBridge-Monitor' // 日志 TAG
|
|
487
|
+
}
|
|
488
|
+
})
|
|
489
|
+
|
|
490
|
+
// 使用 API(自动监控)
|
|
491
|
+
await JSBridge.scan()
|
|
492
|
+
|
|
493
|
+
// 监控数据会自动通过原生 log 能力写入日志文件
|
|
494
|
+
// 原生端可以统一收集这些日志文件上传到服务器
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
### 3. 错误立即上报
|
|
498
|
+
|
|
499
|
+
```typescript
|
|
500
|
+
import { JSBridge, Monitor } from '@hd-front-end/jsbridge-sdk'
|
|
501
|
+
|
|
502
|
+
try {
|
|
503
|
+
await JSBridge.scan()
|
|
504
|
+
} catch (error) {
|
|
505
|
+
// SDK 会自动上报错误
|
|
506
|
+
// 也可以手动上报额外信息
|
|
507
|
+
await Monitor.reportError('scan', error.message, {
|
|
508
|
+
userAction: '用户尝试扫码',
|
|
509
|
+
currentPage: '/product-detail'
|
|
510
|
+
})
|
|
511
|
+
}
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
### 4. 业务日志上报
|
|
515
|
+
|
|
516
|
+
```typescript
|
|
517
|
+
import { JSBridge } from '@hd-front-end/jsbridge-sdk'
|
|
518
|
+
|
|
519
|
+
// 记录用户行为
|
|
520
|
+
await JSBridge.log.info('用户进入商品详情页', 'UserBehavior')
|
|
521
|
+
|
|
522
|
+
// 记录业务错误
|
|
523
|
+
await JSBridge.log.error('订单创建失败: 库存不足', 'OrderService')
|
|
524
|
+
|
|
525
|
+
// 记录性能数据
|
|
526
|
+
await JSBridge.log.info(`页面加载耗时: ${loadTime}ms`, 'Performance')
|
|
527
|
+
|
|
528
|
+
// 记录调试信息
|
|
529
|
+
await JSBridge.log.debug(`API 响应: ${JSON.stringify(response)}`, 'API')
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
## 🔄 日志流程
|
|
533
|
+
|
|
534
|
+
### 1. SDK 监控日志流程
|
|
535
|
+
|
|
536
|
+
```
|
|
537
|
+
SDK API 调用
|
|
538
|
+
↓
|
|
539
|
+
Monitor 记录性能数据
|
|
540
|
+
↓
|
|
541
|
+
定时任务触发(1 分钟)
|
|
542
|
+
↓
|
|
543
|
+
Monitor.report()
|
|
544
|
+
↓
|
|
545
|
+
调用 bridge.call('log', {...})
|
|
546
|
+
↓
|
|
547
|
+
原生 LogUtil 写入日志文件
|
|
548
|
+
↓
|
|
549
|
+
原生统一收集上传到服务器
|
|
550
|
+
```
|
|
551
|
+
|
|
552
|
+
### 2. 子应用业务日志流程
|
|
553
|
+
|
|
554
|
+
```
|
|
555
|
+
子应用调用 JSBridge.log.info(...)
|
|
556
|
+
↓
|
|
557
|
+
SDK log API
|
|
558
|
+
↓
|
|
559
|
+
调用 bridge.call('log', {...})
|
|
560
|
+
↓
|
|
561
|
+
原生 LogUtil 写入日志文件
|
|
562
|
+
↓
|
|
563
|
+
原生统一收集上传到服务器
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
### 3. 错误日志流程(立即上报)
|
|
567
|
+
|
|
568
|
+
```
|
|
569
|
+
API 调用失败
|
|
570
|
+
↓
|
|
571
|
+
Monitor.reportError()
|
|
572
|
+
↓
|
|
573
|
+
立即调用 bridge.call('log', {level: 'ERROR', ...})
|
|
574
|
+
↓
|
|
575
|
+
原生 LogUtil 写入日志文件(ERROR 级别)
|
|
576
|
+
↓
|
|
577
|
+
原生可以立即上传 ERROR 日志
|
|
578
|
+
```
|
|
579
|
+
|
|
580
|
+
## 📊 原生端实现参考
|
|
581
|
+
|
|
582
|
+
### Android (Kotlin)
|
|
583
|
+
|
|
584
|
+
```kotlin
|
|
585
|
+
// 原生端 log handler 注册
|
|
586
|
+
webView.registerHandler("log") { data, callback ->
|
|
587
|
+
val request = Gson().fromJson(data, UniLog::class.java)
|
|
588
|
+
|
|
589
|
+
// 使用 LogUtil 写入日志
|
|
590
|
+
when (request.level) {
|
|
591
|
+
"DEBUG" -> LogUtil.debug(request.tag, request.message)
|
|
592
|
+
"INFO" -> LogUtil.info(request.tag, request.message)
|
|
593
|
+
"WARN" -> LogUtil.warn(request.tag, request.message)
|
|
594
|
+
"ERROR" -> LogUtil.error(request.tag, request.message)
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
callback?.invoke(null)
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
// LogUtil 会通过 SLF4J + Logback 写入文件
|
|
601
|
+
// 配置在 logback.xml 中
|
|
602
|
+
```
|
|
603
|
+
|
|
604
|
+
### iOS (Swift)
|
|
605
|
+
|
|
606
|
+
```swift
|
|
607
|
+
// 原生端 log handler 注册
|
|
608
|
+
bridge.registerHandler("log") { data, callback in
|
|
609
|
+
guard let request = data as? [String: Any],
|
|
610
|
+
let level = request["level"] as? String,
|
|
611
|
+
let message = request["message"] as? String else {
|
|
612
|
+
callback?(nil)
|
|
613
|
+
return
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
let tag = request["tag"] as? String ?? "JSBridge"
|
|
617
|
+
|
|
618
|
+
// 使用 OSLog 写入日志
|
|
619
|
+
switch level {
|
|
620
|
+
case "DEBUG":
|
|
621
|
+
os_log(.debug, log: OSLog(subsystem: "JSBridge", category: tag), "%{public}@", message)
|
|
622
|
+
case "INFO":
|
|
623
|
+
os_log(.info, log: OSLog(subsystem: "JSBridge", category: tag), "%{public}@", message)
|
|
624
|
+
case "WARN":
|
|
625
|
+
os_log(.error, log: OSLog(subsystem: "JSBridge", category: tag), "[WARN] %{public}@", message)
|
|
626
|
+
case "ERROR":
|
|
627
|
+
os_log(.fault, log: OSLog(subsystem: "JSBridge", category: tag), "%{public}@", message)
|
|
628
|
+
default:
|
|
629
|
+
break
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
callback?(nil)
|
|
633
|
+
}
|
|
634
|
+
```
|
|
635
|
+
|
|
636
|
+
## 📊 目录结构
|
|
637
|
+
|
|
638
|
+
```
|
|
639
|
+
@hd-front-end/jsbridge-sdk/
|
|
640
|
+
├── src/
|
|
641
|
+
│ ├── core/
|
|
642
|
+
│ │ └── Bridge.ts # 核心通信(200 行)
|
|
643
|
+
│ │
|
|
644
|
+
│ ├── monitor/
|
|
645
|
+
│ │ └── Monitor.ts # 监控(改用 log 上报)
|
|
646
|
+
│ │
|
|
647
|
+
│ ├── debug/
|
|
648
|
+
│ │ └── Debug.ts # Debug 模式
|
|
649
|
+
│ │
|
|
650
|
+
│ ├── api/
|
|
651
|
+
│ │ ├── device.ts
|
|
652
|
+
│ │ ├── media.ts
|
|
653
|
+
│ │ ├── router.ts
|
|
654
|
+
│ │ ├── storage.ts
|
|
655
|
+
│ │ ├── system.ts
|
|
656
|
+
│ │ └── log.ts # ← 新增 log API
|
|
657
|
+
│ │
|
|
658
|
+
│ ├── types/
|
|
659
|
+
│ │ └── index.ts
|
|
660
|
+
│ │
|
|
661
|
+
│ └── index.ts # 统一导出
|
|
662
|
+
│
|
|
663
|
+
└── package.json
|
|
664
|
+
```
|
|
665
|
+
|
|
666
|
+
## 💡 核心价值
|
|
667
|
+
|
|
668
|
+
### 1. 统一日志管理
|
|
669
|
+
|
|
670
|
+
✅ **SDK 监控日志** - 通过 log 写入原生
|
|
671
|
+
✅ **子应用业务日志** - 通过 log API 写入原生
|
|
672
|
+
✅ **统一收集** - 原生统一管理日志文件
|
|
673
|
+
✅ **统一上传** - 原生统一上传到服务器
|
|
674
|
+
|
|
675
|
+
### 2. 两个目的都实现
|
|
676
|
+
|
|
677
|
+
✅ **监控 SDK 稳定性**
|
|
678
|
+
- SDK 内部自动记录性能数据
|
|
679
|
+
- 定时通过 log 上报
|
|
680
|
+
- 错误立即通过 log 上报
|
|
681
|
+
- 原生收集后可分析 SDK 调用情况
|
|
682
|
+
|
|
683
|
+
✅ **提供子应用上传日志能力**
|
|
684
|
+
- 子应用调用 `JSBridge.log.*` 方法
|
|
685
|
+
- 日志通过 bridge 写入原生
|
|
686
|
+
- 原生统一管理和上传
|
|
687
|
+
- 子应用无需关心上传细节
|
|
688
|
+
|
|
689
|
+
### 3. 优势
|
|
690
|
+
|
|
691
|
+
✅ **统一管理** - 所有日志都通过原生 LogUtil
|
|
692
|
+
✅ **性能更好** - 不需要 H5 端发起 HTTP 请求
|
|
693
|
+
✅ **更可靠** - 原生端控制上传时机和策略
|
|
694
|
+
✅ **易于扩展** - 原生可以灵活处理日志(压缩、加密、批量上传等)
|
|
695
|
+
|
|
696
|
+
## 📊 代码统计
|
|
697
|
+
|
|
698
|
+
| 模块 | 文件 | 代码行数 |
|
|
699
|
+
|------|------|---------|
|
|
700
|
+
| 核心通信(兼容) | Bridge.ts | 200 行 |
|
|
701
|
+
| 监控(改用 log) | Monitor.ts | 180 行 |
|
|
702
|
+
| Debug | Debug.ts | 150 行 |
|
|
703
|
+
| Log API(新增) | log.ts | 80 行 |
|
|
704
|
+
| 其他 API | 5 个文件 | 320 行 |
|
|
705
|
+
| 类型定义 | types/index.ts | 120 行 |
|
|
706
|
+
| 统一导出 | index.ts | 100 行 |
|
|
707
|
+
| **总计** | **11 个文件** | **~1150 行** |
|
|
708
|
+
|
|
709
|
+
打包体积:~20KB(gzip: ~7KB)
|
|
710
|
+
|
|
711
|
+
## 🎯 总结
|
|
712
|
+
|
|
713
|
+
### 核心改进
|
|
714
|
+
|
|
715
|
+
1. ✅ **监控数据通过 log 上报** - 不再使用 HTTP
|
|
716
|
+
2. ✅ **提供 log API** - 子应用可以上传日志
|
|
717
|
+
3. ✅ **统一日志管理** - 原生端统一处理
|
|
718
|
+
4. ✅ **两个目的都实现** - 监控 SDK + 子应用日志
|
|
719
|
+
|
|
720
|
+
### 设计理念不变
|
|
721
|
+
|
|
722
|
+
- SDK 聚焦于 bridge API
|
|
723
|
+
- 不包含适配层
|
|
724
|
+
- 兼容 Android/iOS
|
|
725
|
+
- 简洁实用
|
|
726
|
+
|
|
727
|
+
---
|
|
728
|
+
|
|
729
|
+
**统一日志,统一管理,简洁高效** 🎯
|
|
730
|
+
|