mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-05 17:23:51 -05:00
SQL Operations Studio Public Preview 1 (0.23) release source code
This commit is contained in:
152
src/vs/platform/telemetry/node/appInsightsAppender.ts
Normal file
152
src/vs/platform/telemetry/node/appInsightsAppender.ts
Normal file
@@ -0,0 +1,152 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import * as appInsights from 'applicationinsights';
|
||||
import { isObject } from 'vs/base/common/types';
|
||||
import { safeStringify, mixin } from 'vs/base/common/objects';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { ITelemetryAppender } from 'vs/platform/telemetry/common/telemetryUtils';
|
||||
|
||||
let _initialized = false;
|
||||
|
||||
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)
|
||||
.setAutoCollectPerformance(false)
|
||||
.setAutoCollectRequests(false);
|
||||
|
||||
_initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
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';
|
||||
}
|
||||
return client;
|
||||
}
|
||||
|
||||
interface Properties {
|
||||
[key: string]: string;
|
||||
}
|
||||
|
||||
interface Measurements {
|
||||
[key: string]: number;
|
||||
}
|
||||
|
||||
export class AppInsightsAppender implements ITelemetryAppender {
|
||||
|
||||
private _aiClient: typeof appInsights.client;
|
||||
|
||||
constructor(
|
||||
private _eventPrefix: string,
|
||||
private _defaultData: { [key: string]: any },
|
||||
aiKeyOrClientFactory: string | (() => typeof appInsights.client) // allow factory function for testing
|
||||
) {
|
||||
if (!this._defaultData) {
|
||||
this._defaultData = Object.create(null);
|
||||
}
|
||||
|
||||
if (typeof aiKeyOrClientFactory === 'string') {
|
||||
this._aiClient = getClient(aiKeyOrClientFactory);
|
||||
} else if (typeof aiKeyOrClientFactory === 'function') {
|
||||
this._aiClient = aiKeyOrClientFactory();
|
||||
}
|
||||
}
|
||||
|
||||
private static _getData(data?: any): { properties: Properties, measurements: Measurements } {
|
||||
|
||||
const properties: Properties = Object.create(null);
|
||||
const measurements: Measurements = Object.create(null);
|
||||
|
||||
const flat = Object.create(null);
|
||||
AppInsightsAppender._flaten(data, flat);
|
||||
|
||||
for (let prop in flat) {
|
||||
// enforce property names less than 150 char, take the last 150 char
|
||||
prop = prop.length > 150 ? prop.substr(prop.length - 149) : prop;
|
||||
var value = flat[prop];
|
||||
|
||||
if (typeof value === 'number') {
|
||||
measurements[prop] = value;
|
||||
|
||||
} else if (typeof value === 'boolean') {
|
||||
measurements[prop] = value ? 1 : 0;
|
||||
|
||||
} else if (typeof value === 'string') {
|
||||
//enforce property value to be less than 1024 char, take the first 1024 char
|
||||
properties[prop] = value.substring(0, 1023);
|
||||
|
||||
} else if (typeof value !== 'undefined' && value !== null) {
|
||||
properties[prop] = value;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
properties,
|
||||
measurements
|
||||
};
|
||||
}
|
||||
|
||||
private static _flaten(obj: any, result: { [key: string]: any }, order: number = 0, prefix?: string): void {
|
||||
if (!obj) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (var item of Object.getOwnPropertyNames(obj)) {
|
||||
const value = obj[item];
|
||||
const index = prefix ? prefix + item : item;
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
result[index] = safeStringify(value);
|
||||
|
||||
} else if (value instanceof Date) {
|
||||
// TODO unsure why this is here and not in _getData
|
||||
result[index] = value.toISOString();
|
||||
|
||||
} else if (isObject(value)) {
|
||||
if (order < 2) {
|
||||
AppInsightsAppender._flaten(value, result, order + 1, index + '.');
|
||||
} else {
|
||||
result[index] = safeStringify(value);
|
||||
}
|
||||
} else {
|
||||
result[index] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log(eventName: string, data?: any): void {
|
||||
if (!this._aiClient) {
|
||||
return;
|
||||
}
|
||||
data = mixin(data, this._defaultData);
|
||||
let { properties, measurements } = AppInsightsAppender._getData(data);
|
||||
this._aiClient.trackEvent(this._eventPrefix + '/' + eventName, properties, measurements);
|
||||
}
|
||||
|
||||
dispose(): TPromise<any> {
|
||||
if (this._aiClient) {
|
||||
return new TPromise(resolve => {
|
||||
this._aiClient.sendPendingData(() => {
|
||||
// all data flushed
|
||||
this._aiClient = undefined;
|
||||
resolve(void 0);
|
||||
});
|
||||
});
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
50
src/vs/platform/telemetry/node/commonProperties.ts
Normal file
50
src/vs/platform/telemetry/node/commonProperties.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as Platform from 'vs/base/common/platform';
|
||||
import * as os from 'os';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import * as uuid from 'vs/base/common/uuid';
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
import product from 'vs/platform/node/product';
|
||||
|
||||
export const machineIdStorageKey = 'telemetry.machineId';
|
||||
export const machineIdIpcChannel = 'vscode:machineId';
|
||||
|
||||
export function resolveCommonProperties(commit: string, version: string): TPromise<{ [name: string]: string; }> {
|
||||
const result: { [name: string]: string; } = Object.create(null);
|
||||
|
||||
result['sessionID'] = uuid.generateUuid() + Date.now();
|
||||
result['commitHash'] = commit;
|
||||
result['version'] = version;
|
||||
result['common.osVersion'] = os.release();
|
||||
result['common.platform'] = Platform.Platform[Platform.platform];
|
||||
result['common.nodePlatform'] = process.platform;
|
||||
result['common.nodeArch'] = process.arch;
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
result['common.application.name'] = product.nameLong;
|
||||
|
||||
// dynamic properties which value differs on each call
|
||||
let seq = 0;
|
||||
const startTime = Date.now();
|
||||
Object.defineProperties(result, {
|
||||
'timestamp': {
|
||||
get: () => new Date(),
|
||||
enumerable: true
|
||||
},
|
||||
'common.timesincesessionstart': {
|
||||
get: () => Date.now() - startTime,
|
||||
enumerable: true
|
||||
},
|
||||
'common.sequence': {
|
||||
get: () => seq++,
|
||||
enumerable: true
|
||||
}
|
||||
});
|
||||
|
||||
return TPromise.as(result);
|
||||
}
|
||||
144
src/vs/platform/telemetry/node/workbenchCommonProperties.ts
Normal file
144
src/vs/platform/telemetry/node/workbenchCommonProperties.ts
Normal file
@@ -0,0 +1,144 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as winreg from 'winreg';
|
||||
import * as os from 'os';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import * as errors from 'vs/base/common/errors';
|
||||
import * as uuid from 'vs/base/common/uuid';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { getMachineId } from 'vs/base/node/id';
|
||||
import { resolveCommonProperties, machineIdStorageKey } from '../node/commonProperties';
|
||||
|
||||
// {{ SQL CARBON EDIT }}
|
||||
import product from 'vs/platform/node/product';
|
||||
import * as Utils from 'sql/common/telemetryUtilities';
|
||||
|
||||
const SQM_KEY: string = '\\Software\\Microsoft\\SQMClient';
|
||||
|
||||
export function resolveWorkbenchCommonProperties(storageService: IStorageService, commit: string, version: string): TPromise<{ [name: string]: string }> {
|
||||
return resolveCommonProperties(commit, version).then(result => {
|
||||
result['common.version.shell'] = process.versions && (<any>process).versions['electron'];
|
||||
result['common.version.renderer'] = process.versions && (<any>process).versions['chrome'];
|
||||
result['common.osVersion'] = os.release();
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
result['common.application.name'] = product.nameLong;
|
||||
getUserId(storageService).then(value => result['common.userId'] = value);
|
||||
|
||||
const lastSessionDate = storageService.get('telemetry.lastSessionDate');
|
||||
const firstSessionDate = storageService.get('telemetry.firstSessionDate') || new Date().toUTCString();
|
||||
storageService.store('telemetry.firstSessionDate', firstSessionDate);
|
||||
storageService.store('telemetry.lastSessionDate', new Date().toUTCString());
|
||||
|
||||
result['common.firstSessionDate'] = firstSessionDate;
|
||||
result['common.lastSessionDate'] = lastSessionDate;
|
||||
result['common.isNewSession'] = !lastSessionDate ? '1' : '0';
|
||||
|
||||
const promises: TPromise<any>[] = [];
|
||||
promises.push(getOrCreateInstanceId(storageService).then(value => result['common.instanceId'] = value));
|
||||
promises.push(getOrCreateMachineId(storageService).then(value => result['common.machineId'] = value));
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
promises.push(getSqmUserId(storageService).then(value => result['common.sqm.userid'] = value));
|
||||
promises.push(getSqmMachineId(storageService).then(value => result['common.sqm.machineid'] = value));
|
||||
}
|
||||
|
||||
return TPromise.join(promises).then(() => result);
|
||||
});
|
||||
}
|
||||
|
||||
function getOrCreateInstanceId(storageService: IStorageService): TPromise<string> {
|
||||
let result = storageService.get('telemetry.instanceId') || uuid.generateUuid();
|
||||
storageService.store('telemetry.instanceId', result);
|
||||
return TPromise.as(result);
|
||||
}
|
||||
|
||||
export function getOrCreateMachineId(storageService: IStorageService): TPromise<string> {
|
||||
let result = storageService.get(machineIdStorageKey);
|
||||
|
||||
if (result) {
|
||||
return TPromise.as(result);
|
||||
}
|
||||
|
||||
return getMachineId().then(result => {
|
||||
storageService.store(machineIdStorageKey, result);
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
function getSqmUserId(storageService: IStorageService): TPromise<string> {
|
||||
const sqmUserId = storageService.get('telemetry.sqm.userId');
|
||||
if (sqmUserId) {
|
||||
return TPromise.as(sqmUserId);
|
||||
}
|
||||
return getWinRegKeyData(SQM_KEY, 'UserId', winreg.HKCU).then(result => {
|
||||
if (result) {
|
||||
storageService.store('telemetry.sqm.userId', result);
|
||||
return result;
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
|
||||
function getSqmMachineId(storageService: IStorageService): TPromise<string> {
|
||||
let sqmMachineId = storageService.get('telemetry.sqm.machineId');
|
||||
if (sqmMachineId) {
|
||||
return TPromise.as(sqmMachineId);
|
||||
}
|
||||
return getWinRegKeyData(SQM_KEY, 'MachineId', winreg.HKLM).then(result => {
|
||||
if (result) {
|
||||
storageService.store('telemetry.sqm.machineId', result);
|
||||
return result;
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
|
||||
function getWinRegKeyData(key: string, name: string, hive: string): TPromise<string> {
|
||||
return new TPromise<string>((resolve, reject) => {
|
||||
if (process.platform === 'win32') {
|
||||
try {
|
||||
const reg = new winreg({ hive, key });
|
||||
reg.get(name, (e, result) => {
|
||||
if (e || !result) {
|
||||
reject(null);
|
||||
} else {
|
||||
resolve(result.value);
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
errors.onUnexpectedError(err);
|
||||
reject(err);
|
||||
}
|
||||
} else {
|
||||
resolve(null);
|
||||
}
|
||||
}).then(undefined, err => {
|
||||
// we only want success
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
// Get the unique ID for the current user
|
||||
function getUserId(storageService: IStorageService): Promise<string> {
|
||||
var userId = storageService.get('common.userId');
|
||||
return new Promise<string>(resolve => {
|
||||
// Generate the user id if it has not been created already
|
||||
if (typeof userId === 'undefined') {
|
||||
let id = Utils.generateUserId();
|
||||
id.then( newId => {
|
||||
userId = newId;
|
||||
resolve(userId);
|
||||
//store the user Id in the storage service
|
||||
storageService.store('common.userId', userId);
|
||||
});
|
||||
} else {
|
||||
resolve(userId);
|
||||
}
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user