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

feat: telemetry events added #17

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
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
11 changes: 9 additions & 2 deletions vscode-preview/package.json
Expand Up @@ -6,6 +6,7 @@
"icon": "icon.png",
"preview": true,
"publisher": "salesforce",
"aiKey": "",
Copy link
Collaborator

Choose a reason for hiding this comment

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

is blank aiKey valid?

"license": "BSD 3-Clause",
"engines": {
"vscode": "^1.52.0"
Expand Down Expand Up @@ -236,7 +237,12 @@
"default": "off",
"description": "%salesforcedocs.trace.desc%",
"scope": "window"
}
},
"salesforce-docs-markdown-preview.enableTelemetry": {
"type": "boolean",
"default": true,
"description": "%telemetry_setting_description%"
}
}
},
"configurationDefaults": {
Expand Down Expand Up @@ -306,7 +312,8 @@
"unist-builder": "2.0.3",
"unist-util-visit": "2.0.3",
"vsce": "1.88.0",
"vscode-nls": "^4.0.0"
"vscode-nls": "^4.0.0",
"applicationinsights": "1.0.7"
},
"devDependencies": {
"@types/vscode": "1.45.0",
Expand Down
6 changes: 4 additions & 2 deletions vscode-preview/package.nls.json
Expand Up @@ -23,5 +23,7 @@
"configuration.salesforcedocs.links.openLocation.beside": "Open links beside the active editor.",
"configuration.salesforcedocs.preview.style.preset.description": "Choose the base style preset for your Preview",
"configuration.salesforcedocs.preview.style.preset.salesforcedocs": "Choose Salesforcedocs styling for your preview",
"configuration.salesforcedocs.preview.style.preset.VSCode": "Choose basic Visual Studio Code markdown styling for your preview"
}
"configuration.salesforcedocs.preview.style.preset.VSCode": "Choose basic Visual Studio Code markdown styling for your preview",
"telemetry_setting_description": "Specifies whether to enable Salesforce telemetry. Even when enabled, still abides by the overall `telemetry.enableTelemetry` vscode setting."
}

17 changes: 16 additions & 1 deletion vscode-preview/src/extension.ts
Expand Up @@ -13,6 +13,7 @@ import { Logger } from './logger';
import { MarkdownEngine } from './markdownEngine';
import { getMarkdownExtensionContributions } from './markdownExtensions';
import { ContentSecurityPolicyArbiter, ExtensionContentSecurityPolicyArbiter, PreviewSecuritySelector } from './security';
import { telemetryService } from './telemetry';

function registerMarkdownLanguageFeatures(
): vscode.Disposable {
Expand Down Expand Up @@ -48,7 +49,14 @@ function registerMarkdownCommands(
return commandManager;
}

export function activate(context: vscode.ExtensionContext) {
export async function activate(context: vscode.ExtensionContext) {
const extensionHRStart = process.hrtime();
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { name, aiKey, version } = require(context.asAbsolutePath(
'./package.json'
));
const machineId = vscode.env ? vscode.env.machineId : 'someValue.machineId';
Copy link
Collaborator

Choose a reason for hiding this comment

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

someValue.machineId is this a valid value?

await telemetryService.initializeService(context, name, aiKey, version,machineId);
vscode.commands.executeCommand('setContext', 'markdownPreviewFocus', false);

const contributions = getMarkdownExtensionContributions(context);
Expand All @@ -69,5 +77,12 @@ export function activate(context: vscode.ExtensionContext) {
logger.updateConfiguration();
previewManager.updateConfiguration();
}));
telemetryService.sendExtensionActivationEvent(extensionHRStart);
}

export function deactivate(): void {
// Send metric data.
telemetryService.sendExtensionDeactivationEvent();
telemetryService.dispose();
}

10 changes: 10 additions & 0 deletions vscode-preview/src/telemetry/index.ts
@@ -0,0 +1,10 @@

/*
* Copyright (c) 2021, salesforce.com, inc.
* All rights reserved.
* Licensed under the BSD 3-Clause license.
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
*/
import { TelemetryService } from './telemetry';

export const telemetryService = TelemetryService.getInstance();
152 changes: 152 additions & 0 deletions vscode-preview/src/telemetry/telemetry.ts
@@ -0,0 +1,152 @@
/*
* Copyright (c) 2021, salesforce.com, inc.
* All rights reserved.
* Licensed under the BSD 3-Clause license.
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
*/

import * as util from 'util';
import { ExtensionContext, workspace } from 'vscode';
import { TelemetryReporter } from './telemetryReporter';
import { CommandMetric, Measurements, Properties } from './types';

export class TelemetryService {
private static instance: TelemetryService;
private context: ExtensionContext | undefined;
private reporter: TelemetryReporter | undefined;
private aiKey = '';
private version = '';
private extensionName = 'unknown';

public static getInstance(): TelemetryService {
if (!TelemetryService.instance) {
TelemetryService.instance = new TelemetryService();
}
return TelemetryService.instance;
}

/**
* Initialize Telemetry Service during extension activation.
* @param context extension context
* @param extensionName extension name
*/

public async initializeService(
context: ExtensionContext,
extensionName: string,
aiKey: string,
version: string,
machineId: string
): Promise<void> {
this.context = context;
this.extensionName = extensionName;
this.aiKey = aiKey;
this.version = version;

const isDevMode = machineId === 'someValue.machineId';

// TelemetryReporter is not initialized if user has disabled telemetry setting.
if (
this.reporter === undefined &&
this.isTelemetryExtensionConfigurationEnabled() &&
!isDevMode
) {
try {
this.reporter = new TelemetryReporter(
'salesforce-docs-markdown-preview',
this.version,
this.aiKey,
true
);
this.context.subscriptions.push(this.reporter);
} catch (e) {
console.error(
`Error initializing telemetry on Salesforce Docs Markdown extension: ${e.message}`
);
}
}
}

public getReporter(): TelemetryReporter | undefined {
return this.reporter;
}

public isTelemetryExtensionConfigurationEnabled(): boolean {
return (
workspace
.getConfiguration('telemetry')
.get<boolean>('enableTelemetry', true) &&
workspace
.getConfiguration('salesforce-docs-markdown-preview')
.get<boolean>('enableTelemetry', true)
);
}

public sendExtensionActivationEvent(hrstart: [number, number]): void {
const startupTime = this.getEndHRTime(hrstart);
this.reporter?.sendTelemetryEvent(
'activationEvent',
{
extensionName: this.extensionName,
},
{ startupTime }
);
}

public sendExtensionDeactivationEvent(): void {
this.reporter?.sendTelemetryEvent('deactivationEvent', {
extensionName: this.extensionName,
});
}

public sendCommandEvent(
commandName?: string,
hrstart?: [number, number],
properties?: Properties,
measurements?: Measurements
): void {
if (commandName) {
const baseProperties: CommandMetric = {
extensionName: this.extensionName,
commandName,
};
const aggregatedProps = Object.assign(baseProperties, properties);

let aggregatedMeasurements: Measurements | undefined;
if (hrstart || measurements) {
aggregatedMeasurements = Object.assign({}, measurements);
if (hrstart) {
aggregatedMeasurements.executionTime = this.getEndHRTime(hrstart);
}
}
this.reporter?.sendTelemetryEvent(
'commandExecution',
aggregatedProps,
aggregatedMeasurements
);
}
}

public sendException(name: string, message: string): void {
this.reporter?.sendExceptionEvent(name, message);
}

public sendEventData(
eventName: string,
properties?: Properties,
measures?: Measurements
): void {
this.reporter?.sendTelemetryEvent(eventName, properties, measures);
}

public getEndHRTime(hrstart: [number, number]): number {
const hrend = process.hrtime(hrstart);
return Number(util.format('%d%d', hrend[0], hrend[1] / 1000000));
}

public dispose(): void {
if (this.reporter !== undefined) {
this.reporter.dispose().catch((err) => console.log(err));
}
}
}