Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Send Request notification #1278

Merged
merged 2 commits into from Jun 2, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 3 additions & 2 deletions AISKULight/index.ts
Expand Up @@ -8,7 +8,8 @@ import {
IAppInsightsCore,
_InternalMessageId,
CoreUtils,
ITelemetryItem
ITelemetryItem,
SendRequestReason
} from "@microsoft/applicationinsights-core-js";
import { IConfig } from "@microsoft/applicationinsights-common";
import { Sender } from "@microsoft/applicationinsights-channel-js";
Expand Down Expand Up @@ -83,7 +84,7 @@ export class ApplicationInsights {
CoreUtils.arrForEach(controls, plugin => {
async
? (plugin as Sender).flush()
: (plugin as Sender).triggerSend(async);
: (plugin as Sender).triggerSend(async, null, SendRequestReason.ManualFlush);
});
});
}
Expand Down
106 changes: 105 additions & 1 deletion channels/applicationinsights-channel-js/Tests/Sender.tests.ts
Expand Up @@ -3,7 +3,7 @@ import { Sender } from "../src/Sender";
import { Offline } from '../src/Offline';
import { EnvelopeCreator } from '../src/EnvelopeCreator';
import { Exception, CtxTagKeys, Util } from "@microsoft/applicationinsights-common";
import { ITelemetryItem, AppInsightsCore, ITelemetryPlugin, DiagnosticLogger } from "@microsoft/applicationinsights-core-js";
import { ITelemetryItem, AppInsightsCore, ITelemetryPlugin, DiagnosticLogger, NotificationManager, SendRequestReason } from "@microsoft/applicationinsights-core-js";

export class SenderTests extends TestClass {
private _sender: Sender;
Expand Down Expand Up @@ -837,5 +837,109 @@ export class SenderTests extends TestClass {
Assert.equal("Microsoft.ApplicationInsights.iKey.Pageview", appInsightsEnvelope.name);
}
});

this.testCase({
name: "Channel Config: is sent when requests are being sent when requests exceed max batch size",
MSNev marked this conversation as resolved.
Show resolved Hide resolved
test: () => {
let sendNotifications = [];
let notificationManager = new NotificationManager();
notificationManager.addNotificationListener({
eventsSendRequest: (sendReason: number, isAsync?: boolean) => {
sendNotifications.push({
sendReason,
isAsync
});
}
});

this._sender.initialize(
{
instrumentationKey: 'abc',
maxBatchInterval: 123,
endpointUrl: 'https://example.com',
maxBatchSizeInBytes: 100,
extensionConfig: {
NotificationManager: notificationManager,
[this._sender.identifier]: {
maxBatchSizeInBytes: 100
}
}

}, new AppInsightsCore(), []
);

const loggerSpy = this.sandbox.stub(this._sender, "_setupTimer");
const telemetryItem: ITelemetryItem = {
name: 'fake item',
iKey: 'iKey',
baseType: 'some type',
baseData: {}
};
try {
this._sender.processTelemetry(telemetryItem, null);
} catch(e) {
Assert.ok(false);
}


Assert.ok(loggerSpy.calledOnce);
this.clock.tick(1);
Assert.ok(sendNotifications.length === 1);
Assert.ok(sendNotifications[0].sendReason === SendRequestReason.MaxBatchSize);
}
});

this.testCase({
name: "Channel Config: Notification is sent when requests are being sent with manual flush",
test: () => {
let sendNotifications = [];
let notificationManager = new NotificationManager();
notificationManager.addNotificationListener({
eventsSendRequest: (sendReason: number, isAsync?: boolean) => {
sendNotifications.push({
sendReason,
isAsync
});
}
});

this._sender.initialize(
{
instrumentationKey: 'abc',
maxBatchInterval: 123,
endpointUrl: 'https://example.com',
extensionConfig: {
NotificationManager: notificationManager
}

}, new AppInsightsCore(), []
);

const loggerSpy = this.sandbox.stub(this._sender, "_setupTimer");
const telemetryItem: ITelemetryItem = {
name: 'fake item',
iKey: 'iKey',
baseType: 'some type',
baseData: {}
};
try {
this._sender.processTelemetry(telemetryItem, null);
} catch(e) {
Assert.ok(false);
}

Assert.ok(loggerSpy.calledOnce);
Assert.equal(0, sendNotifications.length);

this._sender.flush();
Assert.equal(0, sendNotifications.length);

this.clock.tick(1);

Assert.equal(1, sendNotifications.length);
Assert.equal(SendRequestReason.ManualFlush, sendNotifications[0].sendReason);
}
});

}
}
33 changes: 26 additions & 7 deletions channels/applicationinsights-channel-js/src/Sender.ts
Expand Up @@ -20,7 +20,8 @@ import {
import {
ITelemetryItem, IProcessTelemetryContext, IConfiguration, CoreUtils,
_InternalMessageId, LoggingSeverity, IDiagnosticLogger, IAppInsightsCore, IPlugin,
getWindow, getNavigator, getJSON, BaseTelemetryPlugin, ITelemetryPluginChain
getWindow, getNavigator, getJSON, BaseTelemetryPlugin, ITelemetryPluginChain, INotificationManager,
SendRequestReason
} from '@microsoft/applicationinsights-core-js';
import { Offline } from './Offline';
import { Sample } from './TelemetryProcessors/Sample'
Expand Down Expand Up @@ -162,6 +163,8 @@ export class Sender extends BaseTelemetryPlugin implements IChannelControlsAI {

private _serializer: Serializer;

private _notificationManager: INotificationManager | undefined;

public pause(): void {
throw new Error("Method not implemented.");
}
Expand All @@ -172,7 +175,7 @@ export class Sender extends BaseTelemetryPlugin implements IChannelControlsAI {

public flush() {
try {
this.triggerSend();
this.triggerSend(true, null, SendRequestReason.ManualFlush);
} catch (e) {
this.diagLog().throwInternal(LoggingSeverity.CRITICAL,
_InternalMessageId.FlushFailed,
Expand All @@ -184,7 +187,7 @@ export class Sender extends BaseTelemetryPlugin implements IChannelControlsAI {
public onunloadFlush() {
if ((this._senderConfig.onunloadDisableBeacon() === false || this._senderConfig.isBeaconApiDisabled() === false) && Util.IsBeaconApiSupported()) {
try {
this.triggerSend(true, this._beaconSender);
this.triggerSend(true, this._beaconSender, SendRequestReason.Unload);
} catch (e) {
this.diagLog().throwInternal(LoggingSeverity.CRITICAL,
_InternalMessageId.FailedToSendQueuedTelemetry,
Expand All @@ -205,7 +208,7 @@ export class Sender extends BaseTelemetryPlugin implements IChannelControlsAI {
let ctx = this._getTelCtx();
let identifier = this.identifier;
this._serializer = new Serializer(core.logger);

this._notificationManager = ((config||{}).extensionConfig||{}).NotificationManager
this._consecutiveErrors = 0;
this._retryAt = null;
this._lastSend = 0;
Expand Down Expand Up @@ -317,7 +320,7 @@ export class Sender extends BaseTelemetryPlugin implements IChannelControlsAI {
const batch = this._buffer.batchPayloads(bufferPayload);

if (batch && (batch.length + payload.length > this._senderConfig.maxBatchSizeInBytes())) {
this.triggerSend();
this.triggerSend(true, null, SendRequestReason.MaxBatchSize);
}

// enqueue the payload
Expand Down Expand Up @@ -396,14 +399,16 @@ export class Sender extends BaseTelemetryPlugin implements IChannelControlsAI {
* @param async {boolean} - Indicates if the events should be sent asynchronously
* @param forcedSender {SenderFunction} - Indicates the forcedSender, undefined if not passed
*/
public triggerSend(async = true, forcedSender?: SenderFunction) {
public triggerSend(async = true, forcedSender?: SenderFunction, sendReason?: SendRequestReason) {
try {
// Send data only if disableTelemetry is false
if (!this._senderConfig.disableTelemetry()) {

if (this._buffer.count() > 0) {
const payload = this._buffer.getItems();

this._notifySendRequest(sendReason||SendRequestReason.Undefined, async);

// invoke send
if (forcedSender) {
forcedSender.call(this, payload, async);
Expand Down Expand Up @@ -658,7 +663,7 @@ export class Sender extends BaseTelemetryPlugin implements IChannelControlsAI {
const timerValue = Math.max(this._senderConfig.maxBatchInterval(), retryInterval);

this._timeoutHandle = setTimeout(() => {
this.triggerSend();
this.triggerSend(true, null, SendRequestReason.NormalSchedule);
}, timerValue);
}
}
Expand Down Expand Up @@ -728,4 +733,18 @@ export class Sender extends BaseTelemetryPlugin implements IChannelControlsAI {

return message;
}

private _notifySendRequest(sendRequest: SendRequestReason, isAsync: boolean) {
let manager = this._notificationManager;
if (manager && manager.eventsSendRequest) {
try {
manager.eventsSendRequest(sendRequest, isAsync);
} catch (e) {
this.diagLog().throwInternal(LoggingSeverity.CRITICAL,
_InternalMessageId.NotificationException,
"send request notification failed: " + Util.getExceptionName(e),
{ exception: Util.dump(e) });
}
}
}
}
48 changes: 24 additions & 24 deletions common/config/rush/npm-shrinkwrap.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Expand Up @@ -93,6 +93,7 @@ export const _InternalMessageId = {
FailedMonitorAjaxSetRequestHeader: 71,
SendBrowserInfoOnUserInit: 72,
PluginException: 73,
NotificationException: 74,
SnippetScriptLoadFailure: 99
};
export type _InternalMessageId = number | typeof _InternalMessageId;
@@ -0,0 +1,46 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
/**
* The EventsDiscardedReason enumeration contains a set of values that specify the reason for discarding an event.
*/
export const enum SendRequestReason {
/**
* No specific reason was specified
*/
Undefined = 0,

/**
* Events are being sent based on the normal event schedule / timer.
*/
NormalSchedule = 1,

/**
* A manual flush request was received
*/
ManualFlush = 1,

/**
* Unload event is being processed
*/
Unload = 2,

/**
* The event(s) being sent are sync events
*/
SyncEvent = 3,

/**
* The Channel was resumed
*/
Resumed = 4,

/**
* Maximum batch size would be exceeded
*/
MaxBatchSize = 10,

/**
* The Maximum number of events have already been queued
*/
MaxQueuedEvents = 20
};