diff --git a/src/standardLanguageClient.ts b/src/standardLanguageClient.ts index bb0ca1335..c0c242b31 100644 --- a/src/standardLanguageClient.ts +++ b/src/standardLanguageClient.ts @@ -2,8 +2,8 @@ import * as net from 'net'; import * as path from 'path'; -import { CancellationToken, CodeActionKind, commands, ConfigurationTarget, DocumentSelector, EventEmitter, ExtensionContext, extensions, languages, Location, ProgressLocation, TextEditor, Uri, ViewColumn, window, workspace } from "vscode"; -import { ConfigurationParams, ConfigurationRequest, LanguageClientOptions, Location as LSLocation, MessageType, Position as LSPosition, TextDocumentPositionParams, WorkspaceEdit, StaticFeature, ClientCapabilities, FeatureState } from "vscode-languageclient"; +import { CancellationToken, CodeActionKind, commands, ConfigurationTarget, DocumentSelector, EventEmitter, ExtensionContext, extensions, languages, Location, ProgressLocation, TextEditor, Uri, ViewColumn, window, workspace, WorkspaceConfiguration } from "vscode"; +import { ConfigurationParams, ConfigurationRequest, LanguageClientOptions, Location as LSLocation, MessageType, Position as LSPosition, TextDocumentPositionParams, WorkspaceEdit, StaticFeature, ClientCapabilities, FeatureState, TelemetryEventNotification } from "vscode-languageclient"; import { LanguageClient, StreamInfo } from "vscode-languageclient/node"; import { apiManager } from "./apiManager"; import * as buildPath from './buildpath'; @@ -336,10 +336,45 @@ export class StandardLanguageClient { return result; }); + function getJavaSettingsForTelemetry(config: WorkspaceConfiguration) { + // settings whose values we can record + const SETTINGS_BASIC = [ + "java.quickfix.showAt", "java.symbols.includeSourceMethodDeclarations", + "java.completion.collapseCompletionItems", "java.completion.guessMethodArguments", + "java.cleanup.actionsOnSave", "java.completion.postfix.enabled", + "java.sharedIndexes.enabled", "java.inlayHints.parameterNames.enabled", + "java.server.launchMode", "java.autobuild.enabled" + ]; + // settings where we only record their existence + const SETTINGS_CUSTOM = [ + "java.settings.url", "java.format.settings.url" + ]; + + let value: any; + const properties = {}; + + for (const key of SETTINGS_CUSTOM) { + if (config.get(key)) { + properties[key] = true; + } + } + for (const key of SETTINGS_BASIC) { + value = config.get(key); + if (value !== undefined) { + properties[key] = value; + } + } + + return properties; + } + this.languageClient.onTelemetry(async (e: TelemetryEvent) => { apiManager.fireTraceEvent(e); if (e.name === Telemetry.SERVER_INITIALIZED_EVT) { - return Telemetry.sendTelemetry(Telemetry.STARTUP_EVT, e.properties); + const javaSettings = getJavaSettingsForTelemetry(workspace.getConfiguration()); + + const properties= { ...e.properties, ...javaSettings }; + await Telemetry.sendTelemetry(Telemetry.STARTUP_EVT, ); } else if (e.name === Telemetry.LS_ERROR) { const tags = []; const exception: string = e?.properties.exception; @@ -357,8 +392,10 @@ export class StandardLanguageClient { if (tags.length > 0) { e.properties['tags'] = tags; - return Telemetry.sendTelemetry(Telemetry.LS_ERROR, e.properties); + await Telemetry.sendTelemetry(Telemetry.LS_ERROR, e.properties); } + } else if (e.name === Telemetry.IMPORT_PROJECT) { + await Telemetry.sendTelemetry(Telemetry.IMPORT_PROJECT, e.properties); } }); diff --git a/src/telemetry.ts b/src/telemetry.ts index 9b0b6c351..db54ee883 100644 --- a/src/telemetry.ts +++ b/src/telemetry.ts @@ -1,5 +1,6 @@ import { TelemetryService, getRedHatService } from "@redhat-developer/vscode-redhat-telemetry"; import { ExtensionContext, workspace, WorkspaceConfiguration } from "vscode"; +import { cyrb53 } from "./utils"; /** * Wrap vscode-redhat-telemetry to suit vscode-java @@ -10,7 +11,10 @@ export namespace Telemetry { export const COMPLETION_EVENT = "textCompletion"; export const SERVER_INITIALIZED_EVT = "java.workspace.initialized"; export const LS_ERROR = "java.ls.error"; + export const IMPORT_PROJECT = "java.workspace.importProject"; + let telemetryManager: TelemetryService = null; + let workspaceHash; /** * Starts the telemetry service @@ -22,6 +26,10 @@ export namespace Telemetry { if (!!telemetryManager) { throw new Error("The telemetry service for vscode-java has already been started"); } + workspaceHash = cyrb53(workspace.workspaceFolders.map(f => f.uri.toString()).join('|')); + workspace.onDidChangeWorkspaceFolders(() => { + workspaceHash = cyrb53(workspace.workspaceFolders.map(f => f.uri.toString()).join('|')); + }); const redhatService = await getRedHatService(context); const telemService = await redhatService.getTelemetryService(); telemetryManager = telemService; @@ -35,54 +43,17 @@ export namespace Telemetry { * @param data the telemetry data * @throws Error if the telemetry service has not been started yet */ - export async function sendTelemetry(eventName: string, data?: any): Promise { + export async function sendTelemetry(eventName: string, data?: object): Promise { + console.log(`Sending telemetry event: ${eventName} with data: ${JSON.stringify(data)}`); if (!telemetryManager) { throw new Error("The telemetry service for vscode-java has not been started yet"); } - const javaSettings = getJavaSettingsForTelemetry(workspace.getConfiguration()); - - let properties: any; - if (eventName === STARTUP_EVT) { - properties= { ...data, ...javaSettings }; - } else { - properties= { ...data}; - } - return telemetryManager.send({ + const event = { name: eventName, - properties - }); - } - - function getJavaSettingsForTelemetry(config: WorkspaceConfiguration) { - // settings whose values we can record - const SETTINGS_BASIC = [ - "java.quickfix.showAt", "java.symbols.includeSourceMethodDeclarations", - "java.completion.collapseCompletionItems", "java.completion.guessMethodArguments", - "java.cleanup.actionsOnSave", "java.completion.postfix.enabled", - "java.sharedIndexes.enabled", "java.inlayHints.parameterNames.enabled", - "java.server.launchMode", "java.autobuild.enabled" - ]; - // settings where we only record their existence - const SETTINGS_CUSTOM = [ - "java.settings.url", "java.format.settings.url" - ]; - - let value: any; - const properties = {}; - - for (const key of SETTINGS_CUSTOM) { - if (config.get(key)) { - properties[key] = true; - } - } - for (const key of SETTINGS_BASIC) { - value = config.get(key); - if (value !== undefined) { - properties[key] = value; - } - } + properties: { workspaceHash, ...data} + }; - return properties; + return telemetryManager.send(event); } } diff --git a/src/utils.ts b/src/utils.ts index 989f3ab24..46334ddc6 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -362,3 +362,23 @@ export function getVSCodeVariablesMap(): any { keys.forEach(key => res[key] = vscodeVariables(`\${${key}}`)); return res; } + +/* +* cyrb53 (c) 2018 bryc (github.com/bryc) +* Public domain (or MIT if needed). Attribution appreciated. +* A fast and simple 53-bit string hash function with decent collision resistance. +* Largely inspired by MurmurHash2/3, but with a focus on speed/simplicity. +*/ +export function cyrb53 (str, seed = 0) { + let h1 = 0xdeadbeef ^ seed, h2 = 0x41c6ce57 ^ seed; + for (let i = 0, ch; i < str.length; i++) { + ch = str.charCodeAt(i); + h1 = Math.imul(h1 ^ ch, 2654435761); + h2 = Math.imul(h2 ^ ch, 1597334677); + } + h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507); + h1 ^= Math.imul(h2 ^ (h2 >>> 13), 3266489909); + h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507); + h2 ^= Math.imul(h1 ^ (h1 >>> 13), 3266489909); + return 4294967296 * (2097151 & h2) + (h1 >>> 0); +}; \ No newline at end of file