mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-24 17:23:05 -05:00
Initial work to update telemetry to use Common Schema (#6203)
* Update admin-tool-ext-win to use new ads-extension-telemetry package * Add AdsTelemetryService for sending telemetry events using the ADS Common Schema * Clean up unused import and add engineType * Address PR comments * Update private var names
This commit is contained in:
189
src/sql/platform/telemetry/adsTelemetryService.ts
Normal file
189
src/sql/platform/telemetry/adsTelemetryService.ts
Normal file
@@ -0,0 +1,189 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IAdsTelemetryService, ITelemetryInfo, ITelemetryEvent, ITelemetryConnectionInfo, ITelemetryEventMeasures, ITelemetryEventProperties } from 'sql/platform/telemetry/telemetry';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
|
||||
|
||||
class TelemetryEventImpl implements ITelemetryEvent {
|
||||
constructor(
|
||||
private _telemetryService: ITelemetryService,
|
||||
private _logService: ILogService,
|
||||
private _eventName: string,
|
||||
private _properties?: ITelemetryEventProperties,
|
||||
private _measurements?: ITelemetryEventMeasures) {
|
||||
_properties = _properties || {};
|
||||
_measurements = _measurements || {};
|
||||
}
|
||||
|
||||
public send(): void {
|
||||
try {
|
||||
this._telemetryService.publicLog(this._eventName, { properties: this._properties, measurements: this._measurements });
|
||||
}
|
||||
catch (e) {
|
||||
// We don't want exceptions sending telemetry to break functionality so just log and ignore
|
||||
if (this._logService) {
|
||||
const msg = e instanceof Error ? e.message : e;
|
||||
this._logService.warn(`Error sending ${this._eventName} event ${msg}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public withAdditionalProperties(additionalProperties: ITelemetryEventProperties): ITelemetryEvent {
|
||||
Object.assign(this._properties, additionalProperties);
|
||||
return this;
|
||||
}
|
||||
|
||||
public withAdditionalMeasurements(additionalMeasurements: ITelemetryEventMeasures): ITelemetryEvent {
|
||||
Object.assign(this._measurements, additionalMeasurements);
|
||||
return this;
|
||||
}
|
||||
|
||||
public withConnectionInfo(connectionInfo: ITelemetryConnectionInfo): ITelemetryEvent {
|
||||
Object.assign(this._properties,
|
||||
{
|
||||
authenticationType: connectionInfo.authenticationType,
|
||||
providerName: connectionInfo.providerName,
|
||||
serverType: connectionInfo.serverType,
|
||||
engineType: connectionInfo.engineType
|
||||
});
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
export class AdsTelemetryService implements IAdsTelemetryService {
|
||||
|
||||
_serviceBrand: any;
|
||||
|
||||
constructor(
|
||||
@ITelemetryService private telemetryService: ITelemetryService,
|
||||
@ILogService private logService: ILogService
|
||||
) { }
|
||||
|
||||
setEnabled(value: boolean): void {
|
||||
return this.telemetryService.setEnabled(value);
|
||||
}
|
||||
|
||||
get isOptedIn(): boolean {
|
||||
return this.telemetryService.isOptedIn;
|
||||
}
|
||||
|
||||
getTelemetryInfo(): Promise<ITelemetryInfo> {
|
||||
return this.telemetryService.getTelemetryInfo();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a View event that can be sent later. This is used to log that a particular page or item was seen.
|
||||
* @param view The name of the page or item that was viewed
|
||||
*/
|
||||
public createViewEvent(view: string): ITelemetryEvent {
|
||||
return new TelemetryEventImpl(this.telemetryService, this.logService, 'view', {
|
||||
view: view
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a View event. This is used to log that a particular page or item was seen.
|
||||
* @param view The name of the page or item that was viewed
|
||||
*/
|
||||
public sendViewEvent(view: string): void {
|
||||
this.createViewEvent(view).send();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an Action event that can be sent later. This is used to log when an action was taken, such as clicking a button.
|
||||
* @param view The name of the page or item where this action occurred
|
||||
* @param action The name of the action taken
|
||||
* @param target The name of the item being acted on
|
||||
* @param source The source of the action
|
||||
*/
|
||||
public createActionEvent(view: string, action: string, target: string = '', source: string = '', durationInMs?: number): ITelemetryEvent {
|
||||
const measures: ITelemetryEventMeasures = durationInMs ? { durationInMs: durationInMs } : {};
|
||||
return new TelemetryEventImpl(this.telemetryService, this.logService, 'action', {
|
||||
view: view,
|
||||
action: action,
|
||||
target: target,
|
||||
source: source
|
||||
}, measures);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a Action event. This is used to log when an action was taken, such as clicking a button.
|
||||
* @param view The name of the page or item where this action occurred
|
||||
* @param action The name of the action taken
|
||||
* @param target The name of the item being acted on
|
||||
* @param source The source of the action
|
||||
*/
|
||||
public sendActionEvent(view: string, action: string, target: string = '', source: string = '', durationInMs?: number): void {
|
||||
this.createActionEvent(view, action, target, source, durationInMs).send();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Metrics event that can be sent later. This is used to log measurements taken.
|
||||
* @param metrics The metrics to send
|
||||
*/
|
||||
public createMetricsEvent(metrics: ITelemetryEventMeasures, groupName: string = ''): ITelemetryEvent {
|
||||
return new TelemetryEventImpl(this.telemetryService, this.logService, 'metrics', { groupName: groupName }, metrics);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a Metrics event. This is used to log measurements taken.
|
||||
* @param measurements The metrics to send
|
||||
*/
|
||||
public sendMetricsEvent(metrics: ITelemetryEventMeasures, groupName: string = ''): void {
|
||||
this.createMetricsEvent(metrics, groupName).send();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Error event that can be sent later. This is used to log errors that occur.
|
||||
* @param view The name of the page or item where the error occurred
|
||||
* @param name The friendly name of the error
|
||||
* @param errorCode The error code returned
|
||||
* @param errorType The specific type of error
|
||||
* @param properties Optional additional properties
|
||||
*/
|
||||
public createErrorEvent(view: string, name: string, errorCode: string = '', errorType: string = ''): ITelemetryEvent {
|
||||
return new TelemetryEventImpl(this.telemetryService, this.logService, 'error', {
|
||||
view: view,
|
||||
name: name,
|
||||
errorCode: errorCode,
|
||||
errorType: errorType
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a Error event. This is used to log errors that occur.
|
||||
* @param view The name of the page or item where the error occurred
|
||||
* @param name The friendly name of the error
|
||||
* @param errorCode The error code returned
|
||||
* @param errorType The specific type of error
|
||||
*/
|
||||
public sendErrorEvent(view: string, name: string, errorCode: string = '', errorType: string = ''): void {
|
||||
this.createErrorEvent(view, name, errorCode, errorType).send();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a custom telemetry event with the specified name that can be sent later. Generally the other send functions should be
|
||||
* preferred over this - only use this if you absolutely need a custom event that can't be covered by the other methods.
|
||||
* @param eventName The name of the event. Will be prefixed with <extension-name>/
|
||||
* @param properties The list of properties to send along with the event
|
||||
* @param measurements The list of measurements to send along with the event
|
||||
*/
|
||||
public createTelemetryEvent(eventName: string, properties?: ITelemetryEventProperties, measurements?: ITelemetryEventMeasures): ITelemetryEvent {
|
||||
return new TelemetryEventImpl(this.telemetryService, this.logService, eventName, properties, measurements);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a custom telemetry event with the specified name. Generally the other send functions should be
|
||||
* preferred over this - only use this if you absolutely need a custom event that can't be covered by the other
|
||||
* @param eventName The name of the event. Will be prefixed with <extension-name>/
|
||||
* @param properties The list of properties to send along with the event
|
||||
* @param measurements The list of measurements to send along with the event
|
||||
*/
|
||||
public sendTelemetryEvent(eventName: string, properties?: ITelemetryEventProperties, measurements?: ITelemetryEventMeasures): void {
|
||||
this.createTelemetryEvent(eventName, properties, measurements).send();
|
||||
}
|
||||
}
|
||||
@@ -3,10 +3,11 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { TelemetryView } from 'sql/platform/telemetry/telemetryKeys';
|
||||
import { IAdsTelemetryService } from 'sql/platform/telemetry/telemetry';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry, IWorkbenchContribution } from 'vs/workbench/common/contributions';
|
||||
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { ICommandService, ICommandEvent } from 'vs/platform/commands/common/commands';
|
||||
@@ -14,7 +15,7 @@ import { ICommandService, ICommandEvent } from 'vs/platform/commands/common/comm
|
||||
export class SqlTelemetryContribution extends Disposable implements IWorkbenchContribution {
|
||||
|
||||
constructor(
|
||||
@ITelemetryService private telemetryService: ITelemetryService,
|
||||
@IAdsTelemetryService private telemetryService: IAdsTelemetryService,
|
||||
@IStorageService storageService: IStorageService,
|
||||
@ICommandService commandService: ICommandService
|
||||
) {
|
||||
@@ -25,7 +26,7 @@ export class SqlTelemetryContribution extends Disposable implements IWorkbenchCo
|
||||
(e: ICommandEvent) => {
|
||||
// Filter out high-frequency events
|
||||
if (!['type', 'cursorUp', 'cursorDown', 'cursorRight', 'cursorLeft', 'deleteLeft', 'deleteRight'].find(id => id === e.commandId)) {
|
||||
telemetryService.publicLog('adsCommandExecuted', { id: e.commandId });
|
||||
telemetryService.sendActionEvent(TelemetryView.Shell, 'adsCommandExecuted', e.commandId);
|
||||
}
|
||||
}));
|
||||
const dailyLastUseDate: number = Date.parse(storageService.get('telemetry.dailyLastUseDate', StorageScope.GLOBAL, '0'));
|
||||
@@ -38,14 +39,14 @@ export class SqlTelemetryContribution extends Disposable implements IWorkbenchCo
|
||||
// daily user event
|
||||
if (this.didDayChange(dailyLastUseDate)) {
|
||||
// daily first use
|
||||
telemetryService.publicLog('telemetry.dailyFirstUse', { dailyFirstUse: true });
|
||||
telemetryService.sendTelemetryEvent('telemetry.dailyFirstUse', { dailyFirstUse: 'true' });
|
||||
storageService.store('telemetry.dailyLastUseDate', todayString, StorageScope.GLOBAL);
|
||||
}
|
||||
|
||||
// weekly user event
|
||||
if (this.didWeekChange(weeklyLastUseDate)) {
|
||||
// weekly first use
|
||||
telemetryService.publicLog('telemetry.weeklyFirstUse', { weeklyFirstUse: true });
|
||||
telemetryService.sendTelemetryEvent('telemetry.weeklyFirstUse', { weeklyFirstUse: 'true' });
|
||||
storageService.store('telemetry.weeklyLastUseDate', todayString, StorageScope.GLOBAL);
|
||||
}
|
||||
|
||||
@@ -54,7 +55,7 @@ export class SqlTelemetryContribution extends Disposable implements IWorkbenchCo
|
||||
after the last time we sent a monthly usage count */
|
||||
const monthlyUseCount: number = storageService.getNumber('telemetry.monthlyUseCount', StorageScope.GLOBAL, 0);
|
||||
if (this.didMonthChange(monthlyLastUseDate)) {
|
||||
telemetryService.publicLog('telemetry.monthlyUse', { monthlyFirstUse: true });
|
||||
telemetryService.sendTelemetryEvent('telemetry.monthlyUse', { monthlyFirstUse: 'true' });
|
||||
// the month changed, so send the user usage type event based on monthly count for last month
|
||||
// and reset the count for this month
|
||||
let lastMonthDate = new Date(monthlyLastUseDate);
|
||||
@@ -132,15 +133,15 @@ export class SqlTelemetryContribution extends Disposable implements IWorkbenchCo
|
||||
userUsageType = UserUsageType.Dedicated;
|
||||
}
|
||||
if (userUsageType) {
|
||||
this.telemetryService.publicLog('telemetry.userUsage',
|
||||
{ userType: userUsageType, monthlyUseCount: monthlyUseCount, month: lastMonthDate.getMonth().toString(), year: lastMonthDate.getFullYear().toString() });
|
||||
this.telemetryService.sendTelemetryEvent('telemetry.userUsage',
|
||||
{ userType: userUsageType.toString(), monthlyUseCount: monthlyUseCount.toString(), month: lastMonthDate.getMonth().toString(), year: lastMonthDate.getFullYear().toString() });
|
||||
}
|
||||
}
|
||||
|
||||
// Growth Metrics
|
||||
private sendGrowthTypeEvent(growthType: UserGrowthType, lastMonthDate: Date): void {
|
||||
this.telemetryService.publicLog('telemetry.userGrowthType', {
|
||||
userGrowthType: growthType, month: lastMonthDate.getMonth().toString(), year: lastMonthDate.getFullYear().toString()
|
||||
this.telemetryService.sendTelemetryEvent('telemetry.userGrowthType', {
|
||||
userGrowthType: growthType.toString(), month: lastMonthDate.getMonth().toString(), year: lastMonthDate.getFullYear().toString()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
87
src/sql/platform/telemetry/telemetry.ts
Normal file
87
src/sql/platform/telemetry/telemetry.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
|
||||
export const IAdsTelemetryService = createDecorator<IAdsTelemetryService>('adsTelemetryService');
|
||||
|
||||
/**
|
||||
* Holds additional properties to send along with an event.
|
||||
*/
|
||||
export interface ITelemetryEventProperties {
|
||||
[key: string]: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds additional measures to send along with an event.
|
||||
*/
|
||||
export interface ITelemetryEventMeasures {
|
||||
[key: string]: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Connection info properties to add into an event.
|
||||
*/
|
||||
export interface ITelemetryConnectionInfo {
|
||||
authenticationType?: string;
|
||||
providerName?: string;
|
||||
serverType?: string;
|
||||
engineType?: string;
|
||||
}
|
||||
|
||||
export interface ITelemetryEvent {
|
||||
/**
|
||||
* Sends the event
|
||||
*/
|
||||
send(): void;
|
||||
|
||||
/**
|
||||
* Adds additional custom properties to this event.
|
||||
* @param additionalProperties The additional properties to add
|
||||
*/
|
||||
withAdditionalProperties(additionalProperties: ITelemetryEventProperties): ITelemetryEvent;
|
||||
|
||||
/**
|
||||
* Adds additional custom measurements to this event.
|
||||
* @param additionalMeasurements The additional measurements to add
|
||||
*/
|
||||
withAdditionalMeasurements(additionalMeasurements: ITelemetryEventMeasures): ITelemetryEvent;
|
||||
|
||||
/**
|
||||
* Adds additional connection-related information to this event.
|
||||
* @param connectionInfo The connection info to add. Only the fields in TelemetryConnectionInfo are included, all others are ignored.
|
||||
*/
|
||||
withConnectionInfo(connectionInfo: ITelemetryConnectionInfo): ITelemetryEvent;
|
||||
}
|
||||
|
||||
export interface ITelemetryInfo {
|
||||
sessionId: string;
|
||||
machineId: string;
|
||||
instanceId: string;
|
||||
}
|
||||
|
||||
export interface IAdsTelemetryService {
|
||||
|
||||
// ITelemetryService functions
|
||||
_serviceBrand: any;
|
||||
|
||||
setEnabled(value: boolean): void;
|
||||
|
||||
getTelemetryInfo(): Promise<ITelemetryInfo>;
|
||||
|
||||
isOptedIn: boolean;
|
||||
|
||||
// Custom event functions
|
||||
createViewEvent(view: string): ITelemetryEvent;
|
||||
sendViewEvent(view: string): void;
|
||||
createActionEvent(view: string, action: string, target?: string, source?: string, durationInMs?: number): ITelemetryEvent;
|
||||
sendActionEvent(view: string, action: string, target?: string, source?: string, durationInMs?: number): void;
|
||||
createMetricsEvent(metrics: ITelemetryEventMeasures, groupName: string): ITelemetryEvent;
|
||||
sendMetricsEvent(metrics: ITelemetryEventMeasures, groupName: string): void;
|
||||
createErrorEvent(view: string, name: string, errorCode?: string, errorType?: string): ITelemetryEvent;
|
||||
sendErrorEvent(view: string, name: string, errorCode?: string, errorType?: string): void;
|
||||
createTelemetryEvent(eventName: string, properties?: ITelemetryEventProperties, measurements?: ITelemetryEventMeasures): ITelemetryEvent;
|
||||
sendTelemetryEvent(eventName: string, properties?: ITelemetryEventProperties, measurements?: ITelemetryEventMeasures): void;
|
||||
}
|
||||
@@ -62,3 +62,7 @@ export const DeleteAgentProxy = 'DeleteAgentProxy';
|
||||
|
||||
// Notebook Events:
|
||||
export const NotebookMarkdownRendered = 'NotebookMarkdownRendered';
|
||||
|
||||
export enum TelemetryView {
|
||||
Shell = 'Shell'
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user