Upgrade appinsights (#58999)

* Upgrade appinsights

* Env, global updates before starting shared process

* Disable console logging from appinsights Fixes #55417

* Move workaround to helper
This commit is contained in:
Ramya Rao 2018-09-22 23:09:48 -07:00 committed by GitHub
parent d5d8d98cf3
commit ae526292df
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 330 additions and 132 deletions

View file

@ -105,18 +105,27 @@ function submitAllStats(productJson, commit) {
.setAutoCollectExceptions(false)
.setAutoCollectPerformance(false)
.setAutoCollectRequests(false)
.setAutoCollectDependencies(false)
.setAutoDependencyCorrelation(false)
.start();
var client = appInsights.getClient(productJson.aiConfig.asimovKey);
client.config.endpointUrl = 'https://vortex.data.microsoft.com/collect/v1';
appInsights.defaultClient.config.endpointUrl = 'https://vortex.data.microsoft.com/collect/v1';
/* __GDPR__
"monacoworkbench/packagemetrics" : {
"commit" : {"classification": "SystemMetaData", "purpose": "PerformanceAndHealth" },
"size" : {"classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true },
"count" : {"classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }
"size" : {"classification": "SystemMetaData", "purpose": "PerformanceAndHealth" },
"count" : {"classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }
}
*/
client.trackEvent("monacoworkbench/packagemetrics", { commit: commit, size: JSON.stringify(sizes), count: JSON.stringify(counts) });
client.sendPendingData(function () { return resolve(); });
appInsights.defaultClient.trackEvent({
name: 'monacoworkbench/packagemetrics',
properties: { commit: commit, size: JSON.stringify(sizes), count: JSON.stringify(counts) }
});
appInsights.defaultClient.flush({
callback: function () {
appInsights.dispose();
resolve();
}
});
});
}
exports.submitAllStats = submitAllStats;

View file

@ -105,25 +105,36 @@ export function submitAllStats(productJson: any, commit: string): Promise<void>
counts[entry.name] = entry.totalCount;
}
appInsights.setup(productJson.aiConfig.asimovKey)
.setAutoCollectConsole(false)
.setAutoCollectExceptions(false)
.setAutoCollectPerformance(false)
.setAutoCollectRequests(false)
.start();
appInsights.setup(productJson.aiConfig.asimovKey)
.setAutoCollectConsole(false)
.setAutoCollectExceptions(false)
.setAutoCollectPerformance(false)
.setAutoCollectRequests(false)
.setAutoCollectDependencies(false)
.setAutoDependencyCorrelation(false)
.start();
const client = appInsights.getClient(productJson.aiConfig.asimovKey);
client.config.endpointUrl = 'https://vortex.data.microsoft.com/collect/v1';
appInsights.defaultClient.config.endpointUrl = 'https://vortex.data.microsoft.com/collect/v1';
/* __GDPR__
"monacoworkbench/packagemetrics" : {
"commit" : {"classification": "SystemMetaData", "purpose": "PerformanceAndHealth" },
"size" : {"classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true },
"count" : {"classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }
}
*/
client.trackEvent(`monacoworkbench/packagemetrics`, { commit, size: JSON.stringify(sizes), count: JSON.stringify(counts) });
client.sendPendingData(() => resolve());
/* __GDPR__
"monacoworkbench/packagemetrics" : {
"commit" : {"classification": "SystemMetaData", "purpose": "PerformanceAndHealth" },
"size" : {"classification": "SystemMetaData", "purpose": "PerformanceAndHealth" },
"count" : {"classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }
}
*/
appInsights.defaultClient.trackEvent({
name: 'monacoworkbench/packagemetrics',
properties: { commit, size: JSON.stringify(sizes), count: JSON.stringify(counts) }
});
appInsights.defaultClient.flush({
callback: () => {
appInsights.dispose();
resolve();
}
});
});
}

View file

@ -25,7 +25,7 @@
"download-builtin-extensions": "node build/lib/builtInExtensions.js"
},
"dependencies": {
"applicationinsights": "0.18.0",
"applicationinsights": "1.0.3",
"fast-plist": "0.1.2",
"gc-signals": "^0.0.1",
"getmac": "1.4.1",

11
src/bootstrap.js vendored
View file

@ -217,4 +217,15 @@ exports.configurePortable = function () {
isPortable
};
};
//#endregion
//#region ApplicationInsights
/**
* Prevents appinsights from monkey patching modules.
* This should be called before importing the applicationinsights module
*/
exports.avoidMonkeyPatchFromAppInsights = function () {
process.env['APPLICATION_INSIGHTS_NO_DIAGNOSTIC_CHANNEL'] = true; // Skip monkey patching of 3rd party modules by appinsights
global['diagnosticsSource'] = {}; // Prevents diagnostic channel (which patches "require") from initializing entirely
};
//#endregion

View file

@ -8,6 +8,9 @@
const bootstrap = require('./bootstrap');
// Avoid Monkey Patches from Application Insights
bootstrap.avoidMonkeyPatchFromAppInsights();
// Enable portable support
bootstrap.configurePortable();

View file

@ -3,64 +3,216 @@
* the auto-collection behavior of the application insights module.
*/
declare module ApplicationInsights {
var client: any;
/**
* Initializes a client with the given instrumentation key, if this is not specified, the value will be
* read from the environment variable APPINSIGHTS_INSTRUMENTATIONKEY
* @returns {ApplicationInsights/Client} a new client
*/
function getClient(instrumentationKey?: string): any /*Client*/;
/**
* Initializes the default client of the client and sets the default configuration
* @param instrumentationKey the instrumentation key to use. Optional, if this is not specified, the value will be
* read from the environment variable APPINSIGHTS_INSTRUMENTATIONKEY
* @returns {ApplicationInsights} this class
*/
function setup(instrumentationKey?: string): typeof ApplicationInsights;
/**
* Starts automatic collection of telemetry. Prior to calling start no telemetry will be collected
* @returns {ApplicationInsights} this class
*/
function start(): typeof ApplicationInsights;
/**
* Sets the state of console tracking (enabled by default)
* @param value if true console activity will be sent to Application Insights
* @returns {ApplicationInsights} this class
*/
function setAutoCollectConsole(value: boolean): typeof ApplicationInsights;
/**
* Sets the state of exception tracking (enabled by default)
* @param value if true uncaught exceptions will be sent to Application Insights
* @returns {ApplicationInsights} this class
*/
function setAutoCollectExceptions(value: boolean): typeof ApplicationInsights;
/**
* Sets the state of performance tracking (enabled by default)
* @param value if true performance counters will be collected every second and sent to Application Insights
* @returns {ApplicationInsights} this class
*/
function setAutoCollectPerformance(value: boolean): typeof ApplicationInsights;
/**
* Sets the state of request tracking (enabled by default)
* @param value if true requests will be sent to Application Insights
* @returns {ApplicationInsights} this class
*/
function setAutoCollectRequests(value: boolean): typeof ApplicationInsights;
/**
* Sets the state of enabling offline mode to cache event when client is offline (disabled by default)
* @param value if true events that happen while client is offline will be cahced on disk,
* client will retry to send events when back online
* @returns {ApplicationInsights} this class
*/
function setOfflineMode(value: boolean): typeof ApplicationInsights;
/**
* Enables verbose debug logging
* The default client, initialized when setup was called. To initialize a different client
* with its own configuration, use `new TelemetryClient(instrumentationKey?)`.
*/
var defaultClient: TelemetryClient;
/**
* Initializes the default client. Should be called after setting
* configuration options.
*
* @param instrumentationKey the instrumentation key to use. Optional, if
* this is not specified, the value will be read from the environment
* variable APPINSIGHTS_INSTRUMENTATIONKEY.
* @returns {Configuration} the configuration class to initialize
* and start the SDK.
*/
function setup(instrumentationKey?: string): typeof Configuration;
/**
* Starts automatic collection of telemetry. Prior to calling start no
* telemetry will be *automatically* collected, though manual collection
* is enabled.
* @returns {ApplicationInsights} this class
*/
function enableVerboseLogging(): typeof ApplicationInsights;
function start(): typeof Configuration;
/**
* The active configuration for global SDK behaviors, such as autocollection.
*/
class Configuration {
static start: typeof start;
/**
* Sets the state of console and logger tracking (enabled by default for third-party loggers only)
* @param value if true logger activity will be sent to Application Insights
* @param collectConsoleLog if true, logger autocollection will include console.log calls (default false)
* @returns {Configuration} this class
*/
static setAutoCollectConsole(value: boolean, collectConsoleLog?: boolean): typeof Configuration;
/**
* Sets the state of exception tracking (enabled by default)
* @param value if true uncaught exceptions will be sent to Application Insights
* @returns {Configuration} this class
*/
static setAutoCollectExceptions(value: boolean): typeof Configuration;
/**
* Sets the state of performance tracking (enabled by default)
* @param value if true performance counters will be collected every second and sent to Application Insights
* @returns {Configuration} this class
*/
static setAutoCollectPerformance(value: boolean): typeof Configuration;
/**
* Sets the state of request tracking (enabled by default)
* @param value if true requests will be sent to Application Insights
* @returns {Configuration} this class
*/
static setAutoCollectRequests(value: boolean): typeof Configuration;
/**
* Sets the state of dependency tracking (enabled by default)
* @param value if true dependencies will be sent to Application Insights
* @returns {Configuration} this class
*/
static setAutoCollectDependencies(value: boolean): typeof Configuration;
/**
* Sets the state of automatic dependency correlation (enabled by default)
* @param value if true dependencies will be correlated with requests
* @returns {Configuration} this class
*/
static setAutoDependencyCorrelation(value: boolean): typeof Configuration;
/**
* Enable or disable disk-backed retry caching to cache events when client is offline (enabled by default)
* Note that this method only applies to the default client. Disk-backed retry caching is disabled by default for additional clients.
* For enable for additional clients, use client.channel.setUseDiskRetryCaching(true).
* These cached events are stored in your system or user's temporary directory and access restricted to your user when possible.
* @param value if true events that occured while client is offline will be cached on disk
* @param resendInterval The wait interval for resending cached events.
* @param maxBytesOnDisk The maximum size (in bytes) that the created temporary directory for cache events can grow to, before caching is disabled.
* @returns {Configuration} this class
*/
static setUseDiskRetryCaching(value: boolean, resendInterval?: number, maxBytesOnDisk?: number): typeof Configuration;
/**
* Enables debug and warning logging for AppInsights itself.
* @param enableDebugLogging if true, enables debug logging
* @param enableWarningLogging if true, enables warning logging
* @returns {Configuration} this class
*/
static setInternalLogging(enableDebugLogging?: boolean, enableWarningLogging?: boolean): typeof Configuration;
}
/**
* Disposes the default client and all the auto collectors so they can be reinitialized with different configuration
*/
function dispose(): void;
interface ITelemetryClient {
config: Config;
channel: Channel;
/**
* Log a user action or other occurrence.
* @param telemetry Object encapsulating tracking options
*/
trackEvent(telemetry: EventTelemetry): void;
/**
* Immediately send all queued telemetry.
* @param options Flush options, including indicator whether app is crashing and callback
*/
flush(options?: FlushOptions): void;
}
class TelemetryClient implements ITelemetryClient {
config: Config;
channel: Channel;
/**
* Constructs a new client of the client
* @param iKey the instrumentation key to use (read from environment variable if not specified)
*/
constructor(iKey?: string);
/**
* Log a user action or other occurrence.
* @param telemetry Object encapsulating tracking options
*/
trackEvent(telemetry: EventTelemetry): void;
/**
* Immediately send all queued telemetry.
* @param options Flush options, including indicator whether app is crashing and callback
*/
flush(options?: FlushOptions): void;
}
class Config {
static ENV_azurePrefix: string;
static ENV_iKey: string;
static legacy_ENV_iKey: string;
static ENV_profileQueryEndpoint: string;
static ENV_http_proxy: string;
static ENV_https_proxy: string;
/** An identifier for your Application Insights resource */
instrumentationKey: string;
/** The id for cross-component correlation. READ ONLY. */
correlationId: string;
/** The ingestion endpoint to send telemetry payloads to */
endpointUrl: string;
/** The maximum number of telemetry items to include in a payload to the ingestion endpoint (Default 250) */
maxBatchSize: number;
/** The maximum amount of time to wait for a payload to reach maxBatchSize (Default 15000) */
maxBatchIntervalMs: number;
/** A flag indicating if telemetry transmission is disabled (Default false) */
disableAppInsights: boolean;
/** The percentage of telemetry items tracked that should be transmitted (Default 100) */
samplingPercentage: number;
/** The time to wait before retrying to retrieve the id for cross-component correlation (Default 30000) */
correlationIdRetryIntervalMs: number;
/** A list of domains to exclude from cross-component header injection */
correlationHeaderExcludedDomains: string[];
/** A proxy server for SDK HTTP traffic (Optional, Default pulled from `http_proxy` environment variable) */
proxyHttpUrl: string;
/** A proxy server for SDK HTTPS traffic (Optional, Default pulled from `https_proxy` environment variable) */
proxyHttpsUrl: string;
}
interface Channel {
/**
* Enable or disable disk-backed retry caching to cache events when client is offline (enabled by default)
* These cached events are stored in your system or user's temporary directory and access restricted to your user when possible.
* @param value if true events that occured while client is offline will be cached on disk
* @param resendInterval The wait interval for resending cached events.
* @param maxBytesOnDisk The maximum size (in bytes) that the created temporary directory for cache events can grow to, before caching is disabled.
* @returns {Configuration} this class
*/
setUseDiskRetryCaching(value: boolean, resendInterval?: number, maxBytesOnDisk?: number): void;
}
/**
* Telemetry about the custom event of interest, such application workflow event, business logic event (purchase) and anything that
* you would like to track and aggregate by count. Event can contain measurements such as purchase amount associated with purchase event
*/
interface EventTelemetry {
/**
* Name of the event
*/
name: string;
/**
* Metrics associated with this event, displayed in Metrics Explorer on the portal.
*/
measurements?: {
[key: string]: number;
};
/**
* Additional data used to filter events and metrics in the portal. Defaults to empty.
*/
properties?: {
[key: string]: string;
};
}
/**
* Encapsulates options passed into client.flush() function
*/
interface FlushOptions {
/**
* Flag indicating whether application is crashing. When this flag is set to true
* and storing data locally is enabled, Node.JS SDK will attempt to store data on disk
*/
isAppCrashing?: boolean;
/**
* Callback that will be invoked with the response from server, in case of isAppCrashing set to true,
* with immediate notification that data was stored
*/
callback?: (v: string) => void;
}
}
declare module 'applicationinsights' {
export = ApplicationInsights;
export = ApplicationInsights;
}

View file

@ -6,8 +6,12 @@
//@ts-check
'use strict';
const bootstrap = require('../../../../bootstrap');
const bootstrapWindow = require('../../../../bootstrap-window');
// Avoid Monkey Patches from Application Insights
bootstrap.avoidMonkeyPatchFromAppInsights();
bootstrapWindow.load(['vs/code/electron-browser/sharedProcess/sharedProcessMain'], function (sharedProcess, configuration) {
sharedProcess.startup({
machineId: configuration.machineId

View file

@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as appInsights from 'applicationinsights';
import { isObject } from 'vs/base/common/types';
import { safeStringify, mixin } from 'vs/base/common/objects';
@ -11,28 +12,26 @@ import { TPromise } from 'vs/base/common/winjs.base';
import { ITelemetryAppender } from 'vs/platform/telemetry/common/telemetryUtils';
import { ILogService } from 'vs/platform/log/common/log';
let _initialized = false;
function getClient(aiKey: string): appInsights.TelemetryClient {
function ensureAIEngineIsInitialized(): void {
if (_initialized === false) {
// we need to pass some fake key, otherwise AI throws an exception
appInsights.setup('2588e01f-f6c9-4cd6-a348-143741f8d702')
.setAutoCollectConsole(false)
.setAutoCollectExceptions(false)
let client: appInsights.TelemetryClient;
if (appInsights.defaultClient) {
client = new appInsights.TelemetryClient(aiKey);
client.channel.setUseDiskRetryCaching(true);
} else {
appInsights.setup(aiKey)
.setAutoCollectRequests(false)
.setAutoCollectPerformance(false)
.setAutoCollectRequests(false);
_initialized = true;
.setAutoCollectExceptions(false)
.setAutoCollectDependencies(false)
.setAutoDependencyCorrelation(false)
.setAutoCollectConsole(false)
.setInternalLogging(false, false)
.setUseDiskRetryCaching(true)
.start();
client = appInsights.defaultClient;
}
}
function getClient(aiKey: string): typeof appInsights.client {
ensureAIEngineIsInitialized();
const client = appInsights.getClient(aiKey);
client.channel.setOfflineMode(true);
client.context.tags[client.context.keys.deviceMachineName] = ''; //prevent App Insights from reporting machine name
if (aiKey.indexOf('AIF-') === 0) {
client.config.endpointUrl = 'https://vortex.data.microsoft.com/collect/v1';
}
@ -49,12 +48,12 @@ interface Measurements {
export class AppInsightsAppender implements ITelemetryAppender {
private _aiClient: typeof appInsights.client;
private _aiClient: appInsights.TelemetryClient;
constructor(
private _eventPrefix: string,
private _defaultData: { [key: string]: any },
aiKeyOrClientFactory: string | (() => typeof appInsights.client), // allow factory function for testing
aiKeyOrClientFactory: string | (() => appInsights.ITelemetryClient), // allow factory function for testing
@ILogService private _logService?: ILogService
) {
if (!this._defaultData) {
@ -140,16 +139,22 @@ export class AppInsightsAppender implements ITelemetryAppender {
if (this._logService) {
this._logService.trace(`telemetry/${eventName}`, data);
}
this._aiClient.trackEvent(this._eventPrefix + '/' + eventName, data.properties, data.measurements);
this._aiClient.trackEvent({
name: this._eventPrefix + '/' + eventName,
properties: data.properties,
measurements: data.measurements
});
}
dispose(): TPromise<any> {
if (this._aiClient) {
return new TPromise(resolve => {
this._aiClient.sendPendingData(() => {
// all data flushed
this._aiClient = undefined;
resolve(void 0);
this._aiClient.flush({
callback: () => {
// all data flushed
this._aiClient = undefined;
resolve(void 0);
}
});
});
}

View file

@ -7,35 +7,20 @@
import * as assert from 'assert';
import { AppInsightsAppender } from 'vs/platform/telemetry/node/appInsightsAppender';
import { ILogService, AbstractLogService, LogLevel, DEFAULT_LOG_LEVEL } from 'vs/platform/log/common/log';
import { ITelemetryClient, EventTelemetry } from 'applicationinsights';
interface IAppInsightsEvent {
eventName: string;
properties?: { [x: string]: string; };
measurements?: { [x: string]: number; };
}
class AppInsightsMock {
public events: IAppInsightsEvent[] = [];
class AppInsightsMock implements ITelemetryClient {
public config: any;
public channel: any;
public events: EventTelemetry[] = [];
public IsTrackingPageView: boolean = false;
public exceptions: any[] = [];
public trackEvent(eventName: string, properties?: { string?: string; }, measurements?: { string?: number; }): void {
this.events.push({
eventName,
properties,
measurements
});
}
public trackPageView(): void {
this.IsTrackingPageView = true;
public trackEvent(event: any) {
this.events.push(event);
}
public trackException(exception: any): void {
this.exceptions.push(exception);
}
public sendPendingData(_callback: any): void {
public flush(options: any): void {
// called on dispose
}
}
@ -108,7 +93,7 @@ suite('AIAdapter', () => {
adapter.log('testEvent');
assert.equal(appInsightsMock.events.length, 1);
assert.equal(appInsightsMock.events[0].eventName, `${prefix}/testEvent`);
assert.equal(appInsightsMock.events[0].name, `${prefix}/testEvent`);
});
test('addional data', () => {
@ -117,7 +102,7 @@ suite('AIAdapter', () => {
assert.equal(appInsightsMock.events.length, 1);
let [first] = appInsightsMock.events;
assert.equal(first.eventName, `${prefix}/testEvent`);
assert.equal(first.name, `${prefix}/testEvent`);
assert.equal(first.properties['first'], '1st');
assert.equal(first.measurements['second'], '2');
assert.equal(first.measurements['third'], 1);
@ -154,7 +139,7 @@ suite('AIAdapter', () => {
adapter.log('testEvent', { favoriteDate: date, likeRed: false, likeBlue: true, favoriteNumber: 1, favoriteColor: 'blue', favoriteCars: ['bmw', 'audi', 'ford'] });
assert.equal(appInsightsMock.events.length, 1);
assert.equal(appInsightsMock.events[0].eventName, `${prefix}/testEvent`);
assert.equal(appInsightsMock.events[0].name, `${prefix}/testEvent`);
assert.equal(appInsightsMock.events[0].properties['favoriteColor'], 'blue');
assert.equal(appInsightsMock.events[0].measurements['likeRed'], 0);
assert.equal(appInsightsMock.events[0].measurements['likeBlue'], 1);
@ -183,7 +168,7 @@ suite('AIAdapter', () => {
});
assert.equal(appInsightsMock.events.length, 1);
assert.equal(appInsightsMock.events[0].eventName, `${prefix}/testEvent`);
assert.equal(appInsightsMock.events[0].name, `${prefix}/testEvent`);
assert.equal(appInsightsMock.events[0].properties['window.title'], 'some title');
assert.equal(appInsightsMock.events[0].measurements['window.measurements.width'], 100);

View file

@ -375,9 +375,13 @@ anymatch@^2.0.0:
micromatch "^3.1.4"
normalize-path "^2.1.1"
applicationinsights@0.18.0:
version "0.18.0"
resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-0.18.0.tgz#162ebb48a383408bc4de44db32b417307f45bbc1"
applicationinsights@1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-1.0.3.tgz#ddbf503612cbbfd7c28e087894dd28cfb06450a1"
dependencies:
diagnostic-channel "0.2.0"
diagnostic-channel-publishers "0.2.1"
zone.js "0.7.6"
aproba@^1.0.3, aproba@^1.1.1:
version "1.2.0"
@ -1802,6 +1806,16 @@ detect-newline@2.X:
version "2.1.0"
resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-2.1.0.tgz#f41f1c10be4b00e87b5f13da680759f2c5bfd3e2"
diagnostic-channel-publishers@0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/diagnostic-channel-publishers/-/diagnostic-channel-publishers-0.2.1.tgz#8e2d607a8b6d79fe880b548bc58cc6beb288c4f3"
diagnostic-channel@0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/diagnostic-channel/-/diagnostic-channel-0.2.0.tgz#cc99af9612c23fb1fff13612c72f2cbfaa8d5a17"
dependencies:
semver "^5.3.0"
diff@1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/diff/-/diff-1.4.0.tgz#7f28d2eb9ee7b15a97efd89ce63dcfdaa3ccbabf"
@ -8275,3 +8289,7 @@ yazl@^2.2.1, yazl@^2.2.2, yazl@^2.4.3:
resolved "https://registry.yarnpkg.com/yazl/-/yazl-2.4.3.tgz#ec26e5cc87d5601b9df8432dbdd3cd2e5173a071"
dependencies:
buffer-crc32 "~0.2.3"
zone.js@0.7.6:
version "0.7.6"
resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.7.6.tgz#fbbc39d3e0261d0986f1ba06306eb3aeb0d22009"