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

Beta Part 1: Part of Mega Dynamic Load/Unload support #1766

Merged
merged 1 commit into from
Feb 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion AISKU/Tests/Unit/src/AISKUSize.Tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { AITestClass, Assert } from "@microsoft/ai-test-framework";
import * as pako from "pako";

export class AISKUSizeCheck extends AITestClass {
private readonly MAX_DEFLATE_SIZE = 40;
private readonly MAX_DEFLATE_SIZE = 42;
private readonly rawFilePath = "../dist/applicationinsights-web.min.js";
private readonly prodFilePath = "../browser/ai.2.min.js";

Expand Down
4 changes: 2 additions & 2 deletions channels/applicationinsights-channel-js/src/Sender.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,13 +196,13 @@ export class Sender extends BaseTelemetryPlugin implements IChannelControlsAI {
}
};

_self.flush = () => {
_self.flush = (isAsync: boolean = true, callBack?: () => void, sendReason?: SendRequestReason) => {
if (!_paused) {
// Clear the normal schedule timer as we are going to try and flush ASAP
_clearScheduledTimer();

try {
_self.triggerSend(true, null, SendRequestReason.ManualFlush);
_self.triggerSend(isAsync, null, sendReason || SendRequestReason.ManualFlush);
} catch (e) {
_self.diagLog().throwInternal(LoggingSeverity.CRITICAL,
_InternalMessageId.FlushFailed,
Expand Down
2,296 changes: 1,206 additions & 1,090 deletions common/config/rush/npm-shrinkwrap.json

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export class PageViewManager {
function _flushChannels() {
if (core) {
arrForEach(core.getTransmissionControls(), queues => {
arrForEach(queues, q => q.flush(true))
arrForEach(queues, (q) => { q.flush(true); })
});
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,7 @@ export class ApplicationInsightsCoreTests extends AITestClass {

// TODO: test stopPollingInternalLogs
this.testCase({
name: "DiagnosticLogger: stop Polling InternalLogs",
name: "DiagnosticLogger: stop Polling InternalLogs flushes logs when not empty",
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a functional change, to support "unloading" the complete SDK we want to make sure that when polling is stopped any queued messages are flushed.

useFakeTimers: true,
test: () => {
// Setup
Expand All @@ -375,10 +375,14 @@ export class ApplicationInsightsCoreTests extends AITestClass {

// Act
appInsightsCore.stopPollingInternalLogs();
this.clock.tick(1);

// Assert postcondition
Assert.equal(2, appInsightsCore.logger.queue.length, "Queue is not empty");
// We now flush the internal logs when stop Polling internal logs is called
Assert.equal(0, appInsightsCore.logger.queue.length, "Queue is empty");

queue.push(new _InternalLogMessage(2, "Hello3"));
this.clock.tick(60000);

Assert.equal(1, appInsightsCore.logger.queue.length, "Queue is not empty");
}
});

Expand Down Expand Up @@ -408,7 +412,8 @@ export class ApplicationInsightsCoreTests extends AITestClass {
appInsightsCore.logger.throwInternal(LoggingSeverity.CRITICAL, count, "Test Error");
--count;
}
// this.clock.tick(1000);

// this.clock.tick(1000);
// Assert postcondition
Assert.equal(26, appInsightsCore.logger.queue.length, "Queue is not empty");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,11 @@ import { IDiagnosticLogger } from "./IDiagnosticLogger";
import { IProcessTelemetryContext } from "./IProcessTelemetryContext";
import { IPerfManagerProvider } from "./IPerfManager";
import { ICookieMgr } from "./ICookieMgr";
import { ITelemetryInitializerHandler, TelemetryInitializerFunction } from "./ITelemetryInitializers";

"use strict";
export interface ILoadedPlugin<T extends IPlugin> {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While this seems like overkill, it will have some additional functions added in future "Parts" of this bigger change added.

plugin: T;
}

export interface IAppInsightsCore extends IPerfManagerProvider {

Expand Down Expand Up @@ -72,6 +75,13 @@ export interface IAppInsightsCore extends IPerfManagerProvider {
*/
removeNotificationListener?(listener: INotificationListener): void;

/**
* Add a telemetry processor to decorate or drop telemetry events.
* @param telemetryInitializer - The Telemetry Initializer function
* @returns - A ITelemetryInitializerHandler to enable the initializer to be removed
*/
addTelemetryInitializer(telemetryInitializer: TelemetryInitializerFunction): ITelemetryInitializerHandler | void;
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is moving the telemetry initializer to the Core so all Sku's will have the telemetry initializer functionality not just the Web Analytics.


pollInternalLogs?(eventName?: string): number;

stopPollingInternalLogs?(): void;
Expand All @@ -80,4 +90,10 @@ export interface IAppInsightsCore extends IPerfManagerProvider {
* Return a new instance of the IProcessTelemetryContext for processing events
*/
getProcessTelContext() : IProcessTelemetryContext;

/**
* Find and return the (first) plugin with the specified identifier if present
* @param pluginIdentifier
*/
getPlugin<T extends IPlugin = IPlugin>(pluginIdentifier: string): ILoadedPlugin<T>;
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Creating a unified "lookup" for a plugin, this happens throughout the code by lookup at the passed config. But as we are going to be updating the loaded extensions that's going to get problematic in later PR's

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import { SendRequestReason } from "../JavaScriptSDK.Enums/SendRequestReason";
import { ITelemetryPlugin } from "./ITelemetryPlugin";

"use strict";
Expand All @@ -26,10 +27,13 @@ export interface IChannelControls extends ITelemetryPlugin {

/**
* Flush to send data immediately; channel should default to sending data asynchronously
* @param async: send data asynchronously when true
* @param callBack: if specified, notify caller when send is complete
* @param async - send data asynchronously when true
* @param callBack - if specified, notify caller when send is complete, the channel should return true to indicate to the caller that it will be called.
* If the caller doesn't return true the caller should assume that it may never be called.
* @param sendReason - specify the reason that you are calling "flush" defaults to ManualFlush (1) if not specified
* @returns - true if the callback will be return after the flush is complete otherwise the caller should assume that any provided callback will never be called
*/
flush(async: boolean, callBack?: () => void): void;
flush(async: boolean, callBack?: (flushComplete?: boolean) => void, sendReason?: SendRequestReason): boolean | void;
}

export const MinChannelPriorty: number = 100;
Original file line number Diff line number Diff line change
Expand Up @@ -85,4 +85,9 @@ export interface IInstrumentCallDetails {
* The error (exception) which occurred while executing the original method
*/
err?: Error;

/**
* The Event object from (window.event) at the start of the original call
*/
evt?: Event;
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,15 @@ import { IAppInsightsCore } from "./IAppInsightsCore";
import { IDiagnosticLogger } from "./IDiagnosticLogger";
import { IConfiguration } from "./IConfiguration";
import { ITelemetryItem } from "./ITelemetryItem";
import { IPlugin } from "./ITelemetryPlugin";
import { IPlugin, ITelemetryPlugin } from "./ITelemetryPlugin";
import { ITelemetryPluginChain } from "./ITelemetryPluginChain";

export const enum GetExtCfgMergeType {
None = 0,
MergeDefaultOnly = 1,
MergeDefaultFromRootOrDefault = 2,
}

/**
* The current context for the current call to processTelemetry(), used to support sharing the same plugin instance
* between multiple AppInsights instances
Expand All @@ -27,12 +33,12 @@ export interface IProcessTelemetryContext {
/**
* Gets the current core config instance
*/
getCfg: ()=> IConfiguration;
getCfg: () => IConfiguration;

/**
* Gets the named extension config
*/
getExtCfg: <T>(identifier: string, defaultValue?:T|any) => T;
getExtCfg: <T>(identifier: string, defaultValue?: T | any, mergeDefault?: GetExtCfgMergeType) => T;
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moving some code that was repeated across the code bases into the central location.


/**
* Gets the named config from either the named identifier extension or core config if neither exist then the
Expand All @@ -41,7 +47,7 @@ export interface IProcessTelemetryContext {
* @param field The config field name
* @param defaultValue The default value to return if no defined config exists
*/
getConfig: (identifier: string, field: string, defaultValue?: number | string | boolean) => number | string | boolean;
getConfig: (identifier: string, field: string, defaultValue?: number | string | boolean | string[] | RegExp[] | Function) => number | string | boolean | string[] | RegExp[] | Function;
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding some more of the actual types so that users don't need to say "any" or "unknown"


/**
* Helper to allow plugins to check and possibly shortcut executing code only
Expand All @@ -57,14 +63,21 @@ export interface IProcessTelemetryContext {
/**
* Helper to set the next plugin proxy
*/
setNext: (nextCtx:ITelemetryPluginChain) => void;
setNext: (nextCtx: ITelemetryPluginChain) => void;

/**
* Call back for telemetry processing before it it is sent
* @param env - This is the current event being reported
*/
processNext: (env: ITelemetryItem) => void;

/**
* Synchronously iterate over the context chain running the callback for each plugin, once
* every plugin has been executed via the callback, any associated onComplete will be called.
* @param callback - The function call for each plugin in the context chain
*/
iterate: <T extends ITelemetryPlugin = ITelemetryPlugin>(callback: (plugin: T) => void) => void;
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This iterates over all of the "loaded" plugins, calling the callback function for each plugin


/**
* Create a new context using the core and config from the current instance
* @param plugins - The execution order to process the plugins, if null or not supplied
Expand All @@ -73,4 +86,9 @@ export interface IProcessTelemetryContext {
* order then the next plugin will be NOT set.
*/
createNew: (plugins?:IPlugin[]|ITelemetryPluginChain, startAt?:IPlugin) => IProcessTelemetryContext;

/**
* Set the function to call when the current chain has executed all processNext or unloadNext items.
*/
onComplete: (onComplete: () => void) => void;
Copy link
Collaborator Author

@MSNev MSNev Feb 19, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding a "callback" that will be called once all of the plugins have been processed with the current context.

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { ITelemetryItem } from "./ITelemetryItem";

export declare type TelemetryInitializerFunction = <T extends ITelemetryItem>(item: T) => boolean | void;

export interface ITelemetryInitializerHandler {
remove(): void;
}

export interface ITelemetryInitializerContainer {
/**
* Add a telemetry processor to decorate or drop telemetry events.
* @param telemetryInitializer - The Telemetry Initializer function
* @returns - A ITelemetryInitializerHandler to enable the initializer to be removed
*/
addTelemetryInitializer(telemetryInitializer: TelemetryInitializerFunction): ITelemetryInitializerHandler | void;
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updating the signature to return a "handler" for any added initializer, this will be used to allow the caller to "remove" the added initializer, which is required for "unloading" the entire SDK.

}
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export interface IPlugin {
* @param pluginChain - [Optional] specifies the current plugin chain which identifies the
* set of plugins and the order they should be executed for the current request.
*/
initialize: (config: IConfiguration, core: IAppInsightsCore, extensions: IPlugin[], pluginChain?:ITelemetryPluginChain) => void;
initialize: (config: IConfiguration, core: IAppInsightsCore, extensions: IPlugin[], pluginChain?: ITelemetryPluginChain) => void;

/**
* Returns a value that indicates whether the plugin has already been previously initialized.
Expand Down
103 changes: 2 additions & 101 deletions shared/AppInsightsCore/src/JavaScriptSDK/AppInsightsCore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,20 @@ import { BaseCore } from "./BaseCore";
import { IConfiguration } from "../JavaScriptSDK.Interfaces/IConfiguration";
import { IPlugin } from "../JavaScriptSDK.Interfaces/ITelemetryPlugin";
import { ITelemetryItem } from "../JavaScriptSDK.Interfaces/ITelemetryItem";
import { INotificationListener } from "../JavaScriptSDK.Interfaces/INotificationListener";
import { EventsDiscardedReason } from "../JavaScriptSDK.Enums/EventsDiscardedReason";
import { NotificationManager } from "./NotificationManager";
import { doPerf } from "./PerfManager";
import { INotificationManager } from "../JavaScriptSDK.Interfaces/INotificationManager";
import { IDiagnosticLogger } from "../JavaScriptSDK.Interfaces/IDiagnosticLogger";
import { _InternalLogMessage, DiagnosticLogger } from "./DiagnosticLogger";
import { DiagnosticLogger } from "./DiagnosticLogger";
import dynamicProto from "@microsoft/dynamicproto-js";
import { arrForEach, isNullOrUndefined, toISOString, throwError } from "./HelperFuncs";
import { isNullOrUndefined, throwError } from "./HelperFuncs";

"use strict";

export class AppInsightsCore extends BaseCore implements IAppInsightsCore {
constructor() {
super();
/**
* Internal log poller
*/
let _internalLogPoller: number = 0;
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moving to BaseCore.


dynamicProto(AppInsightsCore, this, (_self, _base) => {

Expand All @@ -46,67 +41,6 @@ export class AppInsightsCore extends BaseCore implements IAppInsightsCore {
}, () => ({ item: telemetryItem }), !((telemetryItem as any).sync));
};

/**
* Adds a notification listener. The SDK calls methods on the listener when an appropriate notification is raised.
* The added plugins must raise notifications. If the plugins do not implement the notifications, then no methods will be
* called.
* @param {INotificationListener} listener - An INotificationListener object.
*/
_self.addNotificationListener = (listener: INotificationListener): void => {
let manager = _self.getNotifyMgr();
if (manager) {
manager.addNotificationListener(listener);
}
};

/**
* Removes all instances of the listener.
* @param {INotificationListener} listener - INotificationListener to remove.
*/
_self.removeNotificationListener = (listener: INotificationListener): void => {
let manager = _self.getNotifyMgr();
if (manager) {
manager.removeNotificationListener(listener);
}
}

/**
* Periodically check logger.queue for log messages to be flushed
*/
_self.pollInternalLogs = (eventName?: string): number => {
let interval = _self.config.diagnosticLogInterval;
if (!interval || !(interval > 0)) {
interval = 10000;
}
if(_internalLogPoller) {
_self.stopPollingInternalLogs();
}
_internalLogPoller = setInterval(() => {
const queue: _InternalLogMessage[] = _self.logger ? _self.logger.queue : [];
arrForEach(queue, (logMessage: _InternalLogMessage) => {
const item: ITelemetryItem = {
name: eventName ? eventName : "InternalMessageId: " + logMessage.messageId,
iKey: _self.config.instrumentationKey,
time: toISOString(new Date()),
baseType: _InternalLogMessage.dataType,
baseData: { message: logMessage.message }
};
_self.track(item);
});
queue.length = 0;
}, interval) as any;
return _internalLogPoller;
}

/**
* Stop polling log messages from logger.queue
*/
_self.stopPollingInternalLogs = (): void => {
if(!_internalLogPoller) return;
clearInterval(_internalLogPoller);
_internalLogPoller = 0;
}

function _validateTelemetryItem(telemetryItem: ITelemetryItem) {
if (isNullOrUndefined(telemetryItem.name)) {
_notifyInvalidEvent(telemetryItem);
Expand All @@ -130,37 +64,4 @@ export class AppInsightsCore extends BaseCore implements IAppInsightsCore {
public track(telemetryItem: ITelemetryItem) {
// @DynamicProtoStub -- DO NOT add any code as this will be removed during packaging
}

/**
* Adds a notification listener. The SDK calls methods on the listener when an appropriate notification is raised.
* The added plugins must raise notifications. If the plugins do not implement the notifications, then no methods will be
* called.
* @param {INotificationListener} listener - An INotificationListener object.
*/
public addNotificationListener(listener: INotificationListener): void {
// @DynamicProtoStub -- DO NOT add any code as this will be removed during packaging
}

/**
* Removes all instances of the listener.
* @param {INotificationListener} listener - INotificationListener to remove.
*/
public removeNotificationListener(listener: INotificationListener): void {
// @DynamicProtoStub -- DO NOT add any code as this will be removed during packaging
}

/**
* Periodically check logger.queue for
*/
public pollInternalLogs(eventName?: string): number {
// @DynamicProtoStub -- DO NOT add any code as this will be removed during packaging
return 0;
}

/**
* Periodically check logger.queue for
*/
public stopPollingInternalLogs(): void {
// @DynamicProtoStub -- DO NOT add any code as this will be removed during packaging
}
}
Loading