From 05b3877bcbc6501176c4e28cdc5d60bbe6ed5c01 Mon Sep 17 00:00:00 2001 From: Dave Holoway Date: Tue, 21 Jul 2020 20:47:37 +0100 Subject: [PATCH] update analytics (#99) * replace mixpanel with amplitude * remove unused uuid require * include partial count fields to allow easier analysis * enable real logging * record extension deactivation --- extension.js | 28 +++++++---- langserver/analytics.js | 87 ++++++++++++++++----------------- langserver/completions.js | 5 +- langserver/method-signatures.js | 5 +- langserver/package-lock.json | 70 +++++++------------------- langserver/package.json | 2 +- langserver/server.js | 2 +- src/debugMain.js | 15 ++---- 8 files changed, 95 insertions(+), 119 deletions(-) diff --git a/extension.js b/extension.js index af363d2..a6ddb80 100644 --- a/extension.js +++ b/extension.js @@ -12,8 +12,11 @@ const { selectTargetDevice } = require('./src/utils/device'); /** * @param {vscode.ExtensionContext} context + * @param {string} uid + * @param {number} session_id + * @param {*} vscode_props */ -async function createLanguageClient(context) { +async function createLanguageClient(context, uid, session_id, vscode_props) { // The server is implemented in node let serverModule = context.asAbsolutePath(path.join('langserver', 'server.js')); // The debug options for the server @@ -45,8 +48,6 @@ async function createLanguageClient(context) { } const sourceFiles = (await vscode.workspace.findFiles(`${globSearchRoot}**/*.java`, null, 1000, null)).map(uri => uri.toString()); - const mpids = analytics.getIDs(context); - // Options to control the language client /** @type {import('vscode-languageclient').LanguageClientOptions} */ let clientOptions = { @@ -57,11 +58,11 @@ async function createLanguageClient(context) { initializationOptions: { // extensionPath points to the root of the extension (the folder where this file is) extensionPath: context.extensionPath, - mpuid: mpids.uid, - mpsid: mpids.sid, + uid, + session_id, + vscode_props, initialSettings: config, sourceFiles, - vscodeVersion: vscode.version, workspaceFolders: (vscode.workspace.workspaceFolders || []).map(z => z.uri.toString()), }, synchronize: { @@ -114,10 +115,18 @@ function activate(context) { /* Only the logcat stuff is configured here. The debugger is launched from src/debugMain.js */ AndroidContentProvider.register(context, vscode.workspace); - const mpids = analytics.getIDs(context); - analytics.init(undefined, mpids.uid, mpids.sid, package_json, { vscode_version: vscode.version }); + const { uid } = analytics.getIDs(context); + const session_id = Math.trunc(Math.random() * Number.MAX_SAFE_INTEGER); + const vscode_props = { + appName: vscode.env.appName, + language: vscode.env.language, + shell: vscode.env.shell, + uiKind: vscode.env.uiKind, + vscode_version: vscode.version, + } + analytics.init(undefined, uid, session_id, '', package_json, vscode_props, 'extension-start'); - createLanguageClient(context).then(client => { + createLanguageClient(context, uid, session_id, vscode_props).then(client => { languageClient = client; refreshLanguageServerEnabledState(); }); @@ -172,6 +181,7 @@ function activate(context) { // this method is called when your extension is deactivated function deactivate() { + analytics.event('extension-deactivate'); } exports.activate = activate; diff --git a/langserver/analytics.js b/langserver/analytics.js index 1fbd031..d3646de 100644 --- a/langserver/analytics.js +++ b/langserver/analytics.js @@ -1,43 +1,53 @@ const os = require('os'); -let mp; +const uuid = require('uuid').v4; +let client; /** @type {string} */ let uid; /** @type {string} */ -let sid; +let did = uuid(); +/** @type {number} */ +let session_id; /** @type {Map} */ const timeLabels = new Map(); let session_start = Date.now(); /** @type {string|Promise} */ -let ip = getCurrentIP() - .catch(() => null) - .then(res => ip = res); +let ip = ''; let queued_events = null; +let package_info = null; +let vscode_info = null; /** * @param {string} [t] - * @param {string} [u] - * @param {string} [s] - * @param {{name:string,version:string}} [package_json] - * @param {*} [props] + * @param {string} u + * @param {number} s + * @param {string} ipaddr + * @param {{name:string,version:string}} package_json + * @param {*} vscode_props + * @param {string} caller */ -function init(t = '0cca95950055c6553804a46ce7e3df18', u, s, package_json, props) { - if (mp) { +function init(t = '94635b4642d80407accd3739fa35bed6', u, s, ipaddr, package_json, vscode_props, caller) { + if (client) { return; } try { - mp = require('mixpanel').init(t); + client = require('@amplitude/node').init(t); } catch { return; } uid = u; - sid = s; + session_id = s || Math.trunc(Math.random() * Number.MAX_SAFE_INTEGER); + ip = ipaddr || (getCurrentIP() + .catch(() => '') + .then(res => ip = res)); + package_info = package_json; + vscode_info = vscode_props; - if (!props) { + if (!caller) { return; } const now = new Date(); - event(`${package_json.name}-start`, { + event(caller, { extension: package_json.name, ext_version: package_json.version, arch: process.arch, @@ -48,7 +58,7 @@ function init(t = '0cca95950055c6553804a46ce7e3df18', u, s, package_json, props) release: os.release(), localtime: now.toTimeString(), tz: now.getTimezoneOffset(), - ...props + ...vscode_props, }); } @@ -69,7 +79,7 @@ function getCurrentIP() { * @param {*} [properties] */ function event(eventName, properties) { - if (!mp) { + if (!client || !eventName || (!uid && !did) || !ip) { return; } if (queued_events) { @@ -86,23 +96,21 @@ function event(eventName, properties) { return; } try { - if (uid) { - mp.track(eventName, { - ip, - distinct_id: uid, - session_id: sid, + client.logEvent({ + event_type: eventName, + user_id: uid, + device_id: uid ? undefined : did, + app_version: package_info.version, + ip, + language: vscode_info.language, + os_name: process.platform, + os_version: os.release(), + session_id, + event_properties: { session_length: Math.trunc((Date.now() - session_start) / 60e3), ...properties, - }); - } else { - mp.track(eventName, { - ip, - platform: process.platform, - release: os.release(), - node_version: process.version, - ...properties, - }); - } + } + }); } catch {} } @@ -149,29 +157,18 @@ function timeEnd(label, time_unit = 'ms', additionalProps = {}) { function getIDs(context) { if (!context || !context.globalState) { return { - uid: '', sid: '' + uid: '', }; } - let uuidv4 = () => { - try { - uuidv4 = require('uuid').v4; - return uuidv4(); - } catch { - return ''; - } - } let u = uid || (uid = context.globalState.get('mix-panel-id')); if (typeof u !== 'string' || u.length > 36) { - u = uid = uuidv4(); + u = uid = uuid(); context.globalState.update('mix-panel-id', u); } - let s = sid || (sid = uuidv4()); return { uid: u, - sid: s, } } - exports.init = init; exports.event = event; exports.time = time; diff --git a/langserver/completions.js b/langserver/completions.js index c6fcf48..fc19164 100644 --- a/langserver/completions.js +++ b/langserver/completions.js @@ -370,7 +370,10 @@ async function getCompletionItems(params, liveParsers, androidLibrary) { completionRequestCount += 1; if ((completionRequestCount === 1) || (completionRequestCount === 5) || ((completionRequestCount % 25) === 0)) { - event('completion-requests', { comp_req_count: completionRequestCount }); + event('completion-requests', { + comp_req_count: completionRequestCount, // total count for this session + comp_req_partial_count: (completionRequestCount % 25) || 25, + }); } let parsed = docinfo.parsed; diff --git a/langserver/method-signatures.js b/langserver/method-signatures.js index 07663db..0e47e05 100644 --- a/langserver/method-signatures.js +++ b/langserver/method-signatures.js @@ -41,7 +41,10 @@ async function getSignatureHelp(request, liveParsers) { methodsigRequestCount += 1; if ((methodsigRequestCount === 1) || (methodsigRequestCount === 5) || ((methodsigRequestCount % 25) === 0)) { - event('method-sig-requests', { methsig_req_count: methodsigRequestCount }); + event('method-sig-requests', { + methsig_req_count: methodsigRequestCount, + methsig_req_partial_count: (methodsigRequestCount % 25) || 25, + }); } // locate the token at the requested position diff --git a/langserver/package-lock.json b/langserver/package-lock.json index 088504e..ecfdb60 100644 --- a/langserver/package-lock.json +++ b/langserver/package-lock.json @@ -4,20 +4,26 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@amplitude/node": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@amplitude/node/-/node-0.3.3.tgz", + "integrity": "sha512-Uzg4MRAuD053Ex67Iu2lm2GovnVte1uKI3q7CXlMCYZ9ylZmAkPbTnjg9OVyD4f+IiUfgK4p3bE7r9p7jqSDLA==", + "requires": { + "@amplitude/types": "^0.3.2", + "tslib": "^1.9.3" + } + }, + "@amplitude/types": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@amplitude/types/-/types-0.3.2.tgz", + "integrity": "sha512-7+m7nhJMFGbpsppOUsCH8f4FOFyAxgKFuXkKknU/LP2CMYVjWEIoLTKKgaJPc2c8wXaK5KPXVetb8VeiGbuaGg==" + }, "@types/node": { "version": "13.13.4", "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.4.tgz", "integrity": "sha512-x26ur3dSXgv5AwKS0lNfbjpCakGIduWU1DU91Zz58ONRWrIKGunmZBNv4P7N+e27sJkiGDsw/3fT4AtsqQBrBA==", "dev": true }, - "agent-base": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", - "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", - "requires": { - "es6-promisify": "^5.0.0" - } - }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", @@ -79,14 +85,6 @@ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "requires": { - "ms": "^2.1.1" - } - }, "duplexer2": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", @@ -95,19 +93,6 @@ "readable-stream": "^2.0.2" } }, - "es6-promise": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", - "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==" - }, - "es6-promisify": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", - "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", - "requires": { - "es6-promise": "^4.0.3" - } - }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -142,15 +127,6 @@ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" }, - "https-proxy-agent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-3.0.0.tgz", - "integrity": "sha512-y4jAxNEihqvBI5F3SaO2rtsjIOnnNA8sEbuiP+UhJZJHeM2NRm6c09ax2tgqme+SgUUvjao2fJXF4h3D6Cb2HQ==", - "requires": { - "agent-base": "^4.3.0", - "debug": "^3.1.0" - } - }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -195,14 +171,6 @@ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" }, - "mixpanel": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/mixpanel/-/mixpanel-0.11.0.tgz", - "integrity": "sha512-TS7AkCmfC+vGshlCOjEcITFoFxlt5fdSEqmN+d+pTXAhE5v+jPQW2uUcn9W+Oq4NVXz+kdskU09dsm9vmNl0ig==", - "requires": { - "https-proxy-agent": "3.0.0" - } - }, "mkdirp": { "version": "0.5.5", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", @@ -211,11 +179,6 @@ "minimist": "^1.2.5" } }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -279,6 +242,11 @@ "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", "integrity": "sha1-cXuPIgzAu3tE5AUUwisui7xw2Lk=" }, + "tslib": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", + "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==" + }, "unzipper": { "version": "0.10.11", "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.10.11.tgz", diff --git a/langserver/package.json b/langserver/package.json index 4265f53..f9844e9 100644 --- a/langserver/package.json +++ b/langserver/package.json @@ -9,8 +9,8 @@ "author": "", "license": "ISC", "dependencies": { + "@amplitude/node": "^0.3.3", "java-mti": "adelphes/java-mti#ec164ac", - "mixpanel": "0.11.0", "uuid": "8.2.0", "vscode-languageserver": "6.1.1", "vscode-languageserver-textdocument": "1.0.1", diff --git a/langserver/server.js b/langserver/server.js index b9ee522..51478b7 100644 --- a/langserver/server.js +++ b/langserver/server.js @@ -135,7 +135,7 @@ connection.onInitialize((params) => { } Settings.set(startupOpts.initialSettings); - analytics.init(undefined, startupOpts.mpuid, uuidv4(), package_json, { vscode_version: startupOpts.vscodeVersion }); + analytics.init(undefined, startupOpts.uid, startupOpts.session_id, '', package_json, startupOpts.vscode_props, 'langserver-start'); loadCodeCompletionLibrary(startupOpts.extensionPath, Settings.codeCompletionLibraries); diff --git a/src/debugMain.js b/src/debugMain.js index 6e5ff37..b74ccbd 100644 --- a/src/debugMain.js +++ b/src/debugMain.js @@ -6,7 +6,6 @@ const { // node and external modules const os = require('os'); const path = require('path'); -const uuidv4 = require('uuid').v4; // our stuff const { ADBClient } = require('./adbclient'); @@ -111,9 +110,8 @@ class AndroidDebugSession extends DebugSession { this.terminate_reason = ''; - this.session_id = uuidv4(); this.session_start = new Date(); - analytics.init(); + analytics.init(undefined, undefined, undefined, '', require('../package.json'), {}, 'debugger-start'); // this debugger uses one-based lines and columns this.setDebuggerLinesStartAt1(true); @@ -139,8 +137,8 @@ class AndroidDebugSession extends DebugSession { response.body.supportsExceptionInfoRequest = true; response.body.supportsHitConditionalBreakpoints = true; - this.sendResponse(response); - } + this.sendResponse(response); + } /** * @param {string} msg @@ -445,8 +443,7 @@ class AndroidDebugSession extends DebugSession { await this.dbgr.resume(); analytics.event('debug-started', { - dbg_session_id: this.session_id, - dbg_start: this.session_start.toLocaleTimeString(), + dbg_start: this.session_start.toTimeString(), dbg_tz: this.session_start.getTimezoneOffset(), dbg_kind: 'attach', dbg_device_api: this.device_api_level, @@ -611,8 +608,7 @@ class AndroidDebugSession extends DebugSession { await this.dbgr.resume(); analytics.event('debug-started', { - dbg_session_id: this.session_id, - dbg_start: this.session_start.toLocaleTimeString(), + dbg_start: this.session_start.toTimeString(), dbg_tz: this.session_start.getTimezoneOffset(), dbg_kind: 'debug', dbg_device_api: this.device_api_level, @@ -817,7 +813,6 @@ class AndroidDebugSession extends DebugSession { D('disconnectRequest'); this._isDisconnecting = true; analytics.event('debug-end', { - dbg_session_id: this.session_id, dbg_elapsed: Math.trunc((Date.now() - this.session_start.getTime())/1e3), dbg_kind: this.debug_mode, dbg_term_reason: this.terminate_reason,