feat: use vscode log format for client logs

This change updates the log format to use the vscode log format instead
of the custom log format, by replacing the `OutputChannel` with a
`LogOutputChannel` and using the `debug`, `info`, `warn`, and `error`
methods on it. This has the following benefits:

- Each log level now has its own color and the timestamp is in a more
  standard format
- Inspect output (e.g. the log of the config object) is now colored
- Error stack traces are now shown in the output
- The log level is now controlled on the output tab by clicking the gear
  icon and selecting "Debug" or by passing the `--log` parameter to
  vscode. The `trace.extension` setting has been marked as deprecated.
This commit is contained in:
Josh McKinney 2024-07-27 21:43:35 -07:00
parent a46788318c
commit 45a881313a
No known key found for this signature in database
GPG key ID: 722287396A903BC5
6 changed files with 44 additions and 46 deletions

View file

@ -117,9 +117,11 @@ export function isValidExecutable(path: string, extraEnv: Env): boolean {
env: { ...process.env, ...extraEnv },
});
const printOutput = res.error ? log.warn : log.info;
printOutput(path, "--version:", res);
if (res.error) {
log.warn(path, "--version:", res);
} else {
log.info(path, "--version:", res);
}
return res.status === 0;
}

View file

@ -41,7 +41,6 @@ export class Config {
}
private refreshLogging() {
log.setEnabled(this.traceExtension ?? false);
log.info(
"Extension version:",
vscode.extensions.getExtension(this.extensionId)!.packageJSON.version,
@ -253,10 +252,6 @@ export class Config {
await this.cfg.update("checkOnSave", !(value || false), target || null, overrideInLanguage);
}
get traceExtension() {
return this.get<boolean>("trace.extension");
}
get discoverProjectRunner(): string | undefined {
return this.get<string | undefined>("discoverProjectRunner");
}

View file

@ -249,7 +249,8 @@ export class Ctx implements RustAnalyzerExtensionApi {
message +=
'See the logs in "OUTPUT > Rust Analyzer Client" (should open automatically). ';
message += 'To enable verbose logs use { "rust-analyzer.trace.extension": true }';
message +=
'To enable verbose logs, click the gear icon in the "OUTPUT" tab and select "Debug".';
log.error("Bootstrap error", err);
throw new Error(message);

View file

@ -17,49 +17,44 @@ export type Env = {
[name: string]: string;
};
export const log = new (class {
private enabled = true;
private readonly output = vscode.window.createOutputChannel("Rust Analyzer Client");
class Log {
private readonly output = vscode.window.createOutputChannel("Rust Analyzer Client", {
log: true,
});
setEnabled(yes: boolean): void {
log.enabled = yes;
debug(...messages: [unknown, ...unknown[]]): void {
this.output.debug(this.stringify(messages));
}
// Hint: the type [T, ...T[]] means a non-empty array
debug(...msg: [unknown, ...unknown[]]): void {
if (!log.enabled) return;
log.write("DEBUG", ...msg);
info(...messages: [unknown, ...unknown[]]): void {
this.output.info(this.stringify(messages));
}
info(...msg: [unknown, ...unknown[]]): void {
log.write("INFO", ...msg);
warn(...messages: [unknown, ...unknown[]]): void {
this.output.warn(this.stringify(messages));
}
warn(...msg: [unknown, ...unknown[]]): void {
debugger;
log.write("WARN", ...msg);
error(...messages: [unknown, ...unknown[]]): void {
this.output.error(this.stringify(messages));
this.output.show(true);
}
error(...msg: [unknown, ...unknown[]]): void {
debugger;
log.write("ERROR", ...msg);
log.output.show(true);
private stringify(messages: unknown[]): string {
return messages
.map((message) => {
if (typeof message === "string") {
return message;
}
if (message instanceof Error) {
return message.stack || message.message;
}
return inspect(message, { depth: 6, colors: false });
})
.join(" ");
}
}
private write(label: string, ...messageParts: unknown[]): void {
const message = messageParts.map(log.stringify).join(" ");
const dateTime = new Date().toLocaleString();
log.output.appendLine(`${label} [${dateTime}]: ${message}`);
}
private stringify(val: unknown): string {
if (typeof val === "string") return val;
return inspect(val, {
colors: false,
depth: 6, // heuristic
});
}
})();
export const log = new Log();
export function sleep(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms));
@ -135,7 +130,7 @@ export function execute(command: string, options: ExecOptions): Promise<string>
return new Promise((resolve, reject) => {
exec(command, options, (err, stdout, stderr) => {
if (err) {
log.error(err);
log.error("error:", err);
reject(err);
return;
}