@cloudcare/browser-core 3.1.23 → 3.2.0
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/cjs/browser/pageExitObservable.js +1 -1
- package/cjs/browser/pageExitObservable.js.map +1 -1
- package/cjs/browser/runOnReadyState.js +5 -1
- package/cjs/browser/runOnReadyState.js.map +1 -1
- package/cjs/console/consoleObservable.js +17 -9
- package/cjs/console/consoleObservable.js.map +1 -1
- package/cjs/dataMap.js +11 -2
- package/cjs/dataMap.js.map +1 -1
- package/cjs/helper/boundedBuffer.js +21 -17
- package/cjs/helper/boundedBuffer.js.map +1 -1
- package/cjs/helper/connectivity.js +16 -0
- package/cjs/helper/connectivity.js.map +1 -0
- package/cjs/helper/display.js +15 -12
- package/cjs/helper/display.js.map +1 -1
- package/cjs/helper/displayAlreadyInitializedError.js +13 -0
- package/cjs/helper/displayAlreadyInitializedError.js.map +1 -0
- package/cjs/helper/encoder.js +43 -0
- package/cjs/helper/encoder.js.map +1 -0
- package/cjs/helper/lifeCycle.js +0 -1
- package/cjs/helper/lifeCycle.js.map +1 -1
- package/cjs/helper/monitor.js +6 -6
- package/cjs/helper/monitor.js.map +1 -1
- package/cjs/helper/serialisation/jsonStringify.js.map +1 -1
- package/cjs/index.js +33 -0
- package/cjs/index.js.map +1 -1
- package/cjs/report/reportObservable.js +26 -20
- package/cjs/report/reportObservable.js.map +1 -1
- package/cjs/telemetry/telemetry.js +60 -31
- package/cjs/telemetry/telemetry.js.map +1 -1
- package/cjs/telemetry/types.js +2 -1
- package/cjs/telemetry/types.js.map +1 -1
- package/cjs/transport/batch.js +129 -79
- package/cjs/transport/batch.js.map +1 -1
- package/cjs/transport/flushController.js +18 -5
- package/cjs/transport/flushController.js.map +1 -1
- package/cjs/transport/httpRequest.js +35 -22
- package/cjs/transport/httpRequest.js.map +1 -1
- package/cjs/transport/index.js +4 -4
- package/cjs/transport/index.js.map +1 -1
- package/cjs/transport/startBatchWithReplica.js +26 -10
- package/cjs/transport/startBatchWithReplica.js.map +1 -1
- package/esm/browser/pageExitObservable.js +1 -1
- package/esm/browser/pageExitObservable.js.map +1 -1
- package/esm/browser/runOnReadyState.js +5 -1
- package/esm/browser/runOnReadyState.js.map +1 -1
- package/esm/console/consoleObservable.js +18 -10
- package/esm/console/consoleObservable.js.map +1 -1
- package/esm/dataMap.js +11 -2
- package/esm/dataMap.js.map +1 -1
- package/esm/helper/boundedBuffer.js +21 -16
- package/esm/helper/boundedBuffer.js.map +1 -1
- package/esm/helper/connectivity.js +10 -0
- package/esm/helper/connectivity.js.map +1 -0
- package/esm/helper/display.js +12 -11
- package/esm/helper/display.js.map +1 -1
- package/esm/helper/displayAlreadyInitializedError.js +7 -0
- package/esm/helper/displayAlreadyInitializedError.js.map +1 -0
- package/esm/helper/encoder.js +37 -0
- package/esm/helper/encoder.js.map +1 -0
- package/esm/helper/lifeCycle.js +0 -1
- package/esm/helper/lifeCycle.js.map +1 -1
- package/esm/helper/monitor.js +6 -6
- package/esm/helper/monitor.js.map +1 -1
- package/esm/helper/serialisation/jsonStringify.js.map +1 -1
- package/esm/index.js +3 -0
- package/esm/index.js.map +1 -1
- package/esm/report/reportObservable.js +27 -21
- package/esm/report/reportObservable.js.map +1 -1
- package/esm/telemetry/telemetry.js +57 -31
- package/esm/telemetry/telemetry.js.map +1 -1
- package/esm/telemetry/types.js +2 -1
- package/esm/telemetry/types.js.map +1 -1
- package/esm/transport/batch.js +127 -77
- package/esm/transport/batch.js.map +1 -1
- package/esm/transport/flushController.js +18 -5
- package/esm/transport/flushController.js.map +1 -1
- package/esm/transport/httpRequest.js +35 -22
- package/esm/transport/httpRequest.js.map +1 -1
- package/esm/transport/index.js +1 -1
- package/esm/transport/index.js.map +1 -1
- package/esm/transport/startBatchWithReplica.js +27 -11
- package/esm/transport/startBatchWithReplica.js.map +1 -1
- package/package.json +2 -2
- package/src/browser/pageExitObservable.js +2 -2
- package/src/browser/runOnReadyState.js +3 -1
- package/src/console/consoleObservable.js +22 -14
- package/src/dataMap.js +11 -2
- package/src/helper/boundedBuffer.js +21 -14
- package/src/helper/connectivity.js +11 -0
- package/src/helper/display.js +16 -14
- package/src/helper/displayAlreadyInitializedError.js +7 -0
- package/src/helper/encoder.js +43 -0
- package/src/helper/lifeCycle.js +0 -1
- package/src/helper/monitor.js +6 -6
- package/src/helper/serialisation/jsonStringify.js +1 -1
- package/src/index.js +3 -0
- package/src/report/reportObservable.js +43 -27
- package/src/telemetry/telemetry.js +60 -37
- package/src/telemetry/types.js +2 -1
- package/src/transport/batch.js +154 -89
- package/src/transport/flushController.js +19 -6
- package/src/transport/httpRequest.js +34 -35
- package/src/transport/index.js +1 -1
- package/src/transport/startBatchWithReplica.js +25 -14
- package/types/index.d.ts +12 -0
|
@@ -3,8 +3,10 @@ import {
|
|
|
3
3
|
toStackTraceString,
|
|
4
4
|
NO_ERROR_STACK_PRESENT_MESSAGE
|
|
5
5
|
} from '../helper/errorTools'
|
|
6
|
+
import { createBoundedBuffer } from '../helper/boundedBuffer'
|
|
6
7
|
import { computeStackTrace } from '../tracekit'
|
|
7
8
|
import { Observable } from '../helper/observable'
|
|
9
|
+
import { getConnectivity } from '../helper/connectivity'
|
|
8
10
|
import {
|
|
9
11
|
displayIfDebugEnabled,
|
|
10
12
|
startMonitorErrorCollection
|
|
@@ -31,35 +33,37 @@ export var TelemetryService = {
|
|
|
31
33
|
RUM: 'browser-rum-sdk'
|
|
32
34
|
}
|
|
33
35
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
// eslint-disable-next-line local-rules/disallow-side-effects
|
|
37
|
+
var preStartTelemetryBuffer = createBoundedBuffer()
|
|
38
|
+
var onRawTelemetryEventCollected = function (event) {
|
|
39
|
+
preStartTelemetryBuffer.add(function () {
|
|
40
|
+
onRawTelemetryEventCollected(event)
|
|
41
|
+
})
|
|
38
42
|
}
|
|
39
43
|
|
|
40
|
-
var onRawTelemetryEventCollected
|
|
41
|
-
|
|
42
44
|
export function startTelemetry(telemetryService, configuration) {
|
|
43
45
|
let contextProvider
|
|
44
46
|
var observable = new Observable()
|
|
45
|
-
|
|
47
|
+
const alreadySentEvents = new Set()
|
|
48
|
+
const telemetryEnabled =
|
|
46
49
|
configuration.telemetryEnabled &&
|
|
47
50
|
performDraw(configuration.telemetrySampleRate)
|
|
48
|
-
|
|
51
|
+
const runtimeEnvInfo = getRuntimeEnvInfo()
|
|
49
52
|
onRawTelemetryEventCollected = function (rawEvent) {
|
|
50
|
-
|
|
51
|
-
|
|
53
|
+
const stringifiedEvent = jsonStringify(rawEvent)
|
|
54
|
+
if (
|
|
55
|
+
telemetryEnabled &&
|
|
56
|
+
alreadySentEvents.size < configuration.maxTelemetryEventsPerPage &&
|
|
57
|
+
!alreadySentEvents.has(stringifiedEvent)
|
|
58
|
+
) {
|
|
59
|
+
var event = toTelemetryEvent(telemetryService, rawEvent, runtimeEnvInfo)
|
|
52
60
|
observable.notify(event)
|
|
61
|
+
alreadySentEvents.add(stringifiedEvent)
|
|
53
62
|
}
|
|
54
63
|
}
|
|
55
64
|
startMonitorErrorCollection(addTelemetryError)
|
|
56
65
|
|
|
57
|
-
|
|
58
|
-
maxEventsPerPage: configuration.maxTelemetryEventsPerPage,
|
|
59
|
-
sentEventCount: 0
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
function toTelemetryEvent(telemetryService, event) {
|
|
66
|
+
function toTelemetryEvent(telemetryService, event, runtimeEnvInfo) {
|
|
63
67
|
return extend2Lev(
|
|
64
68
|
{
|
|
65
69
|
type: 'telemetry',
|
|
@@ -67,7 +71,10 @@ export function startTelemetry(telemetryService, configuration) {
|
|
|
67
71
|
service: telemetryService,
|
|
68
72
|
version: __BUILD_ENV__SDK_VERSION__,
|
|
69
73
|
source: 'browser',
|
|
70
|
-
telemetry: event
|
|
74
|
+
telemetry: extend2Lev(event, {
|
|
75
|
+
runtime_env: runtimeEnvInfo,
|
|
76
|
+
connectivity: getConnectivity()
|
|
77
|
+
})
|
|
71
78
|
},
|
|
72
79
|
contextProvider !== undefined ? contextProvider() : {}
|
|
73
80
|
)
|
|
@@ -78,17 +85,39 @@ export function startTelemetry(telemetryService, configuration) {
|
|
|
78
85
|
contextProvider = provider
|
|
79
86
|
},
|
|
80
87
|
observable: observable,
|
|
81
|
-
enabled:
|
|
88
|
+
enabled: telemetryEnabled
|
|
82
89
|
}
|
|
83
90
|
}
|
|
91
|
+
function getRuntimeEnvInfo() {
|
|
92
|
+
return {
|
|
93
|
+
is_local_file: window.location.protocol === 'file:',
|
|
94
|
+
is_worker: 'WorkerGlobalScope' in self
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
export function startFakeTelemetry() {
|
|
98
|
+
const events = []
|
|
99
|
+
|
|
100
|
+
onRawTelemetryEventCollected = function (event) {
|
|
101
|
+
events.push(event)
|
|
102
|
+
}
|
|
84
103
|
|
|
104
|
+
return events
|
|
105
|
+
}
|
|
106
|
+
export function drainPreStartTelemetry() {
|
|
107
|
+
preStartTelemetryBuffer.drain()
|
|
108
|
+
}
|
|
85
109
|
export function resetTelemetry() {
|
|
86
|
-
|
|
110
|
+
preStartTelemetryBuffer = createBoundedBuffer()
|
|
111
|
+
onRawTelemetryEventCollected = function (event) {
|
|
112
|
+
preStartTelemetryBuffer.add(function () {
|
|
113
|
+
onRawTelemetryEventCollected(event)
|
|
114
|
+
})
|
|
115
|
+
}
|
|
87
116
|
}
|
|
88
117
|
|
|
89
118
|
export function addTelemetryDebug(message, context) {
|
|
90
|
-
displayIfDebugEnabled(
|
|
91
|
-
|
|
119
|
+
displayIfDebugEnabled(message, context)
|
|
120
|
+
onRawTelemetryEventCollected(
|
|
92
121
|
assign(
|
|
93
122
|
{
|
|
94
123
|
type: TelemetryType.log,
|
|
@@ -101,7 +130,7 @@ export function addTelemetryDebug(message, context) {
|
|
|
101
130
|
}
|
|
102
131
|
|
|
103
132
|
export function addTelemetryError(e, context) {
|
|
104
|
-
|
|
133
|
+
onRawTelemetryEventCollected(
|
|
105
134
|
assign(
|
|
106
135
|
{
|
|
107
136
|
type: TelemetryType.log,
|
|
@@ -114,23 +143,17 @@ export function addTelemetryError(e, context) {
|
|
|
114
143
|
}
|
|
115
144
|
|
|
116
145
|
export function addTelemetryConfiguration(configuration) {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
})
|
|
122
|
-
}
|
|
146
|
+
onRawTelemetryEventCollected({
|
|
147
|
+
type: TelemetryType.configuration,
|
|
148
|
+
configuration: configuration
|
|
149
|
+
})
|
|
123
150
|
}
|
|
124
151
|
|
|
125
|
-
function
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
) {
|
|
131
|
-
telemetryConfiguration.sentEventCount += 1
|
|
132
|
-
onRawTelemetryEventCollected(event)
|
|
133
|
-
}
|
|
152
|
+
export function addTelemetryUsage(usage) {
|
|
153
|
+
onRawTelemetryEventCollected({
|
|
154
|
+
type: TelemetryType.usage,
|
|
155
|
+
usage: usage
|
|
156
|
+
})
|
|
134
157
|
}
|
|
135
158
|
|
|
136
159
|
export function formatError(e) {
|
package/src/telemetry/types.js
CHANGED
package/src/transport/batch.js
CHANGED
|
@@ -22,7 +22,6 @@ import { isPageExitReason } from '../browser/pageExitObservable'
|
|
|
22
22
|
import { jsonStringify } from '../helper/serialisation/jsonStringify'
|
|
23
23
|
// https://en.wikipedia.org/wiki/UTF-8
|
|
24
24
|
// eslint-disable-next-line no-control-regex
|
|
25
|
-
var HAS_MULTI_BYTES_CHARACTERS = /[^\u0000-\u007F]/
|
|
26
25
|
var CUSTOM_KEYS = 'custom_keys'
|
|
27
26
|
export var processedMessageByDataMap = function (message) {
|
|
28
27
|
if (!message || !message.type)
|
|
@@ -128,109 +127,175 @@ export var processedMessageByDataMap = function (message) {
|
|
|
128
127
|
rowData: hasFileds ? rowData : undefined
|
|
129
128
|
}
|
|
130
129
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
var _this = this
|
|
144
|
-
this.flushController.flushObservable.subscribe(function (event) {
|
|
145
|
-
_this.flush(event)
|
|
130
|
+
|
|
131
|
+
export function createBatch(options) {
|
|
132
|
+
var encoder = options.encoder
|
|
133
|
+
var request = options.request
|
|
134
|
+
var messageBytesLimit = options.messageBytesLimit
|
|
135
|
+
var sendContentTypeByJson = options.sendContentTypeByJson
|
|
136
|
+
var flushController = options.flushController
|
|
137
|
+
var upsertBuffer = {}
|
|
138
|
+
var flushSubscription = flushController.flushObservable.subscribe(function (
|
|
139
|
+
event
|
|
140
|
+
) {
|
|
141
|
+
flush(event)
|
|
146
142
|
})
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
}
|
|
154
|
-
batch.prototype.flush = function (event) {
|
|
155
|
-
var messages = this.pushOnlyBuffer.concat(values(this.upsertBuffer))
|
|
156
|
-
this.pushOnlyBuffer = []
|
|
157
|
-
this.upsertBuffer = {}
|
|
158
|
-
if (messages.length > 0) {
|
|
159
|
-
var payloadData = ''
|
|
160
|
-
if (this.sendContentTypeByJson) {
|
|
161
|
-
payloadData = '[' + messages.join(',') + ']'
|
|
143
|
+
function getMessageText(messages, isEmpty = false) {
|
|
144
|
+
if (sendContentTypeByJson) {
|
|
145
|
+
if (isEmpty) {
|
|
146
|
+
return '[' + messages.join(',')
|
|
147
|
+
} else {
|
|
148
|
+
return ',' + messages.join(',')
|
|
149
|
+
}
|
|
162
150
|
} else {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
flushReason: event.reason
|
|
151
|
+
if (isEmpty) {
|
|
152
|
+
return messages.join('\n')
|
|
153
|
+
} else {
|
|
154
|
+
return '\n' + messages.join('\n')
|
|
155
|
+
}
|
|
169
156
|
}
|
|
170
|
-
|
|
171
|
-
|
|
157
|
+
}
|
|
158
|
+
function push(serializedMessage, estimatedMessageBytesCount, key) {
|
|
159
|
+
flushController.notifyBeforeAddMessage(estimatedMessageBytesCount)
|
|
160
|
+
|
|
161
|
+
if (key !== undefined) {
|
|
162
|
+
upsertBuffer[key] = serializedMessage
|
|
163
|
+
flushController.notifyAfterAddMessage()
|
|
172
164
|
} else {
|
|
173
|
-
|
|
165
|
+
encoder.write(
|
|
166
|
+
getMessageText([serializedMessage], encoder.isEmpty()),
|
|
167
|
+
function (realMessageBytesCount) {
|
|
168
|
+
flushController.notifyAfterAddMessage(
|
|
169
|
+
realMessageBytesCount - estimatedMessageBytesCount
|
|
170
|
+
)
|
|
171
|
+
}
|
|
172
|
+
)
|
|
174
173
|
}
|
|
175
174
|
}
|
|
176
|
-
}
|
|
177
175
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
var processedMessage = _process.processedMessage
|
|
181
|
-
var messageBytesCount = _process.messageBytesCount
|
|
182
|
-
if (messageBytesCount >= this.messageBytesLimit) {
|
|
183
|
-
display.warn(
|
|
184
|
-
'Discarded a message whose size was bigger than the maximum allowed size ' +
|
|
185
|
-
this.messageBytesLimit +
|
|
186
|
-
'KB.'
|
|
187
|
-
)
|
|
188
|
-
return
|
|
176
|
+
function hasMessageFor(key) {
|
|
177
|
+
return key !== undefined && upsertBuffer[key] !== undefined
|
|
189
178
|
}
|
|
190
|
-
|
|
191
|
-
|
|
179
|
+
|
|
180
|
+
function remove(key) {
|
|
181
|
+
var removedMessage = upsertBuffer[key]
|
|
182
|
+
delete upsertBuffer[key]
|
|
183
|
+
var messageBytesCount = encoder.estimateEncodedBytesCount(removedMessage)
|
|
184
|
+
flushController.notifyAfterRemoveMessage(messageBytesCount)
|
|
192
185
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
186
|
+
function process(message) {
|
|
187
|
+
var processedMessage = ''
|
|
188
|
+
if (sendContentTypeByJson) {
|
|
189
|
+
processedMessage = jsonStringify(
|
|
190
|
+
processedMessageByDataMap(message).rowData
|
|
191
|
+
)
|
|
192
|
+
} else {
|
|
193
|
+
processedMessage = processedMessageByDataMap(message).rowStr
|
|
194
|
+
}
|
|
195
|
+
return processedMessage
|
|
196
|
+
}
|
|
197
|
+
function addOrUpdate(message, key) {
|
|
198
|
+
const serializedMessage = process(message)
|
|
199
|
+
|
|
200
|
+
const estimatedMessageBytesCount =
|
|
201
|
+
encoder.estimateEncodedBytesCount(serializedMessage)
|
|
202
|
+
|
|
203
|
+
if (estimatedMessageBytesCount >= messageBytesLimit) {
|
|
204
|
+
display.warn(
|
|
205
|
+
`Discarded a message whose size was bigger than the maximum allowed size ${messageBytesLimit}KB.`
|
|
206
|
+
)
|
|
207
|
+
return
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (hasMessageFor(key)) {
|
|
211
|
+
remove(key)
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
push(serializedMessage, estimatedMessageBytesCount, key)
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function flush(event) {
|
|
218
|
+
var upsertMessages = values(upsertBuffer).join(
|
|
219
|
+
sendContentTypeByJson ? ',' : '\n'
|
|
220
|
+
)
|
|
221
|
+
upsertBuffer = {}
|
|
222
|
+
|
|
223
|
+
var isPageExit = isPageExitReason(event.reason)
|
|
224
|
+
var send = isPageExit ? request.sendOnExit : request.send
|
|
225
|
+
|
|
226
|
+
if (
|
|
227
|
+
isPageExit &&
|
|
228
|
+
// Note: checking that the encoder is async is not strictly needed, but it's an optimization:
|
|
229
|
+
// if the encoder is async we need to send two requests in some cases (one for encoded data
|
|
230
|
+
// and the other for non-encoded data). But if it's not async, we don't have to worry about
|
|
231
|
+
// it and always send a single request.
|
|
232
|
+
encoder.isAsync
|
|
233
|
+
) {
|
|
234
|
+
// 咱不支持json 模式
|
|
235
|
+
var encoderResult = encoder.finishSync()
|
|
236
|
+
|
|
237
|
+
// Send encoded messages
|
|
238
|
+
if (encoderResult.outputBytesCount) {
|
|
239
|
+
send(formatPayloadFromEncoder(encoderResult, sendContentTypeByJson))
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Send messages that are not yet encoded at this point
|
|
243
|
+
var pendingMessages = [encoderResult.pendingData, upsertMessages]
|
|
244
|
+
.filter(Boolean)
|
|
245
|
+
.join('\n')
|
|
246
|
+
|
|
247
|
+
if (pendingMessages) {
|
|
248
|
+
send({
|
|
249
|
+
data: pendingMessages,
|
|
250
|
+
bytesCount: computeBytesCount(pendingMessages)
|
|
251
|
+
})
|
|
252
|
+
}
|
|
253
|
+
} else {
|
|
254
|
+
if (upsertMessages) {
|
|
255
|
+
var text = getMessageText([upsertMessages], encoder.isEmpty())
|
|
256
|
+
if (sendContentTypeByJson) {
|
|
257
|
+
text += ']'
|
|
258
|
+
}
|
|
259
|
+
encoder.write(text)
|
|
260
|
+
} else {
|
|
261
|
+
if (sendContentTypeByJson) {
|
|
262
|
+
encoder.write(']')
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
encoder.finish(function (encoderResult) {
|
|
266
|
+
send(formatPayloadFromEncoder(encoderResult))
|
|
267
|
+
})
|
|
268
|
+
}
|
|
201
269
|
}
|
|
202
|
-
|
|
270
|
+
|
|
203
271
|
return {
|
|
204
|
-
|
|
205
|
-
|
|
272
|
+
flushController: flushController,
|
|
273
|
+
add: addOrUpdate,
|
|
274
|
+
upsert: addOrUpdate,
|
|
275
|
+
stop: flushSubscription.unsubscribe
|
|
206
276
|
}
|
|
207
277
|
}
|
|
208
278
|
|
|
209
|
-
|
|
210
|
-
var
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
)
|
|
214
|
-
if (key !== undefined) {
|
|
215
|
-
this.upsertBuffer[key] = processedMessage
|
|
279
|
+
function formatPayloadFromEncoder(encoderResult, sendContentTypeByJson) {
|
|
280
|
+
var data
|
|
281
|
+
if (typeof encoderResult.output === 'string') {
|
|
282
|
+
data = encoderResult.output
|
|
216
283
|
} else {
|
|
217
|
-
|
|
284
|
+
data = new Blob([encoderResult.output], {
|
|
285
|
+
// This will set the 'Content-Type: text/plain' header. Reasoning:
|
|
286
|
+
// * The intake rejects the request if there is no content type.
|
|
287
|
+
// * The browser will issue CORS preflight requests if we set it to 'application/json', which
|
|
288
|
+
// could induce higher intake load (and maybe has other impacts).
|
|
289
|
+
// * Also it's not quite JSON, since we are concatenating multiple JSON objects separated by
|
|
290
|
+
// new lines.
|
|
291
|
+
type: 'text/plain'
|
|
292
|
+
})
|
|
218
293
|
}
|
|
219
|
-
this.flushController.notifyAfterAddMessage()
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
batch.prototype.remove = function (key) {
|
|
223
|
-
var removedMessage = this.upsertBuffer[key]
|
|
224
|
-
delete this.upsertBuffer[key]
|
|
225
|
-
var messageBytesCount = computeBytesCount(removedMessage)
|
|
226
|
-
// If there are other messages, a '\n' will be added at serialization
|
|
227
|
-
var separatorBytesCount = this.flushController.getMessagesCount() > 1 ? 1 : 0
|
|
228
|
-
this.flushController.notifyAfterRemoveMessage(
|
|
229
|
-
messageBytesCount + separatorBytesCount
|
|
230
|
-
)
|
|
231
|
-
}
|
|
232
294
|
|
|
233
|
-
|
|
234
|
-
|
|
295
|
+
return {
|
|
296
|
+
data: data,
|
|
297
|
+
type: sendContentTypeByJson ? 'application/json;UTF-8' : undefined,
|
|
298
|
+
bytesCount: encoderResult.outputBytesCount,
|
|
299
|
+
encoding: encoderResult.encoding
|
|
300
|
+
}
|
|
235
301
|
}
|
|
236
|
-
export var Batch = batch
|
|
@@ -15,14 +15,18 @@ export function createFlushController({
|
|
|
15
15
|
pageExitObservable,
|
|
16
16
|
sessionExpireObservable
|
|
17
17
|
}) {
|
|
18
|
-
var flushObservable = new Observable()
|
|
19
|
-
|
|
20
18
|
pageExitObservable.subscribe(function (event) {
|
|
21
19
|
return flush(event.reason)
|
|
22
20
|
})
|
|
23
21
|
sessionExpireObservable.subscribe(function () {
|
|
24
22
|
return flush('session_expire')
|
|
25
23
|
})
|
|
24
|
+
var flushObservable = new Observable(function () {
|
|
25
|
+
return function () {
|
|
26
|
+
pageExitSubscription.unsubscribe()
|
|
27
|
+
sessionExpireSubscription.unsubscribe()
|
|
28
|
+
}
|
|
29
|
+
})
|
|
26
30
|
|
|
27
31
|
var currentBytesCount = 0
|
|
28
32
|
var currentMessagesCount = 0
|
|
@@ -72,15 +76,15 @@ export function createFlushController({
|
|
|
72
76
|
* This function needs to be called synchronously, right before adding the message, so no flush
|
|
73
77
|
* event can happen after `notifyBeforeAddMessage` and before adding the message.
|
|
74
78
|
*/
|
|
75
|
-
notifyBeforeAddMessage: function (
|
|
76
|
-
if (currentBytesCount +
|
|
79
|
+
notifyBeforeAddMessage: function (estimatedMessageBytesCount) {
|
|
80
|
+
if (currentBytesCount + estimatedMessageBytesCount >= bytesLimit) {
|
|
77
81
|
flush('bytes_limit')
|
|
78
82
|
}
|
|
79
83
|
// Consider the message to be added now rather than in `notifyAfterAddMessage`, because if no
|
|
80
84
|
// message was added yet and `notifyAfterAddMessage` is called asynchronously, we still want
|
|
81
85
|
// to notify when a flush is needed (for example on page exit).
|
|
82
86
|
currentMessagesCount += 1
|
|
83
|
-
currentBytesCount +=
|
|
87
|
+
currentBytesCount += estimatedMessageBytesCount
|
|
84
88
|
scheduleDurationLimitTimeout()
|
|
85
89
|
},
|
|
86
90
|
|
|
@@ -90,7 +94,12 @@ export function createFlushController({
|
|
|
90
94
|
* This function can be called asynchronously after the message was added, but in this case it
|
|
91
95
|
* should not be called if a flush event occurred in between.
|
|
92
96
|
*/
|
|
93
|
-
notifyAfterAddMessage: function () {
|
|
97
|
+
notifyAfterAddMessage: function (messageBytesCountDiff) {
|
|
98
|
+
if (messageBytesCountDiff === undefined) {
|
|
99
|
+
messageBytesCountDiff = 0
|
|
100
|
+
}
|
|
101
|
+
currentBytesCount += messageBytesCountDiff
|
|
102
|
+
|
|
94
103
|
if (currentMessagesCount >= messagesLimit) {
|
|
95
104
|
flush('messages_limit')
|
|
96
105
|
} else if (currentBytesCount >= bytesLimit) {
|
|
@@ -103,6 +112,10 @@ export function createFlushController({
|
|
|
103
112
|
*
|
|
104
113
|
* This function needs to be called synchronously, right after removing the message, so no flush
|
|
105
114
|
* event can happen after removing the message and before `notifyAfterRemoveMessage`.
|
|
115
|
+
*
|
|
116
|
+
* @param messageBytesCount: the message bytes count that was added to the pool. Should
|
|
117
|
+
* correspond to the sum of bytes counts passed to `notifyBeforeAddMessage` and
|
|
118
|
+
* `notifyAfterAddMessage`.
|
|
106
119
|
*/
|
|
107
120
|
notifyAfterRemoveMessage: function (messageBytesCount) {
|
|
108
121
|
currentBytesCount -= messageBytesCount
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { newRetryState, sendWithRetryStrategy } from './sendWithRetryStrategy'
|
|
2
2
|
import { addEventListener } from '../browser/addEventListener'
|
|
3
3
|
import { monitor } from '../helper/monitor'
|
|
4
|
+
import { addTelemetryError } from '../telemetry/telemetry'
|
|
4
5
|
/**
|
|
5
6
|
* Use POST request without content type to:
|
|
6
7
|
* - avoid CORS preflight requests
|
|
@@ -9,28 +10,18 @@ import { monitor } from '../helper/monitor'
|
|
|
9
10
|
* multiple elements are sent separated by \n in order
|
|
10
11
|
* to be parsed correctly without content type header
|
|
11
12
|
*/
|
|
12
|
-
function addBatchPrecision(url) {
|
|
13
|
+
function addBatchPrecision(url, encoding) {
|
|
13
14
|
if (!url) return url
|
|
14
|
-
|
|
15
|
+
url = url + (url.indexOf('?') === -1 ? '?' : '&') + 'precision=ms'
|
|
16
|
+
if (encoding) {
|
|
17
|
+
url = url + '&encoding=' + encoding
|
|
18
|
+
}
|
|
19
|
+
return url
|
|
15
20
|
}
|
|
16
|
-
export function createHttpRequest(
|
|
17
|
-
endpointUrl,
|
|
18
|
-
bytesLimit,
|
|
19
|
-
sendContentTypeByJson,
|
|
20
|
-
reportError
|
|
21
|
-
) {
|
|
22
|
-
var contentType = sendContentTypeByJson
|
|
23
|
-
? 'application/json; charset=UTF-8'
|
|
24
|
-
: undefined
|
|
21
|
+
export function createHttpRequest(endpointUrl, bytesLimit, reportError) {
|
|
25
22
|
var retryState = newRetryState()
|
|
26
23
|
var sendStrategyForRetry = function (payload, onResponse) {
|
|
27
|
-
return fetchKeepAliveStrategy(
|
|
28
|
-
endpointUrl,
|
|
29
|
-
bytesLimit,
|
|
30
|
-
contentType,
|
|
31
|
-
payload,
|
|
32
|
-
onResponse
|
|
33
|
-
)
|
|
24
|
+
return fetchKeepAliveStrategy(endpointUrl, bytesLimit, payload, onResponse)
|
|
34
25
|
}
|
|
35
26
|
|
|
36
27
|
return {
|
|
@@ -48,22 +39,22 @@ export function createHttpRequest(
|
|
|
48
39
|
* keep using sendBeaconStrategy on exit
|
|
49
40
|
*/
|
|
50
41
|
sendOnExit: function (payload) {
|
|
51
|
-
sendBeaconStrategy(endpointUrl, bytesLimit,
|
|
42
|
+
sendBeaconStrategy(endpointUrl, bytesLimit, payload)
|
|
52
43
|
}
|
|
53
44
|
}
|
|
54
45
|
}
|
|
55
46
|
|
|
56
|
-
function sendBeaconStrategy(endpointUrl, bytesLimit,
|
|
47
|
+
function sendBeaconStrategy(endpointUrl, bytesLimit, payload) {
|
|
57
48
|
var data = payload.data
|
|
58
49
|
var bytesCount = payload.bytesCount
|
|
59
|
-
var url = addBatchPrecision(endpointUrl)
|
|
50
|
+
var url = addBatchPrecision(endpointUrl, payload.encoding)
|
|
60
51
|
var canUseBeacon = !!navigator.sendBeacon && bytesCount < bytesLimit
|
|
61
52
|
if (canUseBeacon) {
|
|
62
53
|
try {
|
|
63
54
|
var beaconData
|
|
64
|
-
if (
|
|
55
|
+
if (payload.type) {
|
|
65
56
|
beaconData = new Blob([data], {
|
|
66
|
-
type:
|
|
57
|
+
type: payload.type
|
|
67
58
|
})
|
|
68
59
|
} else {
|
|
69
60
|
beaconData = data
|
|
@@ -75,22 +66,28 @@ function sendBeaconStrategy(endpointUrl, bytesLimit, contentType, payload) {
|
|
|
75
66
|
return
|
|
76
67
|
}
|
|
77
68
|
} catch (e) {
|
|
78
|
-
|
|
69
|
+
reportBeaconError(e)
|
|
79
70
|
}
|
|
80
71
|
}
|
|
81
|
-
sendXHR(url,
|
|
72
|
+
sendXHR(url, payload)
|
|
82
73
|
}
|
|
74
|
+
var hasReportedBeaconError = false
|
|
83
75
|
|
|
76
|
+
function reportBeaconError(e) {
|
|
77
|
+
if (!hasReportedBeaconError) {
|
|
78
|
+
hasReportedBeaconError = true
|
|
79
|
+
addTelemetryError(e)
|
|
80
|
+
}
|
|
81
|
+
}
|
|
84
82
|
export function fetchKeepAliveStrategy(
|
|
85
83
|
endpointUrl,
|
|
86
84
|
bytesLimit,
|
|
87
|
-
contentType,
|
|
88
85
|
payload,
|
|
89
86
|
onResponse
|
|
90
87
|
) {
|
|
91
88
|
var data = payload.data
|
|
92
89
|
var bytesCount = payload.bytesCount
|
|
93
|
-
var url = addBatchPrecision(endpointUrl)
|
|
90
|
+
var url = addBatchPrecision(endpointUrl, payload.encoding)
|
|
94
91
|
var canUseKeepAlive = isKeepAliveSupported() && bytesCount < bytesLimit
|
|
95
92
|
if (canUseKeepAlive) {
|
|
96
93
|
var fetchOption = {
|
|
@@ -99,9 +96,9 @@ export function fetchKeepAliveStrategy(
|
|
|
99
96
|
keepalive: true,
|
|
100
97
|
mode: 'cors'
|
|
101
98
|
}
|
|
102
|
-
if (
|
|
99
|
+
if (payload.type) {
|
|
103
100
|
fetchOption.headers = {
|
|
104
|
-
'Content-Type':
|
|
101
|
+
'Content-Type': payload.type
|
|
105
102
|
}
|
|
106
103
|
}
|
|
107
104
|
fetch(url, fetchOption).then(
|
|
@@ -112,11 +109,11 @@ export function fetchKeepAliveStrategy(
|
|
|
112
109
|
}),
|
|
113
110
|
monitor(function () {
|
|
114
111
|
// failed to queue the request
|
|
115
|
-
sendXHR(url,
|
|
112
|
+
sendXHR(url, payload, onResponse)
|
|
116
113
|
})
|
|
117
114
|
)
|
|
118
115
|
} else {
|
|
119
|
-
sendXHR(url,
|
|
116
|
+
sendXHR(url, payload, onResponse)
|
|
120
117
|
}
|
|
121
118
|
}
|
|
122
119
|
|
|
@@ -129,12 +126,14 @@ function isKeepAliveSupported() {
|
|
|
129
126
|
}
|
|
130
127
|
}
|
|
131
128
|
|
|
132
|
-
function sendXHR(url,
|
|
129
|
+
function sendXHR(url, payload, onResponse) {
|
|
130
|
+
const data = payload.data
|
|
133
131
|
const request = new XMLHttpRequest()
|
|
134
132
|
request.open('POST', url, true)
|
|
135
|
-
if (
|
|
136
|
-
|
|
137
|
-
|
|
133
|
+
if (data instanceof Blob) {
|
|
134
|
+
request.setRequestHeader('Content-Type', data.type)
|
|
135
|
+
} else if (payload.type) {
|
|
136
|
+
request.setRequestHeader('Content-Type', payload.type)
|
|
138
137
|
}
|
|
139
138
|
|
|
140
139
|
addEventListener(
|
package/src/transport/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { createHttpRequest } from './httpRequest'
|
|
2
|
-
export {
|
|
2
|
+
export { createBatch, processedMessageByDataMap } from './batch'
|
|
3
3
|
export { startBatchWithReplica } from './startBatchWithReplica'
|
|
4
4
|
export { createFlushController } from './flushController'
|
|
5
5
|
export { getEventBridge, canUseEventBridge } from './eventBridge'
|