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

Added telemetry #47

Merged
merged 9 commits into from
Mar 21, 2023
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
node_modules
skyline-vscode/src/version.ts
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,13 @@ After installation, please make note of the path to the `skyline` binary. To do
2. Press `Ctrl+Shift+P`, then select `Skyline` from the dropdown list.
3. Click on `Begin Analysis`.

## Disabling telemetry
If you do not want to send usage data to CentML, you can set the skyline.isTelemetryEnabled setting to "No".

You can set the value by going to File > Preferences > Settings (On macOS: Code > Preferences > Settings), and search for telemetry. Then set the value in Skyline > Is Telemetry Enabled. This will disable all telemetry events.

As well, DeepView respects VSCode's telemetry levels. IF telemetry.telemetryLevel is set to off, then no telemetry events will be sent to CentML, even if skyline.telemetry.enabled is set to true. If telemetry.telemetryLevel is set to error or crash, only events containing an error or errors property will be sent to CentML.

## Development Environment Setup

### Dependencies
Expand Down
3 changes: 1 addition & 2 deletions skyline-vscode/.vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@
"tasks": [
{
"type": "npm",
"script": "watch",
"problemMatcher": "$tsc-watch",
"script": "debug",
"isBackground": true,
"presentation": {
"reveal": "never"
Expand Down
52 changes: 52 additions & 0 deletions skyline-vscode/esbuild.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import * as esbuild from 'esbuild';
import ifdefPlugin from 'esbuild-ifdef';

let baseOptions = {
entryPoints: ['./src/extension.js'],
bundle: true,
outfile: 'out/main.js',
external: ['vscode'],
format: 'cjs',
platform: "node"
};

let watchPlugin = [
ifdefPlugin({
variables: {
DEBUG: true
}
})
];

let productionPlugin = [
ifdefPlugin({
variables: {
DEBUG: false
}
})
];

let builds = {
'base': {
...baseOptions,
sourcemap: true
},
'debug': {
...baseOptions,
sourcemap: true,
plugins: watchPlugin
},
'production': {
...baseOptions,
minify: true,
plugins: productionPlugin
}
};

try {


await esbuild.build(builds[process.argv[2]]);
} catch (error) {
process.exit(1);
}
37 changes: 27 additions & 10 deletions skyline-vscode/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,11 @@
"description": "",
"version": "0.1.0",
"engines": {
"vscode": "^1.52.0"
"vscode": "^1.76.0"
},
"categories": [
"Other"
],
"activationEvents": [
"onCommand:skyline-vscode.cmd_begin_analyze"
],
"main": "./out/main.js",
"contributes": {
"commands": [
Expand All @@ -34,33 +31,53 @@
"type": "number",
"default": 60120,
"description": "Specifies the port of the profiler."
},
"skyline.isTelemetryEnabled": {
"type": "string",
"default": "Ask me",
"enum": [
"Ask me",
"Yes",
"No"
],
"enumDescriptions": [
"Prompt the user if they consent on collecting their usage data and errors.",
"Allow usage data and errors to be sent to the developer.",
"Do not allow usage data and errors to be sent to the developer."
],
"description": "Controls DeepView telemetry and if data and errors to be sent to the developer. Note that VSCode's telemetry level is respected and takes precedence over this property."
}
}
}
},
"scripts": {
"vscode:prepublish": "npm run esbuild-base -- --minify",
"esbuild-base": "esbuild ./src/extension.ts --bundle --outfile=out/main.js --external:vscode --format=cjs --platform=node",
"esbuild": "npm run esbuild-base -- --sourcemap",
"esbuild-watch": "npm run esbuild-base -- --sourcemap --watch",
"test-compile": "tsc -p ./"
"vscode:prepublish": "npm run clean && node esbuild.mjs production",
"esbuild": "npm run clean && node esbuild.mjs base",
"watch": "npm run clean && node esbuild.mjs debug",
"clean": "rm -rf ./out",
"test": "mocha"
},
"devDependencies": {
"@types/glob": "^7.1.4",
"@types/google-protobuf": "^3.15.5",
"@types/mocha": "^9.0.0",
"@types/node": "14.x",
"@types/vscode": "^1.52.0",
"@types/vscode": "^1.76.0",
"@typescript-eslint/eslint-plugin": "^4.31.1",
"@typescript-eslint/parser": "^4.31.1",
"@vscode/test-electron": "^1.6.2",
"esbuild": "^0.17.10",
"eslint": "^7.32.0",
"glob": "^7.1.7",
"ifdef-loader": "^2.3.2",
"mocha": "^9.1.1",
"typescript": "^4.4.3"
},
"dependencies": {
"@segment/analytics-node": "^1.0.0-beta.23",
"@types/ws": "^8.2.0",
"bootstrap-fork": "^3.3.6",
"esbuild-ifdef": "^0.2.0",
"google-protobuf": "^3.18.0",
"ts-protoc-gen": "^0.15.0"
}
Expand Down
73 changes: 73 additions & 0 deletions skyline-vscode/src/analytics/AnalyticsManager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { SegmentInitializer } from "./SegmentInitializer";
import Analytics from "@segment/analytics-node";
import { filterObjectByKeyName } from "../utils";
export class AnalyticsManager {

analytics: Analytics;
hasIdentifiedUser: boolean;
userId: string;

constructor() {
this.analytics = SegmentInitializer.initialize();
this.hasIdentifiedUser = false;
this.userId = String();
}

sendEventData = (eventName: string, data?: Record<string, any>) => {
this.identifyUser(data);
/// #if DEBUG
console.log("Event!");
console.log({
userId: this.userId,
event: eventName,
timestamp: new Date(),
properties: data
});
/// #else
this.analytics.track({
userId: this.userId,
event: eventName,
timestamp: new Date(),
properties: data
});
/// #endif
};

sendErrorData = (error: Error, data?: Record<string, any>) => {
this.identifyUser(data);
/// #if DEBUG
console.log("Error!");
console.log({
userId: this.userId,
event: "Client Error",
timestamp: new Date(),
properties: {... data, ...error}
});
/// #else
this.analytics.track({
userId: this.userId,
event: "Client Error",
timestamp: new Date(),
properties: {... data, ...error}
});
/// #endif
};

closeAndFlush = () => {
this.analytics.closeAndFlush();
};

identifyUser(data?: Record<string, any>) {
if (!this.hasIdentifiedUser && data) {
this.userId = data["common.vscodemachineid"];
const commonTraits = filterObjectByKeyName(data, "common.");
this.hasIdentifiedUser = true;
/// #if DEBUG
console.log("Identifying!");
console.log({ userId: this.userId, traits:commonTraits });
/// #else
this.analytics.identify({ userId: this.userId, traits:commonTraits });
/// #endif
}
}
}
11 changes: 11 additions & 0 deletions skyline-vscode/src/analytics/SegmentInitializer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Analytics, AnalyticsSettings } from '@segment/analytics-node'


export namespace SegmentInitializer {
export function initialize(): Analytics {
// TODO: make sure the key is inside package.json and validate it
let analyticsSettings: AnalyticsSettings = {writeKey: 'sOQXQfqVkpJxVqKbL0tbwkO6SFnpm5Ef', maxEventsInBatch: 10, flushInterval: 10000}
let analytics: Analytics = new Analytics(analyticsSettings);
return analytics;
}
}
22 changes: 22 additions & 0 deletions skyline-vscode/src/analytics/USAGE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
## Usage data being collected by CentML
Only anonymous data is collected by CentML using VSCode's Telemetry API. All telemetry events are automatically sanitized to anonymize all paths (best effort) and references to the username.

### Common data
The common data sent in all telemetry requests may contain:
- **Extension Name** `common.extname` - The extension name
- **Extension Version** `common.extversion` - The extension version
- **Machine Identifier** `common.vscodemachineid` - A common machine identifier generated by VS Code
- **Session Identifier** `common.vscodesessionid` - A session identifier generated by VS Code
- **VS Code Version** `common.vscodeversion` - The version of VS Code running the extension
- **OS** `common.os` - The OS running VS Code
- **Platform Version** `common.platformversion` - The version of the OS/Platform
- **Product** `common.product` - What Vs code is hosted in, i.e. desktop, github.dev, codespaces.
- **UI Kind** `common.uikind` - Web or Desktop indicating where VS Code is running
- **Remote Name** `common.remotename` - A name to identify the type of remote connection. `other` indicates a remote connection not from the 3 main extensions (ssh, docker, wsl).
- **Architecture** `common.nodeArch` - What architecture of node is running. i.e. arm or x86. On the web it will just say `web`.

### Usage data
The usage data sent contains the responses given by the DeepView.Profile

### Error data
The error data sent contains all error information thrown by the extension.
42 changes: 41 additions & 1 deletion skyline-vscode/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@ import * as vscode from 'vscode';
import {SkylineEnvironment, SkylineSession, SkylineSessionOptions} from './skyline_session';

import * as path from 'path';
import { AnalyticsManager } from './analytics/AnalyticsManager';

const PRIVACY_STATEMENT_URL = "https://centml.ai/privacy-policy/";
const OPT_OUT_INSTRUCTIONS_URL = "https://github.com/CentML/DeepView.Explore#how-to-disable-telemetry-reporting";
const RETRY_OPTIN_DELAY_IN_MS = 60 * 60 * 1000; // 1h

export function activate(context: vscode.ExtensionContext) {
let sess: SkylineSession;
Expand All @@ -10,6 +15,14 @@ export function activate(context: vscode.ExtensionContext) {
reactProjectRoot: path.join(context.extensionPath, "react-ui")
};

let anayticsManager: AnalyticsManager = new AnalyticsManager();
const telemetrySender: vscode.TelemetrySender = {
sendEventData: anayticsManager.sendEventData,
sendErrorData: anayticsManager.sendErrorData,
flush: anayticsManager.closeAndFlush
};
const logger = vscode.env.createTelemetryLogger(telemetrySender);

let disposable = vscode.commands.registerCommand('skyline-vscode.cmd_begin_analyze', () => {
let vsconfig = vscode.workspace.getConfiguration('skyline');

Expand Down Expand Up @@ -44,7 +57,9 @@ export function activate(context: vscode.ExtensionContext) {
projectRoot: uri[0].fsPath,
addr: vsconfig.address,
port: vsconfig.port,
webviewPanel: panel
isTelemetryEnabled: isTelemetryEnabled,
webviewPanel: panel,
telemetryLogger: logger
};


Expand All @@ -60,6 +75,7 @@ export function activate(context: vscode.ExtensionContext) {
}

startSkyline();
showTelemetryOptInDialogIfNeeded();
});
});

Expand All @@ -69,3 +85,27 @@ export function activate(context: vscode.ExtensionContext) {
export function deactivate() {

}

async function showTelemetryOptInDialogIfNeeded() {
let vsconfig = vscode.workspace.getConfiguration('skyline');
if (vsconfig.isTelemetryEnabled === "Ask me"){
// Pop up the message then wait
const message: string = `Help CentML improve DeepView by allowing us to collect usage data.
Read our [privacy statement](${PRIVACY_STATEMENT_URL})
and learn how to [opt out](${OPT_OUT_INSTRUCTIONS_URL}).`;

const retryOptin = setTimeout(showTelemetryOptInDialogIfNeeded, RETRY_OPTIN_DELAY_IN_MS);
let selection: string | undefined;
selection = await vscode.window.showInformationMessage(message, 'Yes', 'No');
if (!selection) {
return;
}
clearTimeout(retryOptin);
vsconfig.update("isTelemetryEnabled", selection, true);
}
}

function isTelemetryEnabled(): boolean {
let vsconfig = vscode.workspace.getConfiguration('skyline');
return (vsconfig.isTelemetryEnabled === "Yes");
}
Loading