@cloudcare/browser-core 2.0.3 → 2.0.5
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/addEventListener.js +57 -0
- package/cjs/browser/pageExitObservable.js +4 -2
- package/cjs/browser/runOnReadyState.js +21 -0
- package/cjs/browser/xhrObservable.js +7 -5
- package/cjs/configuration/configuration.js +5 -3
- package/cjs/console/consoleObservable.js +3 -1
- package/cjs/dataMap.js +1 -0
- package/cjs/helper/byteUtils.js +26 -0
- package/cjs/helper/contextHistory.js +4 -2
- package/cjs/helper/createEventRateLimiter.js +3 -1
- package/cjs/helper/errorTools.js +4 -2
- package/cjs/helper/instrumentMethod.js +2 -4
- package/cjs/helper/serialisation/contextManager.js +84 -0
- package/cjs/helper/serialisation/heavyCustomerDataWarning.js +34 -0
- package/cjs/helper/serialisation/jsonStringify.js +57 -0
- package/cjs/helper/serialisation/rowData.js +50 -0
- package/cjs/helper/timer.js +29 -0
- package/cjs/helper/tools.js +10 -331
- package/cjs/index.js +117 -0
- package/cjs/init.js +6 -6
- package/cjs/report/reportObservable.js +4 -2
- package/cjs/session/sessionCookieStore.js +3 -1
- package/cjs/session/sessionManagement.js +10 -5
- package/cjs/session/sessionStore.js +10 -4
- package/cjs/transport/batch.js +40 -91
- package/cjs/transport/flushController.js +121 -0
- package/cjs/transport/httpRequest.js +6 -2
- package/cjs/transport/index.js +9 -1
- package/cjs/transport/sendWithRetryStrategy.js +8 -4
- package/cjs/transport/startBatchWithReplica.js +10 -2
- package/cjs/user/index.js +18 -0
- package/cjs/user/user.js +42 -0
- package/esm/browser/addEventListener.js +46 -0
- package/esm/browser/pageExitObservable.js +2 -1
- package/esm/browser/runOnReadyState.js +12 -0
- package/esm/browser/xhrObservable.js +6 -5
- package/esm/configuration/configuration.js +2 -1
- package/esm/console/consoleObservable.js +2 -1
- package/esm/dataMap.js +1 -0
- package/esm/helper/byteUtils.js +16 -0
- package/esm/helper/contextHistory.js +1 -0
- package/esm/helper/createEventRateLimiter.js +1 -0
- package/esm/helper/errorTools.js +2 -1
- package/esm/helper/instrumentMethod.js +1 -3
- package/esm/helper/serialisation/contextManager.js +70 -0
- package/esm/helper/serialisation/heavyCustomerDataWarning.js +20 -0
- package/esm/helper/serialisation/jsonStringify.js +46 -0
- package/esm/helper/serialisation/rowData.js +33 -0
- package/esm/helper/timer.js +14 -0
- package/esm/helper/tools.js +1 -292
- package/esm/index.js +10 -1
- package/esm/init.js +6 -6
- package/esm/report/reportObservable.js +3 -2
- package/esm/session/sessionCookieStore.js +1 -0
- package/esm/session/sessionManagement.js +5 -2
- package/esm/session/sessionStore.js +7 -2
- package/esm/transport/batch.js +31 -85
- package/esm/transport/flushController.js +114 -0
- package/esm/transport/httpRequest.js +5 -2
- package/esm/transport/index.js +2 -1
- package/esm/transport/sendWithRetryStrategy.js +3 -1
- package/esm/transport/startBatchWithReplica.js +9 -2
- package/esm/user/index.js +1 -0
- package/esm/user/user.js +32 -0
- package/package.json +22 -22
- package/src/browser/addEventListener.js +50 -0
- package/src/browser/pageExitObservable.js +4 -5
- package/src/browser/runOnReadyState.js +16 -0
- package/src/browser/xhrObservable.js +57 -32
- package/src/configuration/configuration.js +1 -1
- package/src/console/consoleObservable.js +22 -12
- package/src/dataMap.js +1 -0
- package/src/helper/byteUtils.js +17 -0
- package/src/helper/contextHistory.js +1 -1
- package/src/helper/createEventRateLimiter.js +7 -9
- package/src/helper/errorTools.js +2 -1
- package/src/helper/instrumentMethod.js +1 -2
- package/src/helper/serialisation/contextManager.js +79 -0
- package/src/helper/serialisation/heavyCustomerDataWarning.js +28 -0
- package/src/helper/serialisation/jsonStringify.js +47 -0
- package/src/helper/serialisation/rowData.js +41 -0
- package/src/helper/timer.js +21 -0
- package/src/helper/tools.js +2 -301
- package/src/index.js +11 -0
- package/src/init.js +6 -6
- package/src/report/reportObservable.js +40 -25
- package/src/session/sessionCookieStore.js +24 -14
- package/src/session/sessionManagement.js +34 -17
- package/src/session/sessionStore.js +7 -2
- package/src/transport/batch.js +39 -91
- package/src/transport/flushController.js +115 -0
- package/src/transport/httpRequest.js +13 -6
- package/src/transport/index.js +1 -0
- package/src/transport/sendWithRetryStrategy.js +3 -7
- package/src/transport/startBatchWithReplica.js +11 -7
- package/src/user/index.js +1 -0
- package/src/user/user.js +31 -0
|
@@ -1,63 +1,68 @@
|
|
|
1
|
-
import { relativeNow, clocksOrigin, ONE_MINUTE, each
|
|
1
|
+
import { relativeNow, clocksOrigin, ONE_MINUTE, each } from '../helper/tools'
|
|
2
|
+
import {
|
|
3
|
+
addEventListeners,
|
|
4
|
+
addEventListener
|
|
5
|
+
} from '../browser/addEventListener'
|
|
2
6
|
import { ContextHistory } from '../helper/contextHistory'
|
|
3
7
|
import { startSessionStore } from './sessionStore'
|
|
4
8
|
import { SESSION_TIME_OUT_DELAY } from './sessionConstants'
|
|
5
9
|
import { DOM_EVENT } from '../helper/enums'
|
|
6
|
-
|
|
10
|
+
import { clearInterval, setInterval } from '../helper/timer'
|
|
7
11
|
export var VISIBILITY_CHECK_DELAY = ONE_MINUTE
|
|
8
12
|
var SESSION_CONTEXT_TIMEOUT_DELAY = SESSION_TIME_OUT_DELAY
|
|
9
13
|
var stopCallbacks = []
|
|
10
14
|
|
|
11
|
-
export var startSessionManager = function(
|
|
15
|
+
export var startSessionManager = function (
|
|
12
16
|
options,
|
|
13
17
|
productKey,
|
|
14
18
|
computeSessionState
|
|
15
19
|
) {
|
|
16
20
|
var sessionStore = startSessionStore(options, productKey, computeSessionState)
|
|
17
|
-
stopCallbacks.push(function() {
|
|
21
|
+
stopCallbacks.push(function () {
|
|
18
22
|
return sessionStore.stop()
|
|
19
23
|
})
|
|
20
24
|
|
|
21
25
|
var sessionContextHistory = new ContextHistory(SESSION_CONTEXT_TIMEOUT_DELAY)
|
|
22
|
-
stopCallbacks.push(function() {
|
|
26
|
+
stopCallbacks.push(function () {
|
|
23
27
|
return sessionContextHistory.stop()
|
|
24
28
|
})
|
|
25
29
|
|
|
26
|
-
sessionStore.renewObservable.subscribe(function(){
|
|
30
|
+
sessionStore.renewObservable.subscribe(function () {
|
|
27
31
|
sessionContextHistory.add(buildSessionContext(), relativeNow())
|
|
28
32
|
})
|
|
29
|
-
sessionStore.expireObservable.subscribe(function() {
|
|
33
|
+
sessionStore.expireObservable.subscribe(function () {
|
|
30
34
|
sessionContextHistory.closeActive(relativeNow())
|
|
31
35
|
})
|
|
32
36
|
|
|
33
37
|
sessionStore.expandOrRenewSession()
|
|
34
38
|
sessionContextHistory.add(buildSessionContext(), clocksOrigin().relative)
|
|
35
39
|
|
|
36
|
-
trackActivity(function() {
|
|
40
|
+
trackActivity(function () {
|
|
37
41
|
return sessionStore.expandOrRenewSession()
|
|
38
42
|
})
|
|
39
|
-
trackVisibility(function() {
|
|
43
|
+
trackVisibility(function () {
|
|
40
44
|
return sessionStore.expandSession()
|
|
41
45
|
})
|
|
42
46
|
|
|
43
47
|
function buildSessionContext() {
|
|
44
48
|
return {
|
|
45
49
|
id: sessionStore.getSession().id,
|
|
46
|
-
trackingType: sessionStore.getSession()[productKey]
|
|
50
|
+
trackingType: sessionStore.getSession()[productKey]
|
|
47
51
|
}
|
|
48
52
|
}
|
|
49
53
|
|
|
50
54
|
return {
|
|
51
|
-
findActiveSession: function(startTime) {
|
|
55
|
+
findActiveSession: function (startTime) {
|
|
52
56
|
return sessionContextHistory.find(startTime)
|
|
53
57
|
},
|
|
54
58
|
renewObservable: sessionStore.renewObservable,
|
|
55
59
|
expireObservable: sessionStore.expireObservable,
|
|
60
|
+
expire: sessionStore.expire
|
|
56
61
|
}
|
|
57
62
|
}
|
|
58
63
|
|
|
59
64
|
export function stopSessionManager() {
|
|
60
|
-
each(stopCallbacks, function(e) {
|
|
65
|
+
each(stopCallbacks, function (e) {
|
|
61
66
|
return e()
|
|
62
67
|
})
|
|
63
68
|
stopCallbacks = []
|
|
@@ -66,7 +71,12 @@ export function stopSessionManager() {
|
|
|
66
71
|
function trackActivity(expandOrRenewSession) {
|
|
67
72
|
var _addEventListeners = addEventListeners(
|
|
68
73
|
window,
|
|
69
|
-
[
|
|
74
|
+
[
|
|
75
|
+
DOM_EVENT.CLICK,
|
|
76
|
+
DOM_EVENT.TOUCH_START,
|
|
77
|
+
DOM_EVENT.KEY_DOWN,
|
|
78
|
+
DOM_EVENT.SCROLL
|
|
79
|
+
],
|
|
70
80
|
expandOrRenewSession,
|
|
71
81
|
{ capture: true, passive: true }
|
|
72
82
|
)
|
|
@@ -74,16 +84,23 @@ function trackActivity(expandOrRenewSession) {
|
|
|
74
84
|
}
|
|
75
85
|
|
|
76
86
|
function trackVisibility(expandSession) {
|
|
77
|
-
var expandSessionWhenVisible = function() {
|
|
87
|
+
var expandSessionWhenVisible = function () {
|
|
78
88
|
if (document.visibilityState === 'visible') {
|
|
79
89
|
expandSession()
|
|
80
90
|
}
|
|
81
91
|
}
|
|
82
|
-
var _addEventListener= addEventListener(
|
|
92
|
+
var _addEventListener = addEventListener(
|
|
93
|
+
document,
|
|
94
|
+
DOM_EVENT.VISIBILITY_CHANGE,
|
|
95
|
+
expandSessionWhenVisible
|
|
96
|
+
)
|
|
83
97
|
stopCallbacks.push(_addEventListener.stop)
|
|
84
98
|
|
|
85
|
-
var visibilityCheckInterval = setInterval(
|
|
86
|
-
|
|
99
|
+
var visibilityCheckInterval = setInterval(
|
|
100
|
+
expandSessionWhenVisible,
|
|
101
|
+
VISIBILITY_CHECK_DELAY
|
|
102
|
+
)
|
|
103
|
+
stopCallbacks.push(function () {
|
|
87
104
|
clearInterval(visibilityCheckInterval)
|
|
88
105
|
})
|
|
89
106
|
}
|
|
@@ -3,6 +3,7 @@ import { Observable } from '../helper/observable'
|
|
|
3
3
|
import { dateNow, UUID, throttle } from '../helper/tools'
|
|
4
4
|
import { SESSION_TIME_OUT_DELAY } from './sessionConstants'
|
|
5
5
|
import { retrieveSession, withCookieLockAccess } from './sessionCookieStore'
|
|
6
|
+
import { clearInterval, setInterval } from '../helper/timer'
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* Different session concepts:
|
|
@@ -67,7 +68,7 @@ export function startSessionStore(options, productKey, computeSessionState) {
|
|
|
67
68
|
}
|
|
68
69
|
if (hasSessionInCache()) {
|
|
69
70
|
if (isSessionInCacheOutdated(cookieSession)) {
|
|
70
|
-
|
|
71
|
+
expireSessionInCache()
|
|
71
72
|
} else {
|
|
72
73
|
sessionCache = cookieSession
|
|
73
74
|
}
|
|
@@ -98,7 +99,7 @@ export function startSessionStore(options, productKey, computeSessionState) {
|
|
|
98
99
|
)
|
|
99
100
|
}
|
|
100
101
|
|
|
101
|
-
function
|
|
102
|
+
function expireSessionInCache() {
|
|
102
103
|
sessionCache = {}
|
|
103
104
|
expireObservable.notify()
|
|
104
105
|
}
|
|
@@ -135,6 +136,10 @@ export function startSessionStore(options, productKey, computeSessionState) {
|
|
|
135
136
|
},
|
|
136
137
|
renewObservable: renewObservable,
|
|
137
138
|
expireObservable: expireObservable,
|
|
139
|
+
expire: function () {
|
|
140
|
+
deleteSessionCookie(options)
|
|
141
|
+
synchronizeSession({})
|
|
142
|
+
},
|
|
138
143
|
stop: function () {
|
|
139
144
|
clearInterval(watchSessionTimeoutId)
|
|
140
145
|
}
|
package/src/transport/batch.js
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import { display } from '../helper/display'
|
|
2
2
|
import {
|
|
3
|
-
addEventListener,
|
|
4
|
-
noop,
|
|
5
3
|
values,
|
|
6
4
|
findByPath,
|
|
7
|
-
escapeRowData,
|
|
8
5
|
each,
|
|
9
6
|
isNumber,
|
|
10
7
|
isArray,
|
|
@@ -13,12 +10,17 @@ import {
|
|
|
13
10
|
toServerDuration,
|
|
14
11
|
isBoolean,
|
|
15
12
|
isEmptyObject,
|
|
16
|
-
isObject
|
|
17
|
-
escapeJsonValue,
|
|
18
|
-
escapeRowField
|
|
13
|
+
isObject
|
|
19
14
|
} from '../helper/tools'
|
|
15
|
+
import {
|
|
16
|
+
escapeJsonValue,
|
|
17
|
+
escapeRowField,
|
|
18
|
+
escapeRowData
|
|
19
|
+
} from '../helper/serialisation/rowData'
|
|
20
20
|
import { commonTags, dataMap, commonFields } from '../dataMap'
|
|
21
|
-
import {
|
|
21
|
+
import { RumEventType } from '../helper/enums'
|
|
22
|
+
import { computeBytesCount } from '../helper/byteUtils'
|
|
23
|
+
import { isPageExitReason } from '../browser/pageExitObservable'
|
|
22
24
|
// https://en.wikipedia.org/wiki/UTF-8
|
|
23
25
|
// eslint-disable-next-line no-control-regex
|
|
24
26
|
var HAS_MULTI_BYTES_CHARACTERS = /[^\u0000-\u007F]/
|
|
@@ -133,28 +135,16 @@ export var processedMessageByDataMap = function (message) {
|
|
|
133
135
|
rowData: hasFileds ? rowData : undefined
|
|
134
136
|
}
|
|
135
137
|
}
|
|
136
|
-
var batch = function (
|
|
137
|
-
request,
|
|
138
|
-
batchMessagesLimit,
|
|
139
|
-
batchBytesLimit,
|
|
140
|
-
messageBytesLimit,
|
|
141
|
-
flushTimeout,
|
|
142
|
-
pageExitObservable
|
|
143
|
-
) {
|
|
138
|
+
var batch = function (request, flushController, messageBytesLimit) {
|
|
144
139
|
this.pushOnlyBuffer = []
|
|
145
140
|
this.upsertBuffer = {}
|
|
146
|
-
this.bufferBytesCount = 0
|
|
147
|
-
this.bufferMessagesCount = 0
|
|
148
141
|
this.request = request
|
|
149
|
-
this.
|
|
150
|
-
this.batchBytesLimit = batchBytesLimit
|
|
142
|
+
this.flushController = flushController
|
|
151
143
|
this.messageBytesLimit = messageBytesLimit
|
|
152
|
-
this.flushTimeout = flushTimeout
|
|
153
144
|
var _this = this
|
|
154
|
-
|
|
155
|
-
_this.flush(
|
|
145
|
+
this.flushController.flushObservable.subscribe(function (event) {
|
|
146
|
+
_this.flush(event)
|
|
156
147
|
})
|
|
157
|
-
this.flushPeriodically()
|
|
158
148
|
}
|
|
159
149
|
batch.prototype.add = function (message) {
|
|
160
150
|
this.addOrUpdate(message)
|
|
@@ -162,37 +152,24 @@ batch.prototype.add = function (message) {
|
|
|
162
152
|
batch.prototype.upsert = function (message, key) {
|
|
163
153
|
this.addOrUpdate(message, key)
|
|
164
154
|
}
|
|
165
|
-
batch.prototype.flush = function (
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
}
|
|
169
|
-
if (
|
|
170
|
-
var
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
155
|
+
batch.prototype.flush = function (event) {
|
|
156
|
+
var messages = this.pushOnlyBuffer.concat(values(this.upsertBuffer))
|
|
157
|
+
this.pushOnlyBuffer = []
|
|
158
|
+
this.upsertBuffer = {}
|
|
159
|
+
if (messages.length > 0) {
|
|
160
|
+
var payload = {
|
|
161
|
+
data: messages.join('\n'),
|
|
162
|
+
bytesCount: event.bytesCount,
|
|
163
|
+
flushReason: event.reason
|
|
164
|
+
}
|
|
165
|
+
if (isPageExitReason(event.reason)) {
|
|
166
|
+
this.request.sendOnExit(payload)
|
|
167
|
+
} else {
|
|
168
|
+
this.request.send(payload)
|
|
178
169
|
}
|
|
179
170
|
}
|
|
180
171
|
}
|
|
181
|
-
batch.prototype.flushOnExit = function () {
|
|
182
|
-
this.flush(this.request.sendOnExit)
|
|
183
|
-
}
|
|
184
|
-
batch.prototype.computeBytesCount = function (candidate) {
|
|
185
|
-
// Accurate bytes count computations can degrade performances when there is a lot of events to process
|
|
186
|
-
if (!HAS_MULTI_BYTES_CHARACTERS.test(candidate)) {
|
|
187
|
-
return candidate.length
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
if (window.TextEncoder !== undefined) {
|
|
191
|
-
return new TextEncoder().encode(candidate).length
|
|
192
|
-
}
|
|
193
172
|
|
|
194
|
-
return new Blob([candidate]).size
|
|
195
|
-
}
|
|
196
173
|
batch.prototype.addOrUpdate = function (message, key) {
|
|
197
174
|
var _process = this.process(message)
|
|
198
175
|
var processedMessage = _process.processedMessage
|
|
@@ -208,18 +185,11 @@ batch.prototype.addOrUpdate = function (message, key) {
|
|
|
208
185
|
if (this.hasMessageFor(key)) {
|
|
209
186
|
this.remove(key)
|
|
210
187
|
}
|
|
211
|
-
if (this.willReachedBytesLimitWith(messageBytesCount)) {
|
|
212
|
-
this.flush()
|
|
213
|
-
}
|
|
214
|
-
|
|
215
188
|
this.push(processedMessage, messageBytesCount, key)
|
|
216
|
-
if (this.isFull()) {
|
|
217
|
-
this.flush()
|
|
218
|
-
}
|
|
219
189
|
}
|
|
220
190
|
batch.prototype.process = function (message) {
|
|
221
191
|
var processedMessage = processedMessageByDataMap(message).rowStr
|
|
222
|
-
var messageBytesCount =
|
|
192
|
+
var messageBytesCount = computeBytesCount(processedMessage)
|
|
223
193
|
return {
|
|
224
194
|
processedMessage: processedMessage,
|
|
225
195
|
messageBytesCount: messageBytesCount
|
|
@@ -227,52 +197,30 @@ batch.prototype.process = function (message) {
|
|
|
227
197
|
}
|
|
228
198
|
|
|
229
199
|
batch.prototype.push = function (processedMessage, messageBytesCount, key) {
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
200
|
+
var separatorBytesCount = this.flushController.getMessagesCount() > 0 ? 1 : 0
|
|
201
|
+
this.flushController.notifyBeforeAddMessage(
|
|
202
|
+
messageBytesCount + separatorBytesCount
|
|
203
|
+
)
|
|
234
204
|
if (key !== undefined) {
|
|
235
205
|
this.upsertBuffer[key] = processedMessage
|
|
236
206
|
} else {
|
|
237
207
|
this.pushOnlyBuffer.push(processedMessage)
|
|
238
208
|
}
|
|
239
|
-
this.
|
|
240
|
-
this.bufferMessagesCount += 1
|
|
209
|
+
this.flushController.notifyAfterAddMessage()
|
|
241
210
|
}
|
|
242
211
|
|
|
243
212
|
batch.prototype.remove = function (key) {
|
|
244
213
|
var removedMessage = this.upsertBuffer[key]
|
|
245
214
|
delete this.upsertBuffer[key]
|
|
246
|
-
var messageBytesCount =
|
|
247
|
-
|
|
248
|
-
this.
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
215
|
+
var messageBytesCount = computeBytesCount(removedMessage)
|
|
216
|
+
// If there are other messages, a '\n' will be added at serialization
|
|
217
|
+
var separatorBytesCount = this.flushController.getMessagesCount() > 1 ? 1 : 0
|
|
218
|
+
this.flushController.notifyAfterRemoveMessage(
|
|
219
|
+
messageBytesCount + separatorBytesCount
|
|
220
|
+
)
|
|
252
221
|
}
|
|
253
222
|
|
|
254
223
|
batch.prototype.hasMessageFor = function (key) {
|
|
255
224
|
return key !== undefined && this.upsertBuffer[key] !== undefined
|
|
256
225
|
}
|
|
257
|
-
|
|
258
|
-
batch.prototype.willReachedBytesLimitWith = function (messageBytesCount) {
|
|
259
|
-
// byte of the separator at the end of the message
|
|
260
|
-
return this.bufferBytesCount + messageBytesCount + 1 >= this.batchBytesLimit
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
batch.prototype.isFull = function () {
|
|
264
|
-
return (
|
|
265
|
-
this.bufferMessagesCount === this.batchMessagesLimit ||
|
|
266
|
-
this.bufferBytesCount >= this.batchBytesLimit
|
|
267
|
-
)
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
batch.prototype.flushPeriodically = function () {
|
|
271
|
-
var _this = this
|
|
272
|
-
setTimeout(function () {
|
|
273
|
-
_this.flush()
|
|
274
|
-
_this.flushPeriodically()
|
|
275
|
-
}, this.flushTimeout)
|
|
276
|
-
}
|
|
277
|
-
|
|
278
226
|
export var Batch = batch
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { Observable } from '../helper/observable'
|
|
2
|
+
import { clearTimeout, setTimeout } from '../helper/timer'
|
|
3
|
+
|
|
4
|
+
// type FlushReason = PageExitReason | 'duration_limit' | 'bytes_limit' | 'messages_limit' | 'session_expire'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Returns a "flush controller", responsible of notifying when flushing a pool of pending data needs
|
|
8
|
+
* to happen. The implementation is designed to support both synchronous and asynchronous usages,
|
|
9
|
+
* but relies on invariants described in each method documentation to keep a coherent state.
|
|
10
|
+
*/
|
|
11
|
+
export function createFlushController({
|
|
12
|
+
messagesLimit,
|
|
13
|
+
bytesLimit,
|
|
14
|
+
durationLimit,
|
|
15
|
+
pageExitObservable,
|
|
16
|
+
sessionExpireObservable
|
|
17
|
+
}) {
|
|
18
|
+
var flushObservable = new Observable()
|
|
19
|
+
|
|
20
|
+
pageExitObservable.subscribe(function (event) {
|
|
21
|
+
return flush(event.reason)
|
|
22
|
+
})
|
|
23
|
+
sessionExpireObservable.subscribe(function () {
|
|
24
|
+
return flush('session_expire')
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
var currentBytesCount = 0
|
|
28
|
+
var currentMessagesCount = 0
|
|
29
|
+
|
|
30
|
+
function flush(flushReason) {
|
|
31
|
+
if (currentMessagesCount === 0) {
|
|
32
|
+
return
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
var messagesCount = currentMessagesCount
|
|
36
|
+
var bytesCount = currentBytesCount
|
|
37
|
+
|
|
38
|
+
currentMessagesCount = 0
|
|
39
|
+
currentBytesCount = 0
|
|
40
|
+
cancelDurationLimitTimeout()
|
|
41
|
+
|
|
42
|
+
flushObservable.notify({
|
|
43
|
+
reason: flushReason,
|
|
44
|
+
messagesCount: messagesCount,
|
|
45
|
+
bytesCount: bytesCount
|
|
46
|
+
})
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
var durationLimitTimeoutId
|
|
50
|
+
function scheduleDurationLimitTimeout() {
|
|
51
|
+
if (durationLimitTimeoutId === undefined) {
|
|
52
|
+
durationLimitTimeoutId = setTimeout(function () {
|
|
53
|
+
flush('duration_limit')
|
|
54
|
+
}, durationLimit)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function cancelDurationLimitTimeout() {
|
|
59
|
+
clearTimeout(durationLimitTimeoutId)
|
|
60
|
+
durationLimitTimeoutId = undefined
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
flushObservable: flushObservable,
|
|
65
|
+
getMessagesCount: function () {
|
|
66
|
+
return currentMessagesCount
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Notifies that a message will be added to a pool of pending messages waiting to be flushed.
|
|
71
|
+
*
|
|
72
|
+
* This function needs to be called synchronously, right before adding the message, so no flush
|
|
73
|
+
* event can happen after `notifyBeforeAddMessage` and before adding the message.
|
|
74
|
+
*/
|
|
75
|
+
notifyBeforeAddMessage: function (messageBytesCount) {
|
|
76
|
+
if (currentBytesCount + messageBytesCount >= bytesLimit) {
|
|
77
|
+
flush('bytes_limit')
|
|
78
|
+
}
|
|
79
|
+
// Consider the message to be added now rather than in `notifyAfterAddMessage`, because if no
|
|
80
|
+
// message was added yet and `notifyAfterAddMessage` is called asynchronously, we still want
|
|
81
|
+
// to notify when a flush is needed (for example on page exit).
|
|
82
|
+
currentMessagesCount += 1
|
|
83
|
+
currentBytesCount += messageBytesCount
|
|
84
|
+
scheduleDurationLimitTimeout()
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Notifies that a message *was* added to a pool of pending messages waiting to be flushed.
|
|
89
|
+
*
|
|
90
|
+
* This function can be called asynchronously after the message was added, but in this case it
|
|
91
|
+
* should not be called if a flush event occurred in between.
|
|
92
|
+
*/
|
|
93
|
+
notifyAfterAddMessage: function () {
|
|
94
|
+
if (currentMessagesCount >= messagesLimit) {
|
|
95
|
+
flush('messages_limit')
|
|
96
|
+
} else if (currentBytesCount >= bytesLimit) {
|
|
97
|
+
flush('bytes_limit')
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Notifies that a message was removed from a pool of pending messages waiting to be flushed.
|
|
103
|
+
*
|
|
104
|
+
* This function needs to be called synchronously, right after removing the message, so no flush
|
|
105
|
+
* event can happen after removing the message and before `notifyAfterRemoveMessage`.
|
|
106
|
+
*/
|
|
107
|
+
notifyAfterRemoveMessage: function (messageBytesCount) {
|
|
108
|
+
currentBytesCount -= messageBytesCount
|
|
109
|
+
currentMessagesCount -= 1
|
|
110
|
+
if (currentMessagesCount === 0) {
|
|
111
|
+
cancelDurationLimitTimeout()
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { newRetryState, sendWithRetryStrategy } from './sendWithRetryStrategy'
|
|
2
|
-
|
|
2
|
+
import { addEventListener } from '../browser/addEventListener'
|
|
3
3
|
/**
|
|
4
4
|
* Use POST request without content type to:
|
|
5
5
|
* - avoid CORS preflight requests
|
|
@@ -100,10 +100,17 @@ function isKeepAliveSupported() {
|
|
|
100
100
|
function sendXHR(url, data, onResponse) {
|
|
101
101
|
const request = new XMLHttpRequest()
|
|
102
102
|
request.open('POST', url, true)
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
103
|
+
addEventListener(
|
|
104
|
+
request,
|
|
105
|
+
'loadend',
|
|
106
|
+
function () {
|
|
107
|
+
if (typeof onResponse === 'function') {
|
|
108
|
+
onResponse({ status: request.status })
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
once: true
|
|
107
113
|
}
|
|
108
|
-
|
|
114
|
+
)
|
|
115
|
+
request.send(data)
|
|
109
116
|
}
|
package/src/transport/index.js
CHANGED
|
@@ -1,11 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
ONE_KIBI_BYTE,
|
|
4
|
-
ONE_MEBI_BYTE,
|
|
5
|
-
ONE_SECOND
|
|
6
|
-
} from '../helper/tools'
|
|
1
|
+
import { clocksNow, ONE_SECOND } from '../helper/tools'
|
|
2
|
+
import { ONE_MEBI_BYTE, ONE_KIBI_BYTE } from '../helper/byteUtils'
|
|
7
3
|
import { ErrorSource } from '../helper/errorTools'
|
|
8
|
-
|
|
4
|
+
import { setTimeout } from '../helper/timer'
|
|
9
5
|
export var MAX_ONGOING_BYTES_COUNT = 80 * ONE_KIBI_BYTE
|
|
10
6
|
export var MAX_ONGOING_REQUESTS = 32
|
|
11
7
|
export var MAX_QUEUE_BYTES_COUNT = 3 * ONE_MEBI_BYTE
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { Batch } from './batch'
|
|
2
2
|
import { createHttpRequest } from './httpRequest'
|
|
3
|
-
|
|
3
|
+
import { createFlushController } from './flushController'
|
|
4
4
|
export function startBatchWithReplica(
|
|
5
5
|
configuration,
|
|
6
6
|
endpointUrl,
|
|
7
7
|
reportError,
|
|
8
|
-
pageExitObservable
|
|
8
|
+
pageExitObservable,
|
|
9
|
+
sessionExpireObservable
|
|
9
10
|
) {
|
|
10
11
|
var primaryBatch = createBatch(endpointUrl)
|
|
11
12
|
|
|
@@ -16,11 +17,14 @@ export function startBatchWithReplica(
|
|
|
16
17
|
configuration.batchBytesLimit,
|
|
17
18
|
reportError
|
|
18
19
|
),
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
createFlushController({
|
|
21
|
+
messagesLimit: configuration.batchMessagesLimit,
|
|
22
|
+
bytesLimit: configuration.batchBytesLimit,
|
|
23
|
+
durationLimit: configuration.flushTimeout,
|
|
24
|
+
pageExitObservable: pageExitObservable,
|
|
25
|
+
sessionExpireObservable: sessionExpireObservable
|
|
26
|
+
}),
|
|
27
|
+
configuration.messageBytesLimit
|
|
24
28
|
)
|
|
25
29
|
}
|
|
26
30
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './user'
|
package/src/user/user.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { display } from '../helper/display'
|
|
2
|
+
import { assign, getType, each } from '../helper/tools'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Clone input data and ensure known user properties (id, name, email)
|
|
6
|
+
* are strings, as defined here:
|
|
7
|
+
* https://docs.datadoghq.com/logs/log_configuration/attributes_naming_convention/#user-related-attributes
|
|
8
|
+
*/
|
|
9
|
+
export function sanitizeUser(newUser) {
|
|
10
|
+
// We shallow clone only to prevent mutation of user data.
|
|
11
|
+
var user = assign({}, newUser)
|
|
12
|
+
var keys = ['id', 'name', 'email']
|
|
13
|
+
each(keys, function (key) {
|
|
14
|
+
if (key in user) {
|
|
15
|
+
user[key] = String(user[key])
|
|
16
|
+
}
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
return user
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Simple check to ensure user is valid
|
|
24
|
+
*/
|
|
25
|
+
export function checkUser(newUser) {
|
|
26
|
+
var isValid = getType(newUser) === 'object'
|
|
27
|
+
if (!isValid) {
|
|
28
|
+
display.error('Unsupported user:', newUser)
|
|
29
|
+
}
|
|
30
|
+
return isValid
|
|
31
|
+
}
|