@aochuang/client-sdk 1.0.367 → 1.0.369

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/utils/log.js CHANGED
@@ -1,243 +1,602 @@
1
- import { unique } from './string'
2
- import { parse } from './json'
3
1
  import { formatDate } from './date'
4
2
 
5
- function requestAnimationFrame (callback) {
6
- if (typeof uni === 'object') {
7
- return setTimeout(() => {
8
- callback && callback()
9
- }, 10)
10
- } else {
11
- return window.requestAnimationFrame(() => {
12
- callback && callback()
13
- })
3
+ let uniqueId = Date.now()
4
+
5
+ function unique () {
6
+ uniqueId++
7
+ return uniqueId
8
+ }
9
+
10
+ function parse (value) {
11
+ try {
12
+ return typeof value === 'string' ? JSON.parse(value) : value
13
+ } catch (e) {
14
+ return value
15
+ }
16
+ }
17
+
18
+ const INDEXED_DB_VERSION = 2
19
+ const INDEXED_DB_STORE_NAME = 'logs'
20
+ const INDEXED_DB_META_STORE_NAME = 'meta'
21
+ const INDEXED_DB_ORDER_INDEX_NAME = 'order'
22
+ const INDEXED_DB_MAX_SIZE = 50 * 1024 * 1024
23
+ const INDEXED_DB_STATS_KEY = 'stats'
24
+
25
+ function isUniRuntime () {
26
+ try {
27
+ return !!uni
28
+ } catch (e) {
29
+ return false
30
+ }
31
+ }
32
+
33
+ function isIndexedDbSupported () {
34
+ return typeof window !== 'undefined' && !isUniRuntime() && !!window.indexedDB
35
+ }
36
+
37
+ function getLogSize (item) {
38
+ return item && item.size ? item.size : JSON.stringify(item || {}).length
39
+ }
40
+
41
+ function getLogByteSize (item) {
42
+ const value = JSON.stringify(item || {})
43
+ if (typeof Blob !== 'undefined') {
44
+ return new Blob([value]).size
45
+ }
46
+ return unescape(encodeURIComponent(value)).length
47
+ }
48
+
49
+ function normalizeLogStore (store) {
50
+ let nextStore = store
51
+ if (!nextStore) {
52
+ nextStore = {
53
+ size: 0,
54
+ data: []
55
+ }
56
+ }
57
+ if (Array.isArray(nextStore)) {
58
+ let totalSize = 0
59
+ nextStore = {
60
+ data: nextStore.map(item => {
61
+ if (item && !item.size) {
62
+ item.size = JSON.stringify(item).length
63
+ }
64
+ totalSize += getLogSize(item)
65
+ return item
66
+ })
67
+ }
68
+ nextStore.size = totalSize
69
+ }
70
+ if (!Array.isArray(nextStore.data)) {
71
+ nextStore.data = []
72
+ }
73
+ if (!nextStore.size) {
74
+ nextStore.size = nextStore.data.reduce((totalSize, item) => {
75
+ return totalSize + getLogSize(item)
76
+ }, 0)
77
+ }
78
+ return nextStore
79
+ }
80
+
81
+ function parseLogStoreValue (value) {
82
+ if (!value) {
83
+ return null
84
+ }
85
+ if (typeof value === 'string') {
86
+ value = parse(value)
87
+ }
88
+ return normalizeLogStore(value)
89
+ }
90
+
91
+ function readLegacyStorage (storageKey) {
92
+ if (isUniRuntime()) {
93
+ return uni.getStorageSync(storageKey)
94
+ }
95
+ if (typeof window !== 'undefined' && window.localStorage) {
96
+ return window.localStorage.getItem(storageKey)
97
+ }
98
+ return null
99
+ }
100
+
101
+ function writeLegacyStorage (storageKey, value) {
102
+ if (isUniRuntime()) {
103
+ uni.setStorageSync(storageKey, value)
104
+ return
105
+ }
106
+ if (typeof window !== 'undefined' && window.localStorage) {
107
+ window.localStorage.setItem(storageKey, typeof value === 'string' ? value : JSON.stringify(value))
108
+ }
109
+ }
110
+
111
+ function removeLegacyStorage (storageKey) {
112
+ if (isUniRuntime()) {
113
+ uni.removeStorageSync(storageKey)
114
+ return
115
+ }
116
+ if (typeof window !== 'undefined' && window.localStorage) {
117
+ window.localStorage.removeItem(storageKey)
118
+ }
119
+ }
120
+
121
+ function hashString (value) {
122
+ let hash = 2166136261
123
+ for (let index = 0; index < value.length; index++) {
124
+ hash ^= value.charCodeAt(index)
125
+ hash += (hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24)
126
+ }
127
+ return (hash >>> 0).toString(16)
128
+ }
129
+
130
+ function getLogRecordId (log) {
131
+ if (log && log.id !== undefined && log.id !== null) {
132
+ return `id:${log.id}`
133
+ }
134
+ const value = JSON.stringify(log || {})
135
+ return `legacy:${hashString(value)}:${value.length}`
136
+ }
137
+
138
+ function createIndexedDbStores (db) {
139
+ const logStore = db.createObjectStore(INDEXED_DB_STORE_NAME, { keyPath: 'recordId' })
140
+ logStore.createIndex(INDEXED_DB_ORDER_INDEX_NAME, 'order', { unique: false })
141
+ if (!db.objectStoreNames.contains(INDEXED_DB_META_STORE_NAME)) {
142
+ db.createObjectStore(INDEXED_DB_META_STORE_NAME, { keyPath: 'key' })
14
143
  }
144
+ return logStore
145
+ }
146
+
147
+ function createIndexedDbRecord (log, order) {
148
+ return {
149
+ recordId: getLogRecordId(log),
150
+ order,
151
+ byteSize: getLogByteSize(log),
152
+ log
153
+ }
154
+ }
155
+
156
+ function uniqueLogs (logs) {
157
+ const recordIds = Object.create(null)
158
+ return logs.filter(log => {
159
+ const recordId = getLogRecordId(log)
160
+ if (recordIds[recordId]) {
161
+ return false
162
+ }
163
+ recordIds[recordId] = true
164
+ return true
165
+ })
15
166
  }
16
167
 
17
- function cancelAnimationFrame (rafId) {
18
- if (typeof uni === 'object') {
19
- clearTimeout(rafId)
20
- } else {
21
- window.cancelAnimationFrame(rafId)
22
- }
23
- }
24
-
25
- const LogManage = {
26
- list: [],
27
- add: (logInstance) => {
28
- if (!(logInstance instanceof Log)) {
29
- throw new Error('不是有效日志实例')
30
- }
31
- if (!this.list.some(v => v === logInstance)) {
32
- throw new Error('日志实例已存在')
33
- }
34
- this.list.push(logInstance)
35
- },
36
- remove: (logInstance) => {
37
- let index = -1
38
- this.list.some((v, i) => {
39
- if (v === logInstance) {
40
- index = i
41
- return true
42
- }
43
- })
44
- if (index < 0) {
45
- return
46
- }
47
- this.list.splice(index, 1)
48
- }
168
+ function trimRecordsToIndexedDbLimit (records) {
169
+ let totalBytes = 0
170
+ const retainedRecords = []
171
+ records.some(record => {
172
+ if (record.byteSize + totalBytes > INDEXED_DB_MAX_SIZE) {
173
+ return true
174
+ }
175
+ retainedRecords.push(record)
176
+ totalBytes += record.byteSize
177
+ return false
178
+ })
179
+ return {
180
+ records: retainedRecords,
181
+ totalBytes,
182
+ removedCount: records.length - retainedRecords.length
183
+ }
49
184
  }
50
185
 
51
186
  export default class Log {
52
187
 
53
- prefix = 'log'
54
-
55
- name = null
56
-
57
- tempStore = []
58
-
59
- beforeAddCallback = null
60
-
61
- constructor (options) {
62
- options = Object.assign({
63
- name: null,
64
- console: false, // 是否控制台打印
65
- maxSize: 1024 * 1024 * 3 // 最多存3M的日志数据
66
- }, options)
67
- if (!options.name) {
68
- throw new Error('必须指定日志的name名称')
69
- }
70
- if (LogManage.list.some(v => v.name === options.name)) {
71
- throw new Error('已存在同名的日志实例')
72
- }
73
- this.options = options
74
- this.maxSize = options.maxSize
75
- this.name = options.name
76
- this.validTime = options.validTime
77
- }
78
-
79
- get storageKey () {
80
- return this.prefix + '_' + this.name
81
- }
82
-
83
- onBeforeAdd (callback) {
84
- this.beforeAddCallback = callback
85
- }
86
-
87
- /**
88
- * 新增日志到内存
89
- */
90
- addLog (data) {
91
- data = Object.assign({
92
- group: this.defautGroup,
93
- refererId: undefined, // 可以关联一个日志
94
- content: null
95
- }, data, {
96
- createTime: formatDate(new Date())
97
- })
98
- data.id = unique(data.group)
99
- if (this.beforeAddCallback && this.beforeAddCallback({
100
- data
101
- }) === false) {
102
- return
103
- }
104
- const size = JSON.stringify(data).length
105
- data.size = size
106
- this.tempStore.push(data)
107
- if (this.tempStore.length > 5) {
108
- this.publishLogData().then(null, err => {
109
- console.log(err)
188
+ static checkAndSync (storageKeys) {
189
+ const keys = [].concat(storageKeys || []).filter((storageKey, index, list) => {
190
+ return typeof storageKey === 'string' && storageKey && list.indexOf(storageKey) === index
191
+ })
192
+ return Promise.all(keys.map(storageKey => {
193
+ const log = new Log({ name: storageKey })
194
+ log._customStorageKey = storageKey
195
+ return log.checkAndSync().then(result => {
196
+ return log._closeIndexedDb().then(() => {
197
+ return Object.assign({ storageKey }, result)
198
+ })
110
199
  })
111
- }
112
- if (this.options.console) {
113
- console.log(`【${this.name || 'operateLog'}】${JSON.stringify(data) || '--'}`)
200
+ }))
201
+ }
202
+
203
+ constructor (options) {
204
+ this.prefix = 'log'
205
+ this.name = null
206
+ this.tempStore = []
207
+ this.beforeAddCallback = null
208
+ this._writePromise = Promise.resolve()
209
+ this._indexedDbPromise = null
210
+ this._syncPromise = null
211
+ this._customStorageKey = null
212
+ options = Object.assign({
213
+ name: null,
214
+ console: false, // 是否控制台打印
215
+ maxSize: 1024 * 1024 * 3 // 非网页版本默认最多存3M日志数据
216
+ }, options)
217
+ if (!options.name) {
218
+ throw new Error('必须指定日志的name名称')
114
219
  }
115
- return data
116
- }
117
-
118
- /**
119
- * 清空本地日志
120
- */
121
- clearLogData () {
122
- return new Promise((resolve) => {
123
- this.tempStore = []
124
- if (typeof uni !== 'undefined') {
125
- uni.removeStorageSync(this.storageKey)
126
- } else {
127
- window.localStorage.removeItem(this.storageKey)
128
- }
129
- resolve()
130
- })
131
- }
220
+ this.options = options
221
+ this.maxSize = options.maxSize
222
+ this.name = options.name
223
+ this.validTime = options.validTime
224
+ }
225
+
226
+ get storageKey () {
227
+ return this._customStorageKey || this.prefix + '_' + this.name
228
+ }
229
+
230
+ get useIndexedDb () {
231
+ return isIndexedDbSupported()
232
+ }
233
+
234
+ get indexedDbName () {
235
+ return this.storageKey
236
+ }
237
+
238
+ onBeforeAdd (callback) {
239
+ this.beforeAddCallback = callback
240
+ }
241
+
242
+ _enqueueWrite (callback) {
243
+ const task = this._writePromise.then(callback, callback)
244
+ this._writePromise = task.catch(() => {})
245
+ return task
246
+ }
247
+
248
+ _closeIndexedDb () {
249
+ const indexedDbPromise = this._indexedDbPromise
250
+ this._indexedDbPromise = null
251
+ if (!indexedDbPromise) {
252
+ return Promise.resolve()
253
+ }
254
+ return indexedDbPromise.then(db => {
255
+ db.close()
256
+ }, () => {})
257
+ }
258
+
259
+ _upgradeIndexedDb (request) {
260
+ const db = request.result
261
+ const transaction = request.transaction
262
+ if (!db.objectStoreNames.contains(INDEXED_DB_STORE_NAME)) {
263
+ createIndexedDbStores(db)
264
+ transaction.objectStore(INDEXED_DB_META_STORE_NAME).put({
265
+ key: INDEXED_DB_STATS_KEY,
266
+ totalBytes: 0,
267
+ nextOrder: 0
268
+ })
269
+ return
270
+ }
271
+
272
+ const legacyStore = transaction.objectStore(INDEXED_DB_STORE_NAME)
273
+ const legacyRequest = legacyStore.get(this.storageKey)
274
+ legacyRequest.onerror = () => {
275
+ transaction.abort()
276
+ }
277
+ legacyRequest.onsuccess = () => {
278
+ let legacyLogStore = null
279
+ try {
280
+ const result = legacyRequest.result
281
+ legacyLogStore = result && result.value ? parseLogStoreValue(result.value) : null
282
+ } catch (e) {
283
+ legacyLogStore = null
284
+ }
285
+ db.deleteObjectStore(INDEXED_DB_STORE_NAME)
286
+ if (db.objectStoreNames.contains(INDEXED_DB_META_STORE_NAME)) {
287
+ db.deleteObjectStore(INDEXED_DB_META_STORE_NAME)
288
+ }
289
+ const logStore = createIndexedDbStores(db)
290
+ const logs = uniqueLogs(legacyLogStore ? legacyLogStore.data : [])
291
+ const records = logs.map((log, index) => createIndexedDbRecord(log, logs.length - index))
292
+ const result = trimRecordsToIndexedDbLimit(records)
293
+ result.records.forEach(record => logStore.put(record))
294
+ transaction.objectStore(INDEXED_DB_META_STORE_NAME).put({
295
+ key: INDEXED_DB_STATS_KEY,
296
+ totalBytes: result.totalBytes,
297
+ nextOrder: logs.length
298
+ })
299
+ }
300
+ }
132
301
 
133
- loadLogData () {
302
+ openIndexedDb () {
303
+ if (!this.useIndexedDb) {
304
+ return Promise.reject(new Error('indexedDB不可用'))
305
+ }
306
+ if (this._indexedDbPromise) {
307
+ return this._indexedDbPromise
308
+ }
309
+ this._indexedDbPromise = new Promise((resolve, reject) => {
310
+ const request = window.indexedDB.open(this.indexedDbName, INDEXED_DB_VERSION)
311
+ request.onerror = () => {
312
+ this._indexedDbPromise = null
313
+ reject(request.error || new Error('打开日志数据库失败'))
314
+ }
315
+ request.onupgradeneeded = () => {
316
+ this._upgradeIndexedDb(request)
317
+ }
318
+ request.onsuccess = () => {
319
+ const db = request.result
320
+ db.onversionchange = () => {
321
+ db.close()
322
+ this._indexedDbPromise = null
323
+ }
324
+ resolve(db)
325
+ }
326
+ })
327
+ return this._indexedDbPromise
328
+ }
329
+
330
+ _readIndexedDbLogData () {
331
+ return this.openIndexedDb().then(db => {
134
332
  return new Promise((resolve, reject) => {
135
- if (this._lastLoadLogDataRaf) {
136
- this._lastLoadLogDataRaf.cancelFn()
333
+ const transaction = db.transaction([INDEXED_DB_STORE_NAME], 'readonly')
334
+ const store = transaction.objectStore(INDEXED_DB_STORE_NAME)
335
+ const request = store.index(INDEXED_DB_ORDER_INDEX_NAME).openCursor(null, 'prev')
336
+ const data = []
337
+ let size = 0
338
+ request.onerror = () => {
339
+ reject(request.error || new Error('读取日志失败'))
137
340
  }
138
- let rafId = null
139
- this._lastLoadLogDataRaf = {
140
- cancelFn: () => {
141
- cancelAnimationFrame(rafId)
142
- this._lastLoadLogDataRaf = null
143
- reject('cancel')
144
- },
145
- execFn: () => {
146
- rafId = requestAnimationFrame(() => {
147
- let localTempStore
148
- if (typeof uni !== 'undefined') {
149
- localTempStore = uni.getStorageSync(this.storageKey)
150
- } else {
151
- localTempStore = window.localStorage.getItem(this.storageKey)
152
- }
153
- if (localTempStore) {
154
- localTempStore = parse(localTempStore)
155
- if (Array.isArray(localTempStore)) {
156
- let totalSize = 0
157
- localTempStore = {
158
- data: localTempStore.map(v => {
159
- if (!v.size) {
160
- v.size = JSON.stringify(v).length
161
- }
162
- totalSize += v.size
163
- return v
164
- })
165
- }
166
- localTempStore.size = totalSize
167
- }
168
- }
169
- if (!localTempStore) {
170
- localTempStore = {
171
- size: 0,
172
- data: []
173
- }
174
- }
175
- // 自清理一下
176
- if (this._lastLoadLogDataRaf) {
177
- cancelAnimationFrame(this._lastLoadLogDataRaf)
178
- this._lastLoadLogDataRaf = null
179
- }
180
- resolve(localTempStore)
181
- })
341
+ request.onsuccess = event => {
342
+ const cursor = event.target.result
343
+ if (!cursor) {
344
+ resolve({ size, data })
345
+ return
182
346
  }
347
+ data.push(cursor.value.log)
348
+ size += getLogSize(cursor.value.log)
349
+ cursor.continue()
183
350
  }
184
- this._lastLoadLogDataRaf.execFn()
185
351
  })
186
- }
352
+ })
353
+ }
187
354
 
188
- publishLogData () {
189
- return new Promise((resolve, reject) => {
190
- if (this._lastPublishLogDataRaf) {
191
- this._lastPublishLogDataRaf.cancelFn()
355
+ _writeIndexedDbLogs (logs) {
356
+ if (!logs.length) {
357
+ return Promise.resolve({ migratedCount: 0, duplicateCount: 0, removedCount: 0 })
358
+ }
359
+ const uniqueLogList = uniqueLogs(logs)
360
+ const repeatedInputCount = logs.length - uniqueLogList.length
361
+ return this.openIndexedDb().then(db => {
362
+ return new Promise((resolve, reject) => {
363
+ const transaction = db.transaction(
364
+ [INDEXED_DB_STORE_NAME, INDEXED_DB_META_STORE_NAME],
365
+ 'readwrite'
366
+ )
367
+ const store = transaction.objectStore(INDEXED_DB_STORE_NAME)
368
+ const metaStore = transaction.objectStore(INDEXED_DB_META_STORE_NAME)
369
+ const metaRequest = metaStore.get(INDEXED_DB_STATS_KEY)
370
+ let result = null
371
+ transaction.oncomplete = () => {
372
+ resolve(result || { migratedCount: 0, duplicateCount: 0, removedCount: 0 })
373
+ }
374
+ transaction.onerror = () => {
375
+ reject(transaction.error || new Error('写入日志失败'))
376
+ }
377
+ transaction.onabort = () => {
378
+ reject(transaction.error || new Error('写入日志失败'))
379
+ }
380
+ metaRequest.onsuccess = () => {
381
+ const stats = metaRequest.result || {
382
+ key: INDEXED_DB_STATS_KEY,
383
+ totalBytes: 0,
384
+ nextOrder: 0
192
385
  }
193
- let rafId = null
194
- this._lastPublishLogDataRaf = {
195
- cancelFn: () => {
196
- cancelAnimationFrame(rafId)
197
- this._lastPublishLogDataRaf = null
198
- reject('cancel')
199
- },
200
- execFn: () => {
201
- if (!this.tempStore || !this.tempStore.length) {
202
- return resolve()
386
+ const records = uniqueLogList.map((log, index) => {
387
+ return createIndexedDbRecord(log, stats.nextOrder + uniqueLogList.length - index)
388
+ })
389
+ let pendingCount = records.length
390
+ let duplicateCount = repeatedInputCount
391
+ records.forEach(record => {
392
+ const getRequest = store.get(record.recordId)
393
+ getRequest.onsuccess = () => {
394
+ if (getRequest.result) {
395
+ stats.totalBytes -= getRequest.result.byteSize || 0
396
+ duplicateCount++
397
+ }
398
+ stats.totalBytes += record.byteSize
399
+ store.put(record)
400
+ pendingCount--
401
+ if (!pendingCount) {
402
+ stats.nextOrder += uniqueLogList.length
403
+ trimOldestLogs(stats, duplicateCount)
203
404
  }
204
- rafId = requestAnimationFrame(() => {
205
- this.loadLogData().then((localTempStore) => {
206
- const allData = [].concat(this.tempStore, localTempStore.data)
207
- const nextLocalStore = {
208
- size: 0,
209
- data: []
210
- }
211
- allData.some((item, index) => {
212
- if (item.size + nextLocalStore.size >= this.maxSize) {
213
- return true
214
- }
215
- nextLocalStore.data.push(item)
216
- nextLocalStore.size = nextLocalStore.size + item.size
217
- })
218
- if (typeof uni !== 'undefined') {
219
- uni.setStorageSync(this.storageKey, nextLocalStore)
220
- } else {
221
- window.localStorage.setItem(this.storageKey, JSON.stringify(nextLocalStore))
222
- }
223
- this.tempStore = []
224
- // 自清理一下
225
- if (this._lastPublishLogDataRaf) {
226
- cancelAnimationFrame(this._lastPublishLogDataRaf)
227
- this._lastPublishLogDataRaf = null
228
- }
229
- resolve()
230
- }, reject)
231
- })
405
+ }
406
+ })
407
+
408
+ function trimOldestLogs (nextStats, nextDuplicateCount) {
409
+ let removedCount = 0
410
+ const cursorRequest = store.index(INDEXED_DB_ORDER_INDEX_NAME).openCursor(null, 'next')
411
+ cursorRequest.onsuccess = event => {
412
+ const cursor = event.target.result
413
+ if (nextStats.totalBytes <= INDEXED_DB_MAX_SIZE || !cursor) {
414
+ metaStore.put(nextStats)
415
+ result = {
416
+ migratedCount: logs.length - nextDuplicateCount,
417
+ duplicateCount: nextDuplicateCount,
418
+ removedCount
419
+ }
420
+ return
421
+ }
422
+ nextStats.totalBytes -= cursor.value.byteSize || 0
423
+ cursor.delete()
424
+ removedCount++
425
+ cursor.continue()
232
426
  }
233
427
  }
234
- this._lastPublishLogDataRaf.execFn()
428
+ }
429
+ })
430
+ })
431
+ }
432
+
433
+ _writeLegacyLogs (logs) {
434
+ const localLogStore = parseLogStoreValue(readLegacyStorage(this.storageKey)) || {
435
+ size: 0,
436
+ data: []
437
+ }
438
+ const allData = [].concat(logs, localLogStore.data)
439
+ const nextLocalStore = {
440
+ size: 0,
441
+ data: []
442
+ }
443
+ allData.some(item => {
444
+ const itemSize = getLogSize(item)
445
+ if (itemSize + nextLocalStore.size >= this.maxSize) {
446
+ return true
447
+ }
448
+ nextLocalStore.data.push(item)
449
+ nextLocalStore.size += itemSize
450
+ return false
451
+ })
452
+ writeLegacyStorage(this.storageKey, nextLocalStore)
453
+ }
454
+
455
+ checkAndSync () {
456
+ if (!this.useIndexedDb) {
457
+ return Promise.resolve({
458
+ synced: false,
459
+ migratedCount: 0,
460
+ duplicateCount: 0,
461
+ removedCount: 0
462
+ })
463
+ }
464
+ if (this._syncPromise) {
465
+ return this._syncPromise
466
+ }
467
+ const task = this._enqueueWrite(() => {
468
+ const legacyLogStore = parseLogStoreValue(readLegacyStorage(this.storageKey))
469
+ if (!legacyLogStore || !legacyLogStore.data.length) {
470
+ return {
471
+ synced: false,
472
+ migratedCount: 0,
473
+ duplicateCount: 0,
474
+ removedCount: 0
475
+ }
476
+ }
477
+ return this._writeIndexedDbLogs(legacyLogStore.data).then(result => {
478
+ removeLegacyStorage(this.storageKey)
479
+ return Object.assign({ synced: true }, result)
480
+ })
481
+ })
482
+ this._syncPromise = task.then(result => {
483
+ this._syncPromise = null
484
+ return result
485
+ }, () => {
486
+ this._syncPromise = null
487
+ return {
488
+ synced: false,
489
+ migratedCount: 0,
490
+ duplicateCount: 0,
491
+ removedCount: 0
492
+ }
493
+ })
494
+ return this._syncPromise
495
+ }
496
+
497
+ /**
498
+ * 新增日志到内存
499
+ */
500
+ addLog (data) {
501
+ data = Object.assign({
502
+ group: this.defautGroup,
503
+ refererId: undefined, // 可以关联一个日志
504
+ content: null
505
+ }, data, {
506
+ createTime: formatDate(new Date())
507
+ })
508
+ data.id = unique(data.group)
509
+ if (this.beforeAddCallback && this.beforeAddCallback({ data }) === false) {
510
+ return
511
+ }
512
+ data.size = JSON.stringify(data).length
513
+ this.tempStore.push(data)
514
+ this.checkAndSync().catch(() => {})
515
+ if (this.tempStore.length > 5) {
516
+ this.publishLogData().then(null, err => {
517
+ console.log(err)
518
+ })
519
+ }
520
+ if (this.options.console) {
521
+ console.log(`【${this.name || 'operateLog'}】${JSON.stringify(data) || '--'}`)
522
+ }
523
+ return data
524
+ }
525
+
526
+ /**
527
+ * 清空本地日志
528
+ */
529
+ clearLogData () {
530
+ this.tempStore = []
531
+ const task = this._enqueueWrite(() => {
532
+ if (!this.useIndexedDb) {
533
+ removeLegacyStorage(this.storageKey)
534
+ return
535
+ }
536
+ return this.openIndexedDb().then(db => {
537
+ return new Promise((resolve, reject) => {
538
+ const transaction = db.transaction(
539
+ [INDEXED_DB_STORE_NAME, INDEXED_DB_META_STORE_NAME],
540
+ 'readwrite'
541
+ )
542
+ transaction.objectStore(INDEXED_DB_STORE_NAME).clear()
543
+ transaction.objectStore(INDEXED_DB_META_STORE_NAME).put({
544
+ key: INDEXED_DB_STATS_KEY,
545
+ totalBytes: 0,
546
+ nextOrder: 0
547
+ })
548
+ transaction.oncomplete = resolve
549
+ transaction.onerror = () => reject(transaction.error || new Error('清理日志失败'))
550
+ transaction.onabort = () => reject(transaction.error || new Error('清理日志失败'))
235
551
  })
236
- }
237
-
238
- exportLogData () {
239
- return this.publishLogData().then(() => {
240
- return this.loadLogData()
241
- })
242
- }
243
- }
552
+ }).then(() => {
553
+ removeLegacyStorage(this.storageKey)
554
+ })
555
+ })
556
+ return task.catch(() => {
557
+ removeLegacyStorage(this.storageKey)
558
+ })
559
+ }
560
+
561
+ loadLogData () {
562
+ return this.checkAndSync().then(() => this._writePromise).then(() => {
563
+ if (this.useIndexedDb) {
564
+ return this._readIndexedDbLogData().catch(() => ({ size: 0, data: [] }))
565
+ }
566
+ return parseLogStoreValue(readLegacyStorage(this.storageKey)) || {
567
+ size: 0,
568
+ data: []
569
+ }
570
+ })
571
+ }
572
+
573
+ publishLogData () {
574
+ return this.checkAndSync().then(() => {
575
+ return this._enqueueWrite(() => {
576
+ if (!this.tempStore.length) {
577
+ return
578
+ }
579
+ const flushingTempStore = this.tempStore.slice()
580
+ this.tempStore = []
581
+ let writeTask
582
+ if (this.useIndexedDb) {
583
+ writeTask = this._writeIndexedDbLogs(flushingTempStore).catch(() => {})
584
+ } else {
585
+ writeTask = Promise.resolve().then(() => this._writeLegacyLogs(flushingTempStore))
586
+ }
587
+ return writeTask.catch(error => {
588
+ this.tempStore = flushingTempStore.concat(this.tempStore)
589
+ throw error
590
+ })
591
+ })
592
+ })
593
+ }
594
+
595
+ exportLogData () {
596
+ return this.checkAndSync().then(() => {
597
+ return this.publishLogData()
598
+ }).then(() => {
599
+ return this.loadLogData()
600
+ })
601
+ }
602
+ }