@microsoft/applicationinsights-channel-js 3.0.0-beta.2303-11 → 3.0.0-nightly3.2304-28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/browser/es5/applicationinsights-channel-js.3.0.0-nightly3.2304-28.cjs.js +5768 -0
  2. package/browser/es5/applicationinsights-channel-js.3.0.0-nightly3.2304-28.cjs.js.map +1 -0
  3. package/browser/es5/applicationinsights-channel-js.3.0.0-nightly3.2304-28.cjs.min.js +6 -0
  4. package/browser/es5/applicationinsights-channel-js.3.0.0-nightly3.2304-28.cjs.min.js.map +1 -0
  5. package/browser/es5/applicationinsights-channel-js.3.0.0-nightly3.2304-28.gbl.js +5772 -0
  6. package/browser/es5/applicationinsights-channel-js.3.0.0-nightly3.2304-28.gbl.js.map +1 -0
  7. package/browser/es5/applicationinsights-channel-js.3.0.0-nightly3.2304-28.gbl.min.js +6 -0
  8. package/browser/es5/applicationinsights-channel-js.3.0.0-nightly3.2304-28.gbl.min.js.map +1 -0
  9. package/browser/es5/applicationinsights-channel-js.3.0.0-nightly3.2304-28.integrity.json +66 -0
  10. package/browser/es5/applicationinsights-channel-js.3.0.0-nightly3.2304-28.js +5774 -0
  11. package/browser/es5/applicationinsights-channel-js.3.0.0-nightly3.2304-28.js.map +1 -0
  12. package/browser/es5/applicationinsights-channel-js.3.0.0-nightly3.2304-28.min.js +6 -0
  13. package/browser/es5/applicationinsights-channel-js.3.0.0-nightly3.2304-28.min.js.map +1 -0
  14. package/browser/es5/applicationinsights-channel-js.3.cjs.js +5768 -0
  15. package/browser/es5/applicationinsights-channel-js.3.cjs.js.map +1 -0
  16. package/browser/es5/applicationinsights-channel-js.3.cjs.min.js +6 -0
  17. package/browser/es5/applicationinsights-channel-js.3.cjs.min.js.map +1 -0
  18. package/browser/es5/applicationinsights-channel-js.3.gbl.js +5772 -0
  19. package/browser/es5/applicationinsights-channel-js.3.gbl.js.map +1 -0
  20. package/browser/es5/applicationinsights-channel-js.3.gbl.min.js +6 -0
  21. package/browser/es5/applicationinsights-channel-js.3.gbl.min.js.map +1 -0
  22. package/{dist/applicationinsights-channel-js.js → browser/es5/applicationinsights-channel-js.3.js} +875 -829
  23. package/browser/es5/applicationinsights-channel-js.3.js.map +1 -0
  24. package/browser/es5/applicationinsights-channel-js.3.min.js +6 -0
  25. package/browser/es5/applicationinsights-channel-js.3.min.js.map +1 -0
  26. package/{browser → dist/es5}/applicationinsights-channel-js.js +874 -828
  27. package/dist/es5/applicationinsights-channel-js.js.map +1 -0
  28. package/dist/es5/applicationinsights-channel-js.min.js +6 -0
  29. package/dist/es5/applicationinsights-channel-js.min.js.map +1 -0
  30. package/{dist-esm → dist-es5}/EnvelopeCreator.js +2 -2
  31. package/dist-es5/EnvelopeCreator.js.map +1 -0
  32. package/dist-es5/Interfaces.js +6 -0
  33. package/{dist-esm → dist-es5}/InternalConstants.js +1 -1
  34. package/{dist-esm → dist-es5}/Offline.js +8 -7
  35. package/dist-es5/Offline.js.map +1 -0
  36. package/{dist-esm → dist-es5}/SendBuffer.js +1 -1
  37. package/{dist-esm → dist-es5}/Sender.js +6 -4
  38. package/dist-es5/Sender.js.map +1 -0
  39. package/{dist-esm → dist-es5}/Serializer.js +1 -1
  40. package/{dist-esm → dist-es5}/TelemetryProcessors/Sample.js +1 -1
  41. package/{dist-esm → dist-es5}/TelemetryProcessors/SamplingScoreGenerators/HashCodeScoreGenerator.js +1 -1
  42. package/{dist-esm → dist-es5}/TelemetryProcessors/SamplingScoreGenerators/SamplingScoreGenerator.js +1 -1
  43. package/{dist-esm → dist-es5}/__DynamicConstants.js +2 -1
  44. package/dist-es5/__DynamicConstants.js.map +1 -0
  45. package/dist-es5/applicationinsights-channel-js.js +6 -0
  46. package/package.json +18 -17
  47. package/tsconfig.json +5 -4
  48. package/types/applicationinsights-channel-js.d.ts +284 -1
  49. package/{dist/applicationinsights-channel-js.d.ts → types/applicationinsights-channel-js.namespaced.d.ts} +1 -1
  50. package/browser/applicationinsights-channel-js.integrity.json +0 -26
  51. package/browser/applicationinsights-channel-js.js.map +0 -1
  52. package/browser/applicationinsights-channel-js.min.js +0 -6
  53. package/browser/applicationinsights-channel-js.min.js.map +0 -1
  54. package/dist/applicationinsights-channel-js.api.json +0 -1357
  55. package/dist/applicationinsights-channel-js.api.md +0 -60
  56. package/dist/applicationinsights-channel-js.js.map +0 -1
  57. package/dist/applicationinsights-channel-js.min.js +0 -6
  58. package/dist/applicationinsights-channel-js.min.js.map +0 -1
  59. package/dist/applicationinsights-channel-js.rollup.d.ts +0 -285
  60. package/dist-esm/EnvelopeCreator.js.map +0 -1
  61. package/dist-esm/Interfaces.js +0 -6
  62. package/dist-esm/Offline.js.map +0 -1
  63. package/dist-esm/Sender.js.map +0 -1
  64. package/dist-esm/__DynamicConstants.js.map +0 -1
  65. package/dist-esm/applicationinsights-channel-js.js +0 -6
  66. package/src/EnvelopeCreator.ts +0 -351
  67. package/src/Interfaces.ts +0 -114
  68. package/src/InternalConstants.ts +0 -11
  69. package/src/Offline.ts +0 -106
  70. package/src/SendBuffer.ts +0 -396
  71. package/src/Sender.ts +0 -1256
  72. package/src/Serializer.ts +0 -177
  73. package/src/TelemetryProcessors/Sample.ts +0 -49
  74. package/src/TelemetryProcessors/SamplingScoreGenerators/HashCodeScoreGenerator.ts +0 -37
  75. package/src/TelemetryProcessors/SamplingScoreGenerators/SamplingScoreGenerator.ts +0 -35
  76. package/src/__DynamicConstants.ts +0 -64
  77. package/src/applicationinsights-channel-js.ts +0 -1
  78. package/types/EnvelopeCreator.d.ts +0 -12
  79. package/types/Interfaces.d.ts +0 -95
  80. package/types/InternalConstants.d.ts +0 -1
  81. package/types/Offline.d.ts +0 -10
  82. package/types/SendBuffer.d.ts +0 -73
  83. package/types/Sender.d.ts +0 -84
  84. package/types/Serializer.d.ts +0 -9
  85. package/types/TelemetryProcessors/Sample.d.ts +0 -12
  86. package/types/TelemetryProcessors/SamplingScoreGenerators/HashCodeScoreGenerator.d.ts +0 -5
  87. package/types/TelemetryProcessors/SamplingScoreGenerators/SamplingScoreGenerator.d.ts +0 -5
  88. package/types/__DynamicConstants.d.ts +0 -52
  89. package/types/tsdoc-metadata.json +0 -11
  90. /package/{dist-esm → dist-es5}/Interfaces.js.map +0 -0
  91. /package/{dist-esm → dist-es5}/InternalConstants.js.map +0 -0
  92. /package/{dist-esm → dist-es5}/SendBuffer.js.map +0 -0
  93. /package/{dist-esm → dist-es5}/Serializer.js.map +0 -0
  94. /package/{dist-esm → dist-es5}/TelemetryProcessors/Sample.js.map +0 -0
  95. /package/{dist-esm → dist-es5}/TelemetryProcessors/SamplingScoreGenerators/HashCodeScoreGenerator.js.map +0 -0
  96. /package/{dist-esm → dist-es5}/TelemetryProcessors/SamplingScoreGenerators/SamplingScoreGenerator.js.map +0 -0
  97. /package/{dist-esm → dist-es5}/applicationinsights-channel-js.js.map +0 -0
package/src/Sender.ts DELETED
@@ -1,1256 +0,0 @@
1
- import dynamicProto from "@microsoft/dynamicproto-js";
2
- import {
3
- BreezeChannelIdentifier, DEFAULT_BREEZE_ENDPOINT, DEFAULT_BREEZE_PATH, DisabledPropertyName, Event, Exception, IConfig, IEnvelope,
4
- ISample, Metric, PageView, PageViewPerformance, ProcessLegacy, RemoteDependencyData, RequestHeaders, SampleRate, Trace, eRequestHeaders,
5
- isInternalApplicationInsightsEndpoint, utlCanUseSessionStorage
6
- } from "@microsoft/applicationinsights-common";
7
- import {
8
- BaseTelemetryPlugin, IAppInsightsCore, IChannelControls, IConfigDefaults, IConfiguration, IDiagnosticLogger, INotificationManager,
9
- IPlugin, IProcessTelemetryContext, IProcessTelemetryUnloadContext, ITelemetryItem, ITelemetryPluginChain, ITelemetryUnloadState,
10
- SendRequestReason, _eInternalMessageId, _throwInternal, _warnToConsole, arrForEach, cfgDfBoolean, cfgDfValidate,
11
- createProcessTelemetryContext, createUniqueNamespace, dateNow, dumpObj, eLoggingSeverity, getExceptionName, getIEVersion, getJSON,
12
- getNavigator, getWindow, isArray, isBeaconsSupported, isFetchSupported, isNullOrUndefined, isXhrSupported, mergeEvtNamespace, objExtend,
13
- objKeys, onConfigChange, useXDomainRequest
14
- } from "@microsoft/applicationinsights-core-js";
15
- import { ITimerHandler, isTruthy, objDeepFreeze, objDefine, scheduleTimeout } from "@nevware21/ts-utils";
16
- import {
17
- DependencyEnvelopeCreator, EventEnvelopeCreator, ExceptionEnvelopeCreator, MetricEnvelopeCreator, PageViewEnvelopeCreator,
18
- PageViewPerformanceEnvelopeCreator, TraceEnvelopeCreator
19
- } from "./EnvelopeCreator";
20
- import { IBackendResponse, ISenderConfig, XDomainRequest as IXDomainRequest } from "./Interfaces";
21
- import { IOfflineListener, createOfflineListener } from "./Offline";
22
- import { ArraySendBuffer, ISendBuffer, SessionStorageSendBuffer } from "./SendBuffer";
23
- import { Serializer } from "./Serializer";
24
- import { Sample } from "./TelemetryProcessors/Sample";
25
-
26
- const UNDEFINED_VALUE: undefined = undefined;
27
-
28
- const FetchSyncRequestSizeLimitBytes = 65000; // approx 64kb (the current Edge, Firefox and Chrome max limit)
29
-
30
- declare var XDomainRequest: {
31
- prototype: IXDomainRequest;
32
- new(): IXDomainRequest;
33
- };
34
-
35
- export type SenderFunction = (payload: string[], isAsync: boolean) => void;
36
-
37
- function _getResponseText(xhr: XMLHttpRequest | IXDomainRequest) {
38
- try {
39
- return xhr.responseText;
40
- } catch (e) {
41
- // Best effort, as XHR may throw while XDR wont so just ignore
42
- }
43
-
44
- return null;
45
- }
46
-
47
- const defaultAppInsightsChannelConfig: IConfigDefaults<ISenderConfig> = objDeepFreeze({
48
- // Use the default value (handles empty string in the configuration)
49
- endpointUrl: cfgDfValidate(isTruthy, DEFAULT_BREEZE_ENDPOINT + DEFAULT_BREEZE_PATH),
50
- emitLineDelimitedJson: cfgDfBoolean(),
51
- maxBatchInterval: 15000,
52
- maxBatchSizeInBytes: 102400, // 100kb
53
- disableTelemetry: cfgDfBoolean(),
54
- enableSessionStorageBuffer: cfgDfBoolean(true),
55
- isRetryDisabled: cfgDfBoolean(),
56
- isBeaconApiDisabled: cfgDfBoolean(true),
57
- disableXhr: cfgDfBoolean(),
58
- onunloadDisableFetch: cfgDfBoolean(),
59
- onunloadDisableBeacon: cfgDfBoolean(),
60
- instrumentationKey: UNDEFINED_VALUE, // Channel doesn't need iKey, it should be set already
61
- namePrefix: UNDEFINED_VALUE,
62
- samplingPercentage: cfgDfValidate(_chkSampling, 100),
63
- customHeaders: UNDEFINED_VALUE,
64
- convertUndefined: UNDEFINED_VALUE,
65
- eventsLimitInMem: 10000
66
- });
67
-
68
- function _chkSampling(value: number) {
69
- return !isNaN(value) && value > 0 && value <= 100;
70
- }
71
-
72
- type EnvelopeCreator = (logger: IDiagnosticLogger, telemetryItem: ITelemetryItem, customUndefinedValue?: any) => IEnvelope;
73
-
74
- const EnvelopeTypeCreator: { [key:string] : EnvelopeCreator } = {
75
- [Event.dataType]: EventEnvelopeCreator,
76
- [Trace.dataType]: TraceEnvelopeCreator,
77
- [PageView.dataType]: PageViewEnvelopeCreator,
78
- [PageViewPerformance.dataType]: PageViewPerformanceEnvelopeCreator,
79
- [Exception.dataType]: ExceptionEnvelopeCreator,
80
- [Metric.dataType]: MetricEnvelopeCreator,
81
- [RemoteDependencyData.dataType]: DependencyEnvelopeCreator
82
- };
83
-
84
- export class Sender extends BaseTelemetryPlugin implements IChannelControls {
85
-
86
- public static constructEnvelope(orig: ITelemetryItem, iKey: string, logger: IDiagnosticLogger, convertUndefined?: any): IEnvelope {
87
- let envelope: ITelemetryItem;
88
- if (iKey !== orig.iKey && !isNullOrUndefined(iKey)) {
89
- envelope = {
90
- ...orig,
91
- iKey
92
- };
93
- } else {
94
- envelope = orig;
95
- }
96
-
97
- let creator: EnvelopeCreator = EnvelopeTypeCreator[envelope.baseType] || EventEnvelopeCreator;
98
-
99
- return creator(logger, envelope, convertUndefined);
100
- }
101
-
102
- public readonly priority: number = 1001;
103
-
104
- public readonly identifier: string = BreezeChannelIdentifier;
105
-
106
- /**
107
- * The configuration for this sender instance
108
- */
109
- public readonly _senderConfig: ISenderConfig;
110
-
111
- /**
112
- * A method which will cause data to be send to the url
113
- */
114
- public _sender: SenderFunction;
115
-
116
- /**
117
- * A send buffer object
118
- */
119
- public _buffer: ISendBuffer;
120
-
121
- /**
122
- * AppId of this component parsed from some backend response.
123
- */
124
- public _appId: string;
125
-
126
- protected _sample: ISample;
127
-
128
- constructor() {
129
- super();
130
-
131
- // Don't set the defaults here, set them in the _initDefaults() as this is also called during unload
132
- let _consecutiveErrors: number; // How many times in a row a retryable error condition has occurred.
133
- let _retryAt: number; // The time to retry at in milliseconds from 1970/01/01 (this makes the timer calculation easy).
134
- let _lastSend: number; // The time of the last send operation.
135
- let _paused: boolean; // Flag indicating that the sending should be paused
136
- let _timeoutHandle: ITimerHandler; // Handle to the timer for delayed sending of batches of data.
137
- let _serializer: Serializer;
138
- let _stamp_specific_redirects: number;
139
- let _headers: { [name: string]: string };
140
- let _syncFetchPayload = 0; // Keep track of the outstanding sync fetch payload total (as sync fetch has limits)
141
- let _fallbackSender: SenderFunction; // The sender to use if the payload size is too large
142
- let _syncUnloadSender: SenderFunction; // The identified sender to use for the synchronous unload stage
143
- let _offlineListener: IOfflineListener;
144
- let _evtNamespace: string | string[];
145
- let _endpointUrl: string;
146
- let _orgEndpointUrl: string;
147
- let _maxBatchSizeInBytes: number;
148
- let _beaconSupported: boolean;
149
- let _customHeaders: Array<{header: string, value: string}>;
150
- let _disableTelemetry: boolean;
151
- let _instrumentationKey: string;
152
- let _convertUndefined: any;
153
- let _isRetryDisabled: boolean;
154
- let _maxBatchInterval: number;
155
- let _sessionStorageUsed: boolean;
156
- let _namePrefix: string;
157
-
158
- dynamicProto(Sender, this, (_self, _base) => {
159
-
160
- _initDefaults();
161
-
162
- _self.pause = () => {
163
- _clearScheduledTimer();
164
- _paused = true;
165
- };
166
-
167
- _self.resume = () => {
168
- if (_paused) {
169
- _paused = false;
170
- _retryAt = null;
171
-
172
- // flush if we have exceeded the max-size already
173
- _checkMaxSize();
174
- _setupTimer();
175
- }
176
- };
177
-
178
- _self.flush = (isAsync: boolean = true, callBack?: () => void, sendReason?: SendRequestReason) => {
179
- if (!_paused) {
180
- // Clear the normal schedule timer as we are going to try and flush ASAP
181
- _clearScheduledTimer();
182
-
183
- try {
184
- _self.triggerSend(isAsync, null, sendReason || SendRequestReason.ManualFlush);
185
- } catch (e) {
186
- _throwInternal(_self.diagLog(), eLoggingSeverity.CRITICAL,
187
- _eInternalMessageId.FlushFailed,
188
- "flush failed, telemetry will not be collected: " + getExceptionName(e),
189
- { exception: dumpObj(e) });
190
- }
191
- }
192
- };
193
-
194
- _self.onunloadFlush = () => {
195
- if (!_paused) {
196
- if (_beaconSupported) {
197
- try {
198
- _self.triggerSend(true, _doUnloadSend, SendRequestReason.Unload);
199
- } catch (e) {
200
- _throwInternal(_self.diagLog(), eLoggingSeverity.CRITICAL,
201
- _eInternalMessageId.FailedToSendQueuedTelemetry,
202
- "failed to flush with beacon sender on page unload, telemetry will not be collected: " + getExceptionName(e),
203
- { exception: dumpObj(e) });
204
- }
205
- } else {
206
- _self.flush();
207
- }
208
- }
209
- };
210
-
211
- _self.addHeader = (name: string, value: string) => {
212
- _headers[name] = value;
213
- };
214
-
215
- _self.initialize = (config: IConfiguration & IConfig, core: IAppInsightsCore, extensions: IPlugin[], pluginChain?:ITelemetryPluginChain): void => {
216
- if (_self.isInitialized()) {
217
- _throwInternal(_self.diagLog(), eLoggingSeverity.CRITICAL, _eInternalMessageId.SenderNotInitialized, "Sender is already initialized");
218
- }
219
-
220
- _base.initialize(config, core, extensions, pluginChain);
221
- let identifier = _self.identifier;
222
- _serializer = new Serializer(core.logger);
223
- _consecutiveErrors = 0;
224
- _retryAt = null;
225
- _lastSend = 0;
226
- _self._sender = null;
227
- _stamp_specific_redirects = 0;
228
- let diagLog = _self.diagLog();
229
- _evtNamespace = mergeEvtNamespace(createUniqueNamespace("Sender"), core.evtNamespace && core.evtNamespace());
230
- _offlineListener = createOfflineListener(_evtNamespace);
231
-
232
- // This function will be re-called whenever any referenced configuration is changed
233
- _self._addHook(onConfigChange(config, (details) => {
234
- let config = details.cfg;
235
- let ctx = createProcessTelemetryContext(null, config, core);
236
- let senderConfig = ctx.getExtCfg(identifier, defaultAppInsightsChannelConfig);
237
-
238
- objDefine(_self, "_senderConfig", {
239
- g: function() {
240
- return senderConfig;
241
- }
242
- });
243
-
244
- // Only update the endpoint if the original config !== the current config
245
- // This is so any redirect endpointUrl is not overwritten
246
- if (_orgEndpointUrl !== senderConfig.endpointUrl) {
247
- if (_orgEndpointUrl) {
248
- // TODO: add doc to remind users to flush before changing endpoint, otherwise all unsent payload will be sent to new endpoint
249
- }
250
- _endpointUrl = _orgEndpointUrl = senderConfig.endpointUrl;
251
- }
252
-
253
- if (_customHeaders && _customHeaders !== senderConfig.customHeaders) {
254
- // Removing any previously defined custom headers as they have changed
255
- arrForEach(_customHeaders, customHeader => {
256
- delete _headers[customHeader.header];
257
- });
258
- }
259
-
260
- _maxBatchSizeInBytes = senderConfig.maxBatchSizeInBytes;
261
- _beaconSupported = (senderConfig.onunloadDisableBeacon === false || senderConfig.isBeaconApiDisabled === false) && isBeaconsSupported();
262
-
263
- let canUseSessionStorage = !!senderConfig.enableSessionStorageBuffer && utlCanUseSessionStorage();
264
- let namePrefix = senderConfig.namePrefix;
265
-
266
- //Note: emitLineDelimitedJson and eventsLimitInMem is directly accessed via config in senderBuffer
267
- //Therefore, if canUseSessionStorage is not changed, we do not need to re initialize a new one
268
- let shouldUpdate = (canUseSessionStorage !== _sessionStorageUsed)
269
- || (canUseSessionStorage && (_namePrefix !== namePrefix)); // prefixName is only used in session storage
270
-
271
- if (_self._buffer) {
272
- // case1 (Pre and Now enableSessionStorageBuffer settings are same)
273
- // if namePrefix changes, transfer current buffer to new buffer
274
- // else no action needed
275
-
276
- //case2 (Pre and Now enableSessionStorageBuffer settings are changed)
277
- // transfer current buffer to new buffer
278
-
279
- if (shouldUpdate) {
280
- try {
281
-
282
- _self._buffer = _self._buffer.createNew(diagLog, senderConfig, canUseSessionStorage);
283
-
284
- } catch (e) {
285
- _throwInternal(_self.diagLog(), eLoggingSeverity.CRITICAL,
286
- _eInternalMessageId.FailedAddingTelemetryToBuffer,
287
- "failed to transfer telemetry to different buffer storage, telemetry will be lost: " + getExceptionName(e),
288
- { exception: dumpObj(e) });
289
- }
290
- }
291
- _checkMaxSize();
292
- } else {
293
- _self._buffer = canUseSessionStorage
294
- ? new SessionStorageSendBuffer(diagLog, senderConfig) : new ArraySendBuffer(diagLog, senderConfig);
295
- }
296
-
297
- _namePrefix = namePrefix;
298
- _sessionStorageUsed = canUseSessionStorage;
299
-
300
- _self._sample = new Sample(senderConfig.samplingPercentage, diagLog);
301
-
302
- _instrumentationKey = senderConfig.instrumentationKey;
303
- if(!_validateInstrumentationKey(_instrumentationKey, config)) {
304
- _throwInternal(diagLog,
305
- eLoggingSeverity.CRITICAL,
306
- _eInternalMessageId.InvalidInstrumentationKey, "Invalid Instrumentation key " + _instrumentationKey);
307
- }
308
-
309
- _customHeaders = senderConfig.customHeaders;
310
- if (!isInternalApplicationInsightsEndpoint(_endpointUrl) && _customHeaders && _customHeaders.length > 0) {
311
- arrForEach(_customHeaders, customHeader => {
312
- this.addHeader(customHeader.header, customHeader.value);
313
- });
314
- } else {
315
- _customHeaders = null;
316
- }
317
-
318
- let sendPostFunc: SenderFunction = null;
319
- if (!senderConfig.disableXhr && useXDomainRequest()) {
320
- sendPostFunc = _xdrSender; // IE 8 and 9
321
- } else if (!senderConfig.disableXhr && isXhrSupported()) {
322
- sendPostFunc = _xhrSender;
323
- }
324
-
325
- if (!sendPostFunc && isFetchSupported()) {
326
- sendPostFunc = _fetchSender;
327
- }
328
-
329
- // always fallback to XHR
330
- _fallbackSender = sendPostFunc || _xhrSender;
331
-
332
- if (!senderConfig.isBeaconApiDisabled && isBeaconsSupported()) {
333
- // Config is set to always used beacon sending
334
- sendPostFunc = _beaconSender;
335
- }
336
-
337
- _self._sender = sendPostFunc || _xhrSender;
338
-
339
- if (!senderConfig.onunloadDisableFetch && isFetchSupported(true)) {
340
- // Try and use the fetch with keepalive
341
- _syncUnloadSender = _fetchKeepAliveSender;
342
- } else if (isBeaconsSupported()) {
343
- // Try and use sendBeacon
344
- _syncUnloadSender = _beaconSender;
345
- } else if (!senderConfig.disableXhr && useXDomainRequest()) {
346
- _syncUnloadSender = _xdrSender; // IE 8 and 9
347
- } else if (!senderConfig.disableXhr && isXhrSupported()) {
348
- _syncUnloadSender = _xhrSender;
349
- } else {
350
- _syncUnloadSender = _fallbackSender;
351
- }
352
-
353
- _disableTelemetry = senderConfig.disableTelemetry;
354
- _convertUndefined = senderConfig.convertUndefined || UNDEFINED_VALUE;
355
- _isRetryDisabled = senderConfig.isRetryDisabled;
356
- _maxBatchInterval = senderConfig.maxBatchInterval;
357
- }));
358
- };
359
-
360
- _self.processTelemetry = (telemetryItem: ITelemetryItem, itemCtx?: IProcessTelemetryContext) => {
361
- itemCtx = _self._getTelCtx(itemCtx);
362
- let diagLogger = itemCtx.diagLog();
363
-
364
- try {
365
- // if master off switch is set, don't send any data
366
- if (_disableTelemetry) {
367
- // Do not send/save data
368
- return;
369
- }
370
-
371
- // validate input
372
- if (!telemetryItem) {
373
- _throwInternal(diagLogger, eLoggingSeverity.CRITICAL, _eInternalMessageId.CannotSendEmptyTelemetry, "Cannot send empty telemetry");
374
- return;
375
- }
376
-
377
- // validate event
378
- if (telemetryItem.baseData && !telemetryItem.baseType) {
379
- _throwInternal(diagLogger, eLoggingSeverity.CRITICAL, _eInternalMessageId.InvalidEvent, "Cannot send telemetry without baseData and baseType");
380
- return;
381
- }
382
-
383
- if (!telemetryItem.baseType) {
384
- // Default
385
- telemetryItem.baseType = "EventData";
386
- }
387
-
388
- // ensure a sender was constructed
389
- if (!_self._sender) {
390
- _throwInternal(diagLogger, eLoggingSeverity.CRITICAL, _eInternalMessageId.SenderNotInitialized, "Sender was not initialized");
391
- return;
392
- }
393
-
394
- // check if this item should be sampled in, else add sampleRate tag
395
- if (!_isSampledIn(telemetryItem)) {
396
- // Item is sampled out, do not send it
397
- _throwInternal(diagLogger, eLoggingSeverity.WARNING, _eInternalMessageId.TelemetrySampledAndNotSent,
398
- "Telemetry item was sampled out and not sent", { SampleRate: _self._sample.sampleRate });
399
- return;
400
- } else {
401
- telemetryItem[SampleRate] = _self._sample.sampleRate;
402
- }
403
-
404
- // construct an envelope that Application Insights endpoint can understand
405
- // if ikey of telemetry is provided and not empty, envelope will use this iKey instead of senderConfig iKey
406
- let defaultEnvelopeIkey = telemetryItem.iKey || _instrumentationKey;
407
- let aiEnvelope = Sender.constructEnvelope(telemetryItem, defaultEnvelopeIkey, diagLogger, _convertUndefined);
408
- if (!aiEnvelope) {
409
- _throwInternal(diagLogger, eLoggingSeverity.CRITICAL, _eInternalMessageId.CreateEnvelopeError, "Unable to create an AppInsights envelope");
410
- return;
411
- }
412
-
413
- let doNotSendItem = false;
414
- // this is for running in legacy mode, where customer may already have a custom initializer present
415
- if (telemetryItem.tags && telemetryItem.tags[ProcessLegacy]) {
416
- arrForEach(telemetryItem.tags[ProcessLegacy], (callBack: (env: IEnvelope) => boolean | void) => {
417
- try {
418
- if (callBack && callBack(aiEnvelope) === false) {
419
- doNotSendItem = true;
420
- _warnToConsole(diagLogger, "Telemetry processor check returns false");
421
- }
422
- } catch (e) {
423
- // log error but dont stop executing rest of the telemetry initializers
424
- // doNotSendItem = true;
425
- _throwInternal(diagLogger,
426
- eLoggingSeverity.CRITICAL, _eInternalMessageId.TelemetryInitializerFailed, "One of telemetry initializers failed, telemetry item will not be sent: " + getExceptionName(e),
427
- { exception: dumpObj(e) }, true);
428
- }
429
- });
430
-
431
- delete telemetryItem.tags[ProcessLegacy];
432
- }
433
- if (doNotSendItem) {
434
- return; // do not send, no need to execute next plugin
435
- }
436
-
437
- // check if the incoming payload is too large, truncate if necessary
438
- const payload: string = _serializer.serialize(aiEnvelope);
439
-
440
- // flush if we would exceed the max-size limit by adding this item
441
- const buffer = _self._buffer;
442
- _checkMaxSize(payload);
443
-
444
- // enqueue the payload
445
- buffer.enqueue(payload);
446
-
447
- // ensure an invocation timeout is set
448
- _setupTimer();
449
-
450
- } catch (e) {
451
- _throwInternal(diagLogger,
452
- eLoggingSeverity.WARNING,
453
- _eInternalMessageId.FailedAddingTelemetryToBuffer,
454
- "Failed adding telemetry to the sender's buffer, some telemetry will be lost: " + getExceptionName(e),
455
- { exception: dumpObj(e) });
456
- }
457
-
458
- // hand off the telemetry item to the next plugin
459
- _self.processNext(telemetryItem, itemCtx);
460
- };
461
-
462
- /**
463
- * xhr state changes
464
- */
465
- _self._xhrReadyStateChange = (xhr: XMLHttpRequest, payload: string[], countOfItemsInPayload: number) => {
466
- if (xhr.readyState === 4) {
467
- _checkResponsStatus(xhr.status, payload, xhr.responseURL, countOfItemsInPayload, _formatErrorMessageXhr(xhr), _getResponseText(xhr) || xhr.response);
468
- }
469
- };
470
-
471
- /**
472
- * Immediately send buffered data
473
- * @param async - {boolean} - Indicates if the events should be sent asynchronously
474
- * @param forcedSender - {SenderFunction} - Indicates the forcedSender, undefined if not passed
475
- */
476
- _self.triggerSend = (async = true, forcedSender?: SenderFunction, sendReason?: SendRequestReason) => {
477
- if (!_paused) {
478
- try {
479
- const buffer = _self._buffer;
480
-
481
- // Send data only if disableTelemetry is false
482
- if (!_disableTelemetry) {
483
-
484
- if (buffer.count() > 0) {
485
- const payload = buffer.getItems();
486
-
487
- _notifySendRequest(sendReason||SendRequestReason.Undefined, async);
488
-
489
- // invoke send
490
- if (forcedSender) {
491
- forcedSender.call(this, payload, async);
492
- } else {
493
- _self._sender(payload, async);
494
- }
495
- }
496
-
497
- // update lastSend time to enable throttling
498
- _lastSend = +new Date;
499
- } else {
500
- buffer.clear();
501
- }
502
-
503
- _clearScheduledTimer();
504
- } catch (e) {
505
- /* Ignore this error for IE under v10 */
506
- let ieVer = getIEVersion();
507
- if (!ieVer || ieVer > 9) {
508
- _throwInternal(_self.diagLog(),
509
- eLoggingSeverity.CRITICAL,
510
- _eInternalMessageId.TransmissionFailed,
511
- "Telemetry transmission failed, some telemetry will be lost: " + getExceptionName(e),
512
- { exception: dumpObj(e) });
513
- }
514
- }
515
- }
516
- };
517
-
518
- _self._doTeardown = (unloadCtx?: IProcessTelemetryUnloadContext, unloadState?: ITelemetryUnloadState) => {
519
- _self.onunloadFlush();
520
- _offlineListener.unload();
521
- _initDefaults();
522
- };
523
-
524
- /**
525
- * error handler
526
- */
527
- _self._onError = (payload: string[], message: string, event?: ErrorEvent) => {
528
- _throwInternal(_self.diagLog(),
529
- eLoggingSeverity.WARNING,
530
- _eInternalMessageId.OnError,
531
- "Failed to send telemetry.",
532
- { message });
533
-
534
- _self._buffer.clearSent(payload);
535
- };
536
-
537
- /**
538
- * partial success handler
539
- */
540
- _self._onPartialSuccess = (payload: string[], results: IBackendResponse) => {
541
- const failed: string[] = [];
542
- const retry: string[] = [];
543
-
544
- // Iterate through the reversed array of errors so that splicing doesn't have invalid indexes after the first item.
545
- const errors = results.errors.reverse();
546
- for (const error of errors) {
547
- const extracted = payload.splice(error.index, 1)[0];
548
- if (_isRetriable(error.statusCode)) {
549
- retry.push(extracted);
550
- } else {
551
- // All other errors, including: 402 (Monthly quota exceeded) and 439 (Too many requests and refresh cache).
552
- failed.push(extracted);
553
- }
554
- }
555
-
556
- if (payload.length > 0) {
557
- _self._onSuccess(payload, results.itemsAccepted);
558
- }
559
-
560
- if (failed.length > 0) {
561
- _self._onError(failed, _formatErrorMessageXhr(null, ["partial success", results.itemsAccepted, "of", results.itemsReceived].join(" ")));
562
- }
563
-
564
- if (retry.length > 0) {
565
- _resendPayload(retry);
566
-
567
- _throwInternal(_self.diagLog(),
568
- eLoggingSeverity.WARNING,
569
- _eInternalMessageId.TransmissionFailed, "Partial success. " +
570
- "Delivered: " + payload.length + ", Failed: " + failed.length +
571
- ". Will retry to send " + retry.length + " our of " + results.itemsReceived + " items");
572
- }
573
- };
574
-
575
- /**
576
- * success handler
577
- */
578
- _self._onSuccess = (payload: string[], countOfItemsInPayload: number) => {
579
- _self._buffer.clearSent(payload);
580
- };
581
-
582
- /**
583
- * xdr state changes
584
- */
585
- _self._xdrOnLoad = (xdr: IXDomainRequest, payload: string[]) => {
586
- const responseText = _getResponseText(xdr);
587
- if (xdr && (responseText + "" === "200" || responseText === "")) {
588
- _consecutiveErrors = 0;
589
- _self._onSuccess(payload, 0);
590
- } else {
591
- const results = _parseResponse(responseText);
592
-
593
- if (results && results.itemsReceived && results.itemsReceived > results.itemsAccepted
594
- && !_isRetryDisabled) {
595
- _self._onPartialSuccess(payload, results);
596
- } else {
597
- _self._onError(payload, _formatErrorMessageXdr(xdr));
598
- }
599
- }
600
- };
601
-
602
- function _isSampledIn(envelope: ITelemetryItem): boolean {
603
- return _self._sample.isSampledIn(envelope);
604
- }
605
-
606
- function _checkMaxSize(incomingPayload?: string): boolean {
607
- let incomingSize = incomingPayload? incomingPayload.length : 0;
608
- if ((_self._buffer.size() + incomingSize) > _maxBatchSizeInBytes) {
609
- _self.triggerSend(true, null, SendRequestReason.MaxBatchSize);
610
- return true;
611
- }
612
- return false;
613
- }
614
-
615
- function _checkResponsStatus(status: number, payload: string[], responseUrl: string, countOfItemsInPayload: number, errorMessage: string, res: any) {
616
- let response: IBackendResponse = null;
617
-
618
- if (!_self._appId) {
619
- response = _parseResponse(res);
620
- if (response && response.appId) {
621
- _self._appId = response.appId;
622
- }
623
- }
624
-
625
- if ((status < 200 || status >= 300) && status !== 0) {
626
-
627
- // Update End Point url if permanent redirect or moved permanently
628
- // Updates the end point url before retry
629
- if(status === 301 || status === 307 || status === 308) {
630
- if(!_checkAndUpdateEndPointUrl(responseUrl)) {
631
- _self._onError(payload, errorMessage);
632
- return;
633
- }
634
- }
635
- if (!_isRetryDisabled && _isRetriable(status)) {
636
- _resendPayload(payload);
637
- _throwInternal(_self.diagLog(),
638
- eLoggingSeverity.WARNING,
639
- _eInternalMessageId.TransmissionFailed, ". " +
640
- "Response code " + status + ". Will retry to send " + payload.length + " items.");
641
- } else {
642
- _self._onError(payload, errorMessage);
643
- }
644
- } else if (_offlineListener && !_offlineListener.isOnline()) { // offline
645
- // Note: Don't check for status == 0, since adblock gives this code
646
- if (!_isRetryDisabled) {
647
- const offlineBackOffMultiplier = 10; // arbritrary number
648
- _resendPayload(payload, offlineBackOffMultiplier);
649
-
650
- _throwInternal(_self.diagLog(),
651
- eLoggingSeverity.WARNING,
652
- _eInternalMessageId.TransmissionFailed, `. Offline - Response Code: ${status}. Offline status: ${!_offlineListener.isOnline()}. Will retry to send ${payload.length} items.`);
653
- }
654
- } else {
655
-
656
- // check if the xhr's responseURL or fetch's response.url is same as endpoint url
657
- // TODO after 10 redirects force send telemetry with 'redirect=false' as query parameter.
658
- _checkAndUpdateEndPointUrl(responseUrl);
659
-
660
- if (status === 206) {
661
- if (!response) {
662
- response = _parseResponse(res);
663
- }
664
-
665
- if (response && !_isRetryDisabled) {
666
- _self._onPartialSuccess(payload, response);
667
- } else {
668
- _self._onError(payload, errorMessage);
669
- }
670
- } else {
671
- _consecutiveErrors = 0;
672
- _self._onSuccess(payload, countOfItemsInPayload);
673
- }
674
- }
675
- }
676
-
677
- function _checkAndUpdateEndPointUrl(responseUrl: string) {
678
- // Maximum stamp specific redirects allowed(uncomment this when breeze is ready with not allowing redirects feature)
679
- if(_stamp_specific_redirects >= 10) {
680
- // _self._senderConfig.endpointUrl = () => Sender._getDefaultAppInsightsChannelConfig().endpointUrl()+"/?redirect=false";
681
- // _stamp_specific_redirects = 0;
682
- return false;
683
- }
684
- if(!isNullOrUndefined(responseUrl) && responseUrl !== "") {
685
- if(responseUrl !== _endpointUrl) {
686
- _endpointUrl = responseUrl;
687
- ++_stamp_specific_redirects;
688
- return true;
689
- }
690
- }
691
- return false;
692
- }
693
-
694
- function _doUnloadSend(payload: string[], isAsync: boolean) {
695
- if (_syncUnloadSender) {
696
- // We are unloading so always call the sender with sync set to false
697
- _syncUnloadSender(payload, false);
698
- } else {
699
- // Fallback to the previous beacon Sender (which causes a CORB warning on chrome now)
700
- _beaconSender(payload, isAsync);
701
- }
702
- }
703
-
704
- function _doBeaconSend(payload: string[]) {
705
- const nav = getNavigator();
706
- const buffer = _self._buffer;
707
- const url = _endpointUrl;
708
- const batch = _self._buffer.batchPayloads(payload);
709
-
710
- // Chrome only allows CORS-safelisted values for the sendBeacon data argument
711
- // see: https://bugs.chromium.org/p/chromium/issues/detail?id=720283
712
- const plainTextBatch = new Blob([batch], { type: "text/plain;charset=UTF-8" });
713
-
714
- // The sendBeacon method returns true if the user agent is able to successfully queue the data for transfer. Otherwise it returns false.
715
- const queued = nav.sendBeacon(url, plainTextBatch);
716
- if (queued) {
717
- buffer.markAsSent(payload);
718
- // no response from beaconSender, clear buffer
719
- _self._onSuccess(payload, payload.length);
720
- }
721
-
722
- return queued;
723
- }
724
- /**
725
- * Send Beacon API request
726
- * @param payload - {string} - The data payload to be sent.
727
- * @param isAsync - {boolean} - not used
728
- * Note: Beacon API does not support custom headers and we are not able to get
729
- * appId from the backend for the correct correlation.
730
- */
731
- function _beaconSender(payload: string[], isAsync: boolean) {
732
- if (isArray(payload) && payload.length > 0) {
733
- // The sendBeacon method returns true if the user agent is able to successfully queue the data for transfer. Otherwise it returns false.
734
- if (!_doBeaconSend(payload)) {
735
- // Failed to send entire payload so try and split data and try to send as much events as possible
736
- let droppedPayload: string[] = [];
737
- for (let lp = 0; lp < payload.length; lp++) {
738
- const thePayload = payload[lp];
739
-
740
- if (!_doBeaconSend([thePayload])) {
741
- // Can't send anymore, so split the batch and drop the rest
742
- droppedPayload.push(thePayload);
743
- }
744
- }
745
-
746
- if (droppedPayload.length > 0) {
747
- _fallbackSender && _fallbackSender(droppedPayload, true);
748
- _throwInternal(_self.diagLog(), eLoggingSeverity.WARNING, _eInternalMessageId.TransmissionFailed, ". " + "Failed to send telemetry with Beacon API, retried with normal sender.");
749
- }
750
- }
751
- }
752
- }
753
-
754
- /**
755
- * Send XMLHttpRequest
756
- * @param payload - {string} - The data payload to be sent.
757
- * @param isAsync - {boolean} - Indicates if the request should be sent asynchronously
758
- */
759
- function _xhrSender(payload: string[], isAsync: boolean) {
760
- const xhr = new XMLHttpRequest();
761
- const endPointUrl = _endpointUrl;
762
- try {
763
- xhr[DisabledPropertyName] = true;
764
- } catch(e) {
765
- // If the environment has locked down the XMLHttpRequest (preventExtensions and/or freeze), this would
766
- // cause the request to fail and we no telemetry would be sent
767
- }
768
- xhr.open("POST", endPointUrl, isAsync);
769
- xhr.setRequestHeader("Content-type", "application/json");
770
-
771
- // append Sdk-Context request header only in case of breeze endpoint
772
- if (isInternalApplicationInsightsEndpoint(endPointUrl)) {
773
- xhr.setRequestHeader(RequestHeaders[eRequestHeaders.sdkContextHeader], RequestHeaders[eRequestHeaders.sdkContextHeaderAppIdRequest]);
774
- }
775
-
776
- arrForEach(objKeys(_headers), (headerName) => {
777
- xhr.setRequestHeader(headerName, _headers[headerName]);
778
- });
779
-
780
- xhr.onreadystatechange = () => _self._xhrReadyStateChange(xhr, payload, payload.length);
781
- xhr.onerror = (event: ErrorEvent|any) => _self._onError(payload, _formatErrorMessageXhr(xhr), event);
782
-
783
- // compose an array of payloads
784
- const batch = _self._buffer.batchPayloads(payload);
785
- xhr.send(batch);
786
-
787
- _self._buffer.markAsSent(payload);
788
- }
789
-
790
- function _fetchKeepAliveSender(payload: string[], isAsync: boolean) {
791
- if (isArray(payload)) {
792
- let payloadSize = payload.length;
793
- for (let lp = 0; lp < payload.length; lp++) {
794
- payloadSize += payload[lp].length;
795
- }
796
-
797
- if ((_syncFetchPayload + payloadSize) <= FetchSyncRequestSizeLimitBytes) {
798
- _doFetchSender(payload, false);
799
- } else if (isBeaconsSupported()) {
800
- // Fallback to beacon sender as we at least get told which events can't be scheduled
801
- _beaconSender(payload, isAsync);
802
- } else {
803
- // Payload is going to be too big so just try and send via XHR
804
- _fallbackSender && _fallbackSender(payload, true);
805
- _throwInternal(_self.diagLog(), eLoggingSeverity.WARNING, _eInternalMessageId.TransmissionFailed, ". " + "Failed to send telemetry with Beacon API, retried with xhrSender.");
806
- }
807
- }
808
- }
809
-
810
- /**
811
- * Send fetch API request
812
- * @param payload - {string} - The data payload to be sent.
813
- * @param isAsync - {boolean} - not used
814
- */
815
- function _fetchSender(payload: string[], isAsync: boolean) {
816
- _doFetchSender(payload, true);
817
- }
818
-
819
- /**
820
- * Send fetch API request
821
- * @param payload - {string} - The data payload to be sent.
822
- * @param isAsync - {boolean} - For fetch this identifies whether we are "unloading" (false) or a normal request
823
- */
824
- function _doFetchSender(payload: string[], isAsync: boolean) {
825
- const endPointUrl = _endpointUrl;
826
- const batch = _self._buffer.batchPayloads(payload);
827
- const plainTextBatch = new Blob([batch], { type: "application/json" });
828
- let requestHeaders = new Headers();
829
- let batchLength = batch.length;
830
- let ignoreResponse = false;
831
- let responseHandled = false;
832
-
833
- // append Sdk-Context request header only in case of breeze endpoint
834
- if (isInternalApplicationInsightsEndpoint(endPointUrl)) {
835
- requestHeaders.append(RequestHeaders[eRequestHeaders.sdkContextHeader], RequestHeaders[eRequestHeaders.sdkContextHeaderAppIdRequest]);
836
- }
837
-
838
- arrForEach(objKeys(_headers), (headerName) => {
839
- requestHeaders.append(headerName, _headers[headerName]);
840
- });
841
-
842
- const init: RequestInit = {
843
- method: "POST",
844
- headers: requestHeaders,
845
- body: plainTextBatch,
846
- [DisabledPropertyName]: true // Mark so we don't attempt to track this request
847
- };
848
-
849
- if (!isAsync) {
850
- init.keepalive = true;
851
- // As a sync request (during unload), it is unlikely that we will get a chance to process the response so
852
- // just like beacon send assume that the events have been accepted and processed
853
- ignoreResponse = true;
854
- _syncFetchPayload += batchLength;
855
- }
856
-
857
- const request = new Request(endPointUrl, init);
858
- try {
859
- // Also try and tag the request (just in case the value in init is not copied over)
860
- request[DisabledPropertyName] = true;
861
- } catch(e) {
862
- // If the environment has locked down the XMLHttpRequest (preventExtensions and/or freeze), this would
863
- // cause the request to fail and we no telemetry would be sent
864
- }
865
-
866
- _self._buffer.markAsSent(payload);
867
-
868
- try {
869
- fetch(request).then(response => {
870
- if (!isAsync) {
871
- _syncFetchPayload -= batchLength;
872
- batchLength = 0;
873
- }
874
-
875
- if (!responseHandled) {
876
- responseHandled = true;
877
-
878
- /**
879
- * The Promise returned from fetch() won’t reject on HTTP error status even if the response is an HTTP 404 or 500.
880
- * Instead, it will resolve normally (with ok status set to false), and it will only reject on network failure
881
- * or if anything prevented the request from completing.
882
- */
883
- if (!response.ok) {
884
- _self._onError(payload, response.statusText)
885
- } else {
886
- response.text().then(text => {
887
- _checkResponsStatus(response.status, payload, response.url, payload.length, response.statusText, text);
888
- });
889
- }
890
- }
891
- }).catch((error: Error) => {
892
- if (!isAsync) {
893
- _syncFetchPayload -= batchLength;
894
- batchLength = 0;
895
- }
896
-
897
- if (!responseHandled) {
898
- responseHandled = true;
899
- _self._onError(payload, error.message)
900
- }
901
- });
902
- } catch (e) {
903
- if (!responseHandled) {
904
- _self._onError(payload, dumpObj(e));
905
- }
906
- }
907
-
908
- if (ignoreResponse && !responseHandled) {
909
- // Assume success during unload processing as we most likely won't get the response
910
- responseHandled = true;
911
- _self._onSuccess(payload, payload.length);
912
- }
913
- }
914
-
915
- /**
916
- * Parses the response from the backend.
917
- * @param response - XMLHttpRequest or XDomainRequest response
918
- */
919
- function _parseResponse(response: any): IBackendResponse {
920
- try {
921
- if (response && response !== "") {
922
- const result = getJSON().parse(response);
923
-
924
- if (result && result.itemsReceived && result.itemsReceived >= result.itemsAccepted &&
925
- result.itemsReceived - result.itemsAccepted === result.errors.length) {
926
- return result;
927
- }
928
- }
929
- } catch (e) {
930
- _throwInternal(_self.diagLog(),
931
- eLoggingSeverity.CRITICAL,
932
- _eInternalMessageId.InvalidBackendResponse,
933
- "Cannot parse the response. " + getExceptionName(e),
934
- {
935
- response
936
- });
937
- }
938
-
939
- return null;
940
- }
941
-
942
- /**
943
- * Resend payload. Adds payload back to the send buffer and setup a send timer (with exponential backoff).
944
- * @param payload
945
- */
946
- function _resendPayload(payload: string[], linearFactor: number = 1) {
947
- if (!payload || payload.length === 0) {
948
- return;
949
- }
950
-
951
- const buffer = _self._buffer;
952
- buffer.clearSent(payload);
953
- _consecutiveErrors++;
954
-
955
- for (const item of payload) {
956
- buffer.enqueue(item);
957
- }
958
-
959
- // setup timer
960
- _setRetryTime(linearFactor);
961
- _setupTimer();
962
- }
963
-
964
- /**
965
- * Calculates the time to wait before retrying in case of an error based on
966
- * http://en.wikipedia.org/wiki/Exponential_backoff
967
- */
968
- function _setRetryTime(linearFactor: number) {
969
- const SlotDelayInSeconds = 10;
970
- let delayInSeconds: number;
971
-
972
- if (_consecutiveErrors <= 1) {
973
- delayInSeconds = SlotDelayInSeconds;
974
- } else {
975
- const backOffSlot = (Math.pow(2, _consecutiveErrors) - 1) / 2;
976
- // tslint:disable-next-line:insecure-random
977
- let backOffDelay = Math.floor(Math.random() * backOffSlot * SlotDelayInSeconds) + 1;
978
- backOffDelay = linearFactor * backOffDelay;
979
- delayInSeconds = Math.max(Math.min(backOffDelay, 3600), SlotDelayInSeconds);
980
- }
981
-
982
- // TODO: Log the backoff time like the C# version does.
983
- const retryAfterTimeSpan = dateNow() + (delayInSeconds * 1000);
984
-
985
- // TODO: Log the retry at time like the C# version does.
986
- _retryAt = retryAfterTimeSpan;
987
- }
988
-
989
- /**
990
- * Sets up the timer which triggers actually sending the data.
991
- */
992
- function _setupTimer() {
993
- if (!_timeoutHandle && !_paused) {
994
- const retryInterval = _retryAt ? Math.max(0, _retryAt - dateNow()) : 0;
995
- const timerValue = Math.max(_maxBatchInterval, retryInterval);
996
-
997
- _timeoutHandle = scheduleTimeout(() => {
998
- _timeoutHandle = null;
999
- _self.triggerSend(true, null, SendRequestReason.NormalSchedule);
1000
- }, timerValue);
1001
- }
1002
- }
1003
-
1004
- function _clearScheduledTimer() {
1005
- _timeoutHandle && _timeoutHandle.cancel();
1006
- _timeoutHandle = null;
1007
- _retryAt = null;
1008
- }
1009
-
1010
- /**
1011
- * Checks if the SDK should resend the payload after receiving this status code from the backend.
1012
- * @param statusCode
1013
- */
1014
- function _isRetriable(statusCode: number): boolean {
1015
- return statusCode === 401 // Unauthorized
1016
- || statusCode === 403 // Forbidden
1017
- || statusCode === 408 // Timeout
1018
- || statusCode === 429 // Too many requests.
1019
- || statusCode === 500 // Internal server error.
1020
- || statusCode === 502 // Bad Gateway.
1021
- || statusCode === 503 // Service unavailable.
1022
- || statusCode === 504; // Gateway timeout.
1023
- }
1024
-
1025
- function _formatErrorMessageXhr(xhr: XMLHttpRequest, message?: string): string {
1026
- if (xhr) {
1027
- return "XMLHttpRequest,Status:" + xhr.status + ",Response:" + _getResponseText(xhr) || xhr.response || "";
1028
- }
1029
-
1030
- return message;
1031
- }
1032
-
1033
- /**
1034
- * Send XDomainRequest
1035
- * @param payload - {string} - The data payload to be sent.
1036
- * @param isAsync - {boolean} - Indicates if the request should be sent asynchronously
1037
- *
1038
- * Note: XDomainRequest does not support sync requests. This 'isAsync' parameter is added
1039
- * to maintain consistency with the xhrSender's contract
1040
- * Note: XDomainRequest does not support custom headers and we are not able to get
1041
- * appId from the backend for the correct correlation.
1042
- */
1043
- function _xdrSender(payload: string[], isAsync: boolean) {
1044
- const buffer = _self._buffer;
1045
- let _window = getWindow();
1046
- const xdr = new XDomainRequest();
1047
- // NOTE: xdr may send previous retry payload to new endpoint since we are not able to check response URL
1048
- xdr.onload = () => _self._xdrOnLoad(xdr, payload);
1049
- xdr.onerror = (event: ErrorEvent|any) => _self._onError(payload, _formatErrorMessageXdr(xdr), event);
1050
-
1051
- // XDomainRequest requires the same protocol as the hosting page.
1052
- // If the protocol doesn't match, we can't send the telemetry :(.
1053
- const hostingProtocol = _window && _window.location && _window.location.protocol || "";
1054
- if (_endpointUrl.lastIndexOf(hostingProtocol, 0) !== 0) {
1055
- _throwInternal(_self.diagLog(),
1056
- eLoggingSeverity.WARNING,
1057
- _eInternalMessageId.TransmissionFailed, ". " +
1058
- "Cannot send XDomain request. The endpoint URL protocol doesn't match the hosting page protocol.");
1059
-
1060
- buffer.clear();
1061
- return;
1062
- }
1063
-
1064
- const endpointUrl = _endpointUrl.replace(/^(https?:)/, "");
1065
- xdr.open("POST", endpointUrl);
1066
-
1067
- // compose an array of payloads
1068
- const batch = buffer.batchPayloads(payload);
1069
- xdr.send(batch);
1070
-
1071
- buffer.markAsSent(payload);
1072
- }
1073
-
1074
- function _formatErrorMessageXdr(xdr: IXDomainRequest, message?: string): string {
1075
- if (xdr) {
1076
- return "XDomainRequest,Response:" + _getResponseText(xdr) || "";
1077
- }
1078
-
1079
- return message;
1080
- }
1081
-
1082
- // Using function lookups for backward compatibility as the getNotifyMgr() did not exist until after v2.5.6
1083
- function _getNotifyMgr() : INotificationManager {
1084
- const func = "getNotifyMgr";
1085
- if (_self.core[func]) {
1086
- return _self.core[func]();
1087
- }
1088
-
1089
- // using _self.core['_notificationManager'] for backward compatibility
1090
- return _self.core["_notificationManager"];
1091
- }
1092
-
1093
- function _notifySendRequest(sendRequest: SendRequestReason, isAsync: boolean) {
1094
- let manager = _getNotifyMgr();
1095
- if (manager && manager.eventsSendRequest) {
1096
- try {
1097
- manager.eventsSendRequest(sendRequest, isAsync);
1098
- } catch (e) {
1099
- _throwInternal(_self.diagLog(), eLoggingSeverity.CRITICAL,
1100
- _eInternalMessageId.NotificationException,
1101
- "send request notification failed: " + getExceptionName(e),
1102
- { exception: dumpObj(e) });
1103
- }
1104
- }
1105
- }
1106
-
1107
- /**
1108
- * Validate UUID Format
1109
- * Specs taken from https://tools.ietf.org/html/rfc4122 and breeze repo
1110
- */
1111
- function _validateInstrumentationKey(instrumentationKey: string, config: IConfiguration & IConfig) :boolean {
1112
- let disableValidation = config.disableInstrumentationKeyValidation;
1113
- const disableIKeyValidationFlag = isNullOrUndefined(disableValidation) ? false : disableValidation;
1114
- if(disableIKeyValidationFlag) {
1115
- return true;
1116
- }
1117
-
1118
- const UUID_Regex = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$";
1119
- const regexp = new RegExp(UUID_Regex);
1120
- return regexp.test(instrumentationKey);
1121
- }
1122
-
1123
- function _initDefaults() {
1124
- _self._sender = null;
1125
- _self._buffer = null;
1126
- _self._appId = null;
1127
- _self._sample = null;
1128
- _headers = {};
1129
- _offlineListener = null;
1130
- _consecutiveErrors = 0;
1131
- _retryAt = null;
1132
- _lastSend = null;
1133
- _paused = false;
1134
- _timeoutHandle = null;
1135
- _serializer = null;
1136
- _stamp_specific_redirects = 0;
1137
- _syncFetchPayload = 0;
1138
- _fallbackSender = null;
1139
- _syncUnloadSender = null;
1140
- _evtNamespace = null;
1141
- _endpointUrl = null;
1142
- _orgEndpointUrl = null;
1143
- _maxBatchSizeInBytes = 0;
1144
- _beaconSupported = false;
1145
- _customHeaders = null;
1146
- _disableTelemetry = false;
1147
- _instrumentationKey = null;
1148
- _convertUndefined = UNDEFINED_VALUE;
1149
- _isRetryDisabled = false;
1150
- _sessionStorageUsed = null;
1151
- _namePrefix = UNDEFINED_VALUE;
1152
-
1153
- objDefine(_self, "_senderConfig", {
1154
- g: function() {
1155
- return objExtend({}, defaultAppInsightsChannelConfig);
1156
- }
1157
- });
1158
- }
1159
- });
1160
- }
1161
-
1162
- /**
1163
- * Pause the sending (transmission) of events, this will cause all events to be batched only until the maximum limits are
1164
- * hit at which point new events are dropped. Will also cause events to NOT be sent during page unload, so if Session storage
1165
- * is disabled events will be lost.
1166
- * SessionStorage Limit is 2000 events, In-Memory (Array) Storage is 10,000 events (can be configured via the eventsLimitInMem).
1167
- */
1168
- public pause(): void {
1169
- // @DynamicProtoStub -- DO NOT add any code as this will be removed during packaging
1170
- }
1171
-
1172
- /**
1173
- * Resume the sending (transmission) of events, this will restart the timer and any batched events will be sent using the normal
1174
- * send interval.
1175
- */
1176
- public resume(): void {
1177
- // @DynamicProtoStub -- DO NOT add any code as this will be removed during packaging
1178
- }
1179
-
1180
- /**
1181
- * Flush the batched events immediately (not synchronously).
1182
- * Will not flush if the Sender has been paused.
1183
- */
1184
- public flush() {
1185
- // @DynamicProtoStub -- DO NOT add any code as this will be removed during packaging
1186
- }
1187
-
1188
- /**
1189
- * Flush the batched events synchronously (if possible -- based on configuration).
1190
- * Will not flush if the Send has been paused.
1191
- */
1192
- public onunloadFlush() {
1193
- // @DynamicProtoStub -- DO NOT add any code as this will be removed during packaging
1194
- }
1195
-
1196
- public initialize(config: IConfiguration & IConfig, core: IAppInsightsCore, extensions: IPlugin[], pluginChain?:ITelemetryPluginChain): void {
1197
- // @DynamicProtoStub -- DO NOT add any code as this will be removed during packaging
1198
- }
1199
-
1200
- public processTelemetry(telemetryItem: ITelemetryItem, itemCtx?: IProcessTelemetryContext) {
1201
- // @DynamicProtoStub -- DO NOT add any code as this will be removed during packaging
1202
- }
1203
-
1204
- /**
1205
- * xhr state changes
1206
- */
1207
- public _xhrReadyStateChange(xhr: XMLHttpRequest, payload: string[], countOfItemsInPayload: number) {
1208
- // @DynamicProtoStub -- DO NOT add any code as this will be removed during packaging
1209
- }
1210
-
1211
- /**
1212
- * Immediately send buffered data
1213
- * @param async - {boolean} - Indicates if the events should be sent asynchronously
1214
- * @param forcedSender - {SenderFunction} - Indicates the forcedSender, undefined if not passed
1215
- */
1216
- public triggerSend(async = true, forcedSender?: SenderFunction, sendReason?: SendRequestReason) {
1217
- // @DynamicProtoStub -- DO NOT add any code as this will be removed during packaging
1218
- }
1219
-
1220
- /**
1221
- * error handler
1222
- */
1223
- public _onError(payload: string[], message: string, event?: ErrorEvent) {
1224
- // @DynamicProtoStub -- DO NOT add any code as this will be removed during packaging
1225
- }
1226
-
1227
- /**
1228
- * partial success handler
1229
- */
1230
- public _onPartialSuccess(payload: string[], results: IBackendResponse) {
1231
- // @DynamicProtoStub -- DO NOT add any code as this will be removed during packaging
1232
- }
1233
-
1234
- /**
1235
- * success handler
1236
- */
1237
- public _onSuccess(payload: string[], countOfItemsInPayload: number) {
1238
- // @DynamicProtoStub -- DO NOT add any code as this will be removed during packaging
1239
- }
1240
-
1241
- /**
1242
- * xdr state changes
1243
- */
1244
- public _xdrOnLoad(xdr: IXDomainRequest, payload: string[]) {
1245
- // @DynamicProtoStub -- DO NOT add any code as this will be removed during packaging
1246
- }
1247
-
1248
- /**
1249
- * Add header to request
1250
- * @param name - Header name.
1251
- * @param value - Header value.
1252
- */
1253
- public addHeader(name: string, value: string) {
1254
- // @DynamicProtoStub - DO NOT add any code as this will be removed during packaging
1255
- }
1256
- }