Skip to content

Commit 3b5df0c

Browse files
Track LSP usage stats (#1240)
1 parent d65d37a commit 3b5df0c

File tree

2 files changed

+76
-6
lines changed

2 files changed

+76
-6
lines changed

src/daemon/index.ts

Lines changed: 74 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -91,22 +91,26 @@ const INTERESTED_REQUESTS: Set<string> = new Set([
9191
const CANCELLATION_CODE: number = -32800; // report such error if the request is cancelled.
9292
const CONTENT_MODIFIED_CODE: number = -32801; // report such error if semantic token request is outdated while content modified.
9393
const INTERNAL_ERROR_CODE: number = -32603; // Internal Error.
94+
let lspUsageStats: LSPUsageStats;
9495
async function traceLSPPerformance(javaExt: vscode.Extension<any>) {
9596
const javaExtVersion = javaExt.packageJSON?.version;
9697
const isPreReleaseVersion = /^\d+\.\d+\.\d{10}/.test(javaExtVersion);
9798
const redHatTelemetryEnabled = workspace.getConfiguration('redhat.telemetry').get('enabled', false);
9899
const isTreatment = !isPreReleaseVersion &&
99100
(redHatTelemetryEnabled || await getExpService()?.getTreatmentVariableAsync(TreatmentVariables.VSCodeConfig, TreatmentVariables.JavaCompletionSampling, true /*checkCache*/));
100101
const sampling: string = isPreReleaseVersion ? "pre-release" : (isTreatment ? "sampling" : "");
102+
if (!isPreReleaseVersion && !isTreatment) {
103+
return;
104+
}
105+
106+
lspUsageStats = new LSPUsageStats(javaExtVersion, sampling);
101107
// Trace the interested LSP requests performance
102108
javaExt.exports?.onDidRequestEnd?.((traceEvent: any) => {
103-
if (!isPreReleaseVersion && !isTreatment) {
104-
return;
105-
}
106-
109+
lspUsageStats.addRequest(traceEvent.type);
107110
// Trace the timeout requests
108111
if (traceEvent.duration > 5000
109112
|| (traceEvent.duration > 1000 && RESPONSIVE_REQUESTS.has(traceEvent.type))) {
113+
lspUsageStats.addTimeoutRequest(traceEvent.type);
110114
sendInfo("", {
111115
name: "lsp.timeout",
112116
kind: escapeLspRequestName(traceEvent.type),
@@ -124,6 +128,7 @@ async function traceLSPPerformance(javaExt: vscode.Extension<any>) {
124128
return;
125129
}
126130

131+
lspUsageStats.addErrorRequest(traceEvent.type);
127132
// See https://github.com/eclipse-lsp4j/lsp4j/commit/bf22871f4e669a2d7fd97ce046cb50903aa68120#diff-3b3e5d6517a47e0459195078645a0837aafa4d4520fe79b1cb1922a749074748
128133
// lsp4j will wrap the error message as "Internal error."
129134
// when it encounters an uncaught exception from jdt language server.
@@ -166,7 +171,10 @@ async function traceLSPPerformance(javaExt: vscode.Extension<any>) {
166171
remark: sampling,
167172
data: redactDataProperties(traceEvent.data),
168173
});
169-
return;
174+
}
175+
176+
if (traceEvent.resultLength === 0) {
177+
lspUsageStats.addNoResultRequest(traceEvent.type);
170178
}
171179
});
172180
}
@@ -287,3 +295,64 @@ function resolveActualCause(callstack: any): Exception | undefined {
287295

288296
return;
289297
}
298+
299+
export function sendLSPUsageStats() {
300+
if (lspUsageStats) {
301+
lspUsageStats.sendStats();
302+
}
303+
}
304+
305+
class LSPUsageStats {
306+
private totalRequests: any = {};
307+
private timeoutRequests: any = {};
308+
private errorRequests: any = {};
309+
private noResultRequests: any = {};
310+
311+
public constructor(readonly javaExtVersion: string, readonly sampling: string) {
312+
}
313+
314+
public addRequest(type: string) {
315+
this.totalRequests[type] = (this.totalRequests[type] || 0) + 1;
316+
}
317+
318+
public addTimeoutRequest(type: string) {
319+
this.timeoutRequests[type] = (this.timeoutRequests[type] || 0) + 1;
320+
}
321+
322+
public addErrorRequest(type: string) {
323+
this.errorRequests[type] = (this.errorRequests[type] || 0) + 1;
324+
}
325+
326+
public addNoResultRequest(type: string) {
327+
this.noResultRequests[type] = (this.noResultRequests[type] || 0) + 1;
328+
}
329+
330+
public sendStats() {
331+
if (Object.keys(this.totalRequests).length) {
332+
const data: any = {};
333+
for (const key of Object.keys(this.totalRequests)) {
334+
const simpleKey = this.getSimpleKey(key);
335+
data[simpleKey] = [this.totalRequests[key],
336+
this.timeoutRequests[key] || 0,
337+
this.errorRequests[key] || 0,
338+
this.noResultRequests[key] || 0];
339+
}
340+
sendInfo("", {
341+
name: "lsp.aggregate",
342+
javaversion: this.javaExtVersion,
343+
remark: this.sampling,
344+
data: JSON.stringify(data),
345+
});
346+
}
347+
}
348+
349+
private getSimpleKey(key: string): string {
350+
if (key.startsWith("workspace/executeCommand/")) {
351+
return key.replace("workspace/executeCommand/", "we/");
352+
}
353+
if (key.startsWith("textDocument/")) {
354+
return key.replace("textDocument/", "td/");
355+
}
356+
return key;
357+
}
358+
}

src/extension.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { dispose as disposeTelemetryWrapper, initialize, instrumentOperation, se
88
import { BeginnerTipsViewSerializer } from "./beginner-tips";
99
import { ClassPathConfigurationViewSerializer } from "./classpath/classpathConfigurationView";
1010
import { initialize as initCommands } from "./commands";
11-
import { initDaemon } from "./daemon";
11+
import { initDaemon, sendLSPUsageStats } from "./daemon";
1212
import { initialize as initExp } from "./exp";
1313
import { JavaExtGuideViewSerializer } from "./ext-guide";
1414
import { initFormatterSettingsEditorProvider } from "./formatter-settings";
@@ -111,5 +111,6 @@ export async function deactivate() {
111111
data.name = "cleanJavaLSWorkspace";
112112
}
113113
sendInfo("", data);
114+
sendLSPUsageStats();
114115
await disposeTelemetryWrapper();
115116
}

0 commit comments

Comments
 (0)