Make sure we also log the typescript error properties on fatal error telemetry events
Fixes #86205 We already log error metadata for failed requests. However we don't include this on the fatalError event. This makes investigation of these errors difficult
This commit is contained in:
parent
9fd89ca349
commit
57455124b5
|
@ -60,7 +60,7 @@ export interface ITypeScriptServer {
|
|||
}
|
||||
|
||||
export interface TsServerDelegate {
|
||||
onFatalError(command: string): void;
|
||||
onFatalError(command: string, error: Error): void;
|
||||
}
|
||||
|
||||
export interface TsServerProcess {
|
||||
|
@ -222,21 +222,13 @@ export class ProcessBasedTsServer extends Disposable implements ITypeScriptServe
|
|||
if (!executeInfo.token || !executeInfo.token.isCancellationRequested) {
|
||||
/* __GDPR__
|
||||
"languageServiceErrorResponse" : {
|
||||
"command" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"message" : { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" },
|
||||
"stack" : { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" },
|
||||
"errortext" : { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" },
|
||||
"${include}": [
|
||||
"${TypeScriptCommonProperties}"
|
||||
"${TypeScriptCommonProperties}",
|
||||
"${TypeScriptRequestErrorProperties}"
|
||||
]
|
||||
}
|
||||
*/
|
||||
this._telemetryReporter.logTelemetry('languageServiceErrorResponse', {
|
||||
command: err.serverCommand,
|
||||
message: err.serverMessage || '',
|
||||
stack: err.serverStack || '',
|
||||
errortext: err.serverErrorText || '',
|
||||
});
|
||||
this._telemetryReporter.logTelemetry('languageServiceErrorResponse', err.telemetry);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -367,9 +359,8 @@ export class SyntaxRoutingTsServer extends Disposable implements ITypeScriptServ
|
|||
} else if (SyntaxRoutingTsServer.sharedCommands.has(command)) {
|
||||
// Dispatch to both server but only return from syntax one
|
||||
|
||||
const enum RequestState { Unresolved, Resolved, Errored }
|
||||
let syntaxRequestState = RequestState.Unresolved;
|
||||
let semanticRequestState = RequestState.Unresolved;
|
||||
let syntaxRequestState: RequestState.State = RequestState.Unresolved;
|
||||
let semanticRequestState: RequestState.State = RequestState.Unresolved;
|
||||
|
||||
// Also make sure we never cancel requests to just one server
|
||||
let token: vscode.CancellationToken | undefined = undefined;
|
||||
|
@ -394,16 +385,16 @@ export class SyntaxRoutingTsServer extends Disposable implements ITypeScriptServ
|
|||
semanticRequest
|
||||
.then(result => {
|
||||
semanticRequestState = RequestState.Resolved;
|
||||
if (syntaxRequestState === RequestState.Errored) {
|
||||
if (syntaxRequestState.type === RequestState.Type.Errored) {
|
||||
// We've gone out of sync
|
||||
this._delegate.onFatalError(command);
|
||||
this._delegate.onFatalError(command, syntaxRequestState.err);
|
||||
}
|
||||
return result;
|
||||
}, err => {
|
||||
semanticRequestState = RequestState.Errored;
|
||||
semanticRequestState = new RequestState.Errored(err);
|
||||
if (syntaxRequestState === RequestState.Resolved) {
|
||||
// We've gone out of sync
|
||||
this._delegate.onFatalError(command);
|
||||
this._delegate.onFatalError(command, err);
|
||||
}
|
||||
throw err;
|
||||
});
|
||||
|
@ -413,16 +404,16 @@ export class SyntaxRoutingTsServer extends Disposable implements ITypeScriptServ
|
|||
syntaxRequest
|
||||
.then(result => {
|
||||
syntaxRequestState = RequestState.Resolved;
|
||||
if (semanticRequestState === RequestState.Errored) {
|
||||
if (semanticRequestState.type === RequestState.Type.Errored) {
|
||||
// We've gone out of sync
|
||||
this._delegate.onFatalError(command);
|
||||
this._delegate.onFatalError(command, semanticRequestState.err);
|
||||
}
|
||||
return result;
|
||||
}, err => {
|
||||
syntaxRequestState = RequestState.Errored;
|
||||
syntaxRequestState = new RequestState.Errored(err);
|
||||
if (semanticRequestState === RequestState.Resolved) {
|
||||
// We've gone out of sync
|
||||
this._delegate.onFatalError(command);
|
||||
this._delegate.onFatalError(command, err);
|
||||
}
|
||||
throw err;
|
||||
});
|
||||
|
@ -433,3 +424,21 @@ export class SyntaxRoutingTsServer extends Disposable implements ITypeScriptServ
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace RequestState {
|
||||
export const enum Type { Unresolved, Resolved, Errored }
|
||||
|
||||
export const Unresolved = { type: Type.Unresolved } as const;
|
||||
|
||||
export const Resolved = { type: Type.Resolved } as const;
|
||||
|
||||
export class Errored {
|
||||
readonly type = Type.Errored;
|
||||
|
||||
constructor(
|
||||
public readonly err: Error
|
||||
) { }
|
||||
}
|
||||
|
||||
export type State = typeof Unresolved | typeof Resolved | Errored;
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import * as Proto from '../protocol';
|
|||
import { escapeRegExp } from '../utils/regexp';
|
||||
import { TypeScriptVersion } from '../utils/versionProvider';
|
||||
|
||||
|
||||
export class TypeScriptServerError extends Error {
|
||||
public static create(
|
||||
serverId: string,
|
||||
|
@ -31,6 +32,23 @@ export class TypeScriptServerError extends Error {
|
|||
|
||||
public get serverCommand() { return this.response.command; }
|
||||
|
||||
public get telemetry() {
|
||||
/* __GDPR__FRAGMENT__
|
||||
"TypeScriptRequestErrorProperties" : {
|
||||
"command" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"message" : { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" },
|
||||
"stack" : { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" },
|
||||
"errortext" : { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" }
|
||||
}
|
||||
*/
|
||||
return {
|
||||
command: this.serverCommand,
|
||||
message: this.serverMessage || '',
|
||||
stack: this.serverStack || '',
|
||||
errortext: this.serverErrorText || '',
|
||||
} as const;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a `errorText` from a tsserver request indicating failure in handling a request,
|
||||
* prepares a payload for telemetry-logging.
|
||||
|
|
|
@ -11,7 +11,9 @@ import BufferSyncSupport from './features/bufferSyncSupport';
|
|||
import { DiagnosticKind, DiagnosticsManager } from './features/diagnostics';
|
||||
import * as Proto from './protocol';
|
||||
import { ITypeScriptServer } from './tsServer/server';
|
||||
import { ITypeScriptServiceClient, ServerResponse, TypeScriptRequests, ExecConfig } from './typescriptService';
|
||||
import { TypeScriptServerError } from './tsServer/serverError';
|
||||
import { TypeScriptServerSpawner } from './tsServer/spawner';
|
||||
import { ExecConfig, ITypeScriptServiceClient, ServerResponse, TypeScriptRequests } from './typescriptService';
|
||||
import API from './utils/api';
|
||||
import { TsServerLogLevel, TypeScriptServiceConfiguration } from './utils/configuration';
|
||||
import { Disposable } from './utils/dispose';
|
||||
|
@ -25,7 +27,6 @@ import Tracer from './utils/tracer';
|
|||
import { inferredProjectConfig } from './utils/tsconfig';
|
||||
import { TypeScriptVersionPicker } from './utils/versionPicker';
|
||||
import { TypeScriptVersion, TypeScriptVersionProvider } from './utils/versionProvider';
|
||||
import { TypeScriptServerSpawner } from './tsServer/spawner';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
|
@ -314,7 +315,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType
|
|||
this.onDidChangeTypeScriptVersion(currentVersion);
|
||||
let mytoken = ++this.token;
|
||||
const handle = this.typescriptServerSpawner.spawn(currentVersion, this.configuration, this.pluginManager, {
|
||||
onFatalError: (command) => this.fatalError(command),
|
||||
onFatalError: (command, err) => this.fatalError(command, err),
|
||||
});
|
||||
this.serverState = new ServerState.Running(handle, apiVersion, undefined, true);
|
||||
this.lastStart = Date.now();
|
||||
|
@ -670,7 +671,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType
|
|||
}
|
||||
|
||||
if (config?.nonRecoverable) {
|
||||
execution.catch(() => this.fatalError(command));
|
||||
execution.catch(err => this.fatalError(command, err));
|
||||
}
|
||||
|
||||
return execution;
|
||||
|
@ -704,14 +705,16 @@ export default class TypeScriptServiceClient extends Disposable implements IType
|
|||
return this.bufferSyncSupport.interuptGetErr(f);
|
||||
}
|
||||
|
||||
private fatalError(command: string): void {
|
||||
private fatalError(command: string, error: Error): void {
|
||||
/* __GDPR__
|
||||
"fatalError" : {
|
||||
"${include}": [ "${TypeScriptCommonProperties}" ],
|
||||
"command" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
"${include}": [
|
||||
"${TypeScriptCommonProperties}",
|
||||
"${TypeScriptRequestErrorProperties}"
|
||||
]
|
||||
}
|
||||
*/
|
||||
this.logTelemetry('fatalError', { command });
|
||||
this.logTelemetry('fatalError', { command, ...(error instanceof TypeScriptServerError ? error.telemetry : {}) });
|
||||
console.error(`A non-recoverable error occured while executing tsserver command: ${command}`);
|
||||
|
||||
if (this.serverState.type === ServerState.Type.Running) {
|
||||
|
|
Loading…
Reference in a new issue