Skip to content

Commit 661eed6

Browse files
authored
Use VS Code's LogOutputChannel for logging (#553)
1 parent 8dee463 commit 661eed6

14 files changed

+118
-148
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
and configFile are provided.
77
- Add `coder.disableUpdateNotifications` setting to disable workspace template
88
update notifications.
9+
- Coder output panel enhancements: All log entries now include timestamps, and you
10+
can filter messages by log level in the panel.
911

1012
## [v1.9.2](https://github.com/coder/vscode-coder/releases/tag/v1.9.2) 2025-06-25
1113

src/api.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ export function makeCoderSdk(
105105
restClient.getAxiosInstance().interceptors.response.use(
106106
(r) => r,
107107
async (err) => {
108-
throw await CertificateError.maybeWrap(err, baseUrl, storage);
108+
throw await CertificateError.maybeWrap(err, baseUrl, storage.output);
109109
},
110110
);
111111

src/commands.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -245,8 +245,9 @@ export class Commands {
245245
} catch (err) {
246246
const message = getErrorMessage(err, "no response from the server");
247247
if (isAutologin) {
248-
this.storage.writeToCoderOutputChannel(
249-
`Failed to log in to Coder server: ${message}`,
248+
this.storage.output.warn(
249+
"Failed to log in to Coder server:",
250+
message,
250251
);
251252
} else {
252253
this.vscodeProposed.window.showErrorMessage(

src/error.test.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import https from "https";
44
import * as path from "path";
55
import { afterAll, beforeAll, it, expect, vi } from "vitest";
66
import { CertificateError, X509_ERR, X509_ERR_CODE } from "./error";
7+
import { Logger } from "./logger";
78

89
// Before each test we make a request to sanity check that we really get the
910
// error we are expecting, then we run it through CertificateError.
@@ -23,10 +24,16 @@ beforeAll(() => {
2324
});
2425
});
2526

26-
const logger = {
27-
writeToCoderOutputChannel(message: string) {
28-
throw new Error(message);
29-
},
27+
const throwingLog = (message: string) => {
28+
throw new Error(message);
29+
};
30+
31+
const logger: Logger = {
32+
trace: throwingLog,
33+
debug: throwingLog,
34+
info: throwingLog,
35+
warn: throwingLog,
36+
error: throwingLog,
3037
};
3138

3239
const disposers: (() => void)[] = [];

src/error.ts

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { isApiError, isApiErrorResponse } from "coder/site/src/api/errors";
33
import * as forge from "node-forge";
44
import * as tls from "tls";
55
import * as vscode from "vscode";
6+
import { Logger } from "./logger";
67

78
// X509_ERR_CODE represents error codes as returned from BoringSSL/OpenSSL.
89
export enum X509_ERR_CODE {
@@ -21,10 +22,6 @@ export enum X509_ERR {
2122
UNTRUSTED_CHAIN = "Your Coder deployment's certificate chain does not appear to be trusted by this system. The root of the certificate chain must be added to this system's trust store. ",
2223
}
2324

24-
export interface Logger {
25-
writeToCoderOutputChannel(message: string): void;
26-
}
27-
2825
interface KeyUsage {
2926
keyCertSign: boolean;
3027
}
@@ -59,9 +56,7 @@ export class CertificateError extends Error {
5956
await CertificateError.determineVerifyErrorCause(address);
6057
return new CertificateError(err.message, cause);
6158
} catch (error) {
62-
logger.writeToCoderOutputChannel(
63-
`Failed to parse certificate from ${address}: ${error}`,
64-
);
59+
logger.warn(`Failed to parse certificate from ${address}`, error);
6560
break;
6661
}
6762
case X509_ERR_CODE.DEPTH_ZERO_SELF_SIGNED_CERT:

src/extension.ts

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ export async function activate(ctx: vscode.ExtensionContext): Promise<void> {
4747
);
4848
}
4949

50-
const output = vscode.window.createOutputChannel("Coder");
50+
const output = vscode.window.createOutputChannel("Coder", { log: true });
5151
const storage = new Storage(
5252
output,
5353
ctx.globalState,
@@ -317,7 +317,7 @@ export async function activate(ctx: vscode.ExtensionContext): Promise<void> {
317317
}
318318
} catch (ex) {
319319
if (ex instanceof CertificateError) {
320-
storage.writeToCoderOutputChannel(ex.x509Err || ex.message);
320+
storage.output.warn(ex.x509Err || ex.message);
321321
await ex.showModal("Failed to open workspace");
322322
} else if (isAxiosError(ex)) {
323323
const msg = getErrorMessage(ex, "None");
@@ -326,7 +326,7 @@ export async function activate(ctx: vscode.ExtensionContext): Promise<void> {
326326
const method = ex.config?.method?.toUpperCase() || "request";
327327
const status = ex.response?.status || "None";
328328
const message = `API ${method} to '${urlString}' failed.\nStatus code: ${status}\nMessage: ${msg}\nDetail: ${detail}`;
329-
storage.writeToCoderOutputChannel(message);
329+
storage.output.warn(message);
330330
await vscodeProposed.window.showErrorMessage(
331331
"Failed to open workspace",
332332
{
@@ -337,7 +337,7 @@ export async function activate(ctx: vscode.ExtensionContext): Promise<void> {
337337
);
338338
} else {
339339
const message = errToStr(ex, "No error message was provided");
340-
storage.writeToCoderOutputChannel(message);
340+
storage.output.warn(message);
341341
await vscodeProposed.window.showErrorMessage(
342342
"Failed to open workspace",
343343
{
@@ -356,14 +356,12 @@ export async function activate(ctx: vscode.ExtensionContext): Promise<void> {
356356
// See if the plugin client is authenticated.
357357
const baseUrl = restClient.getAxiosInstance().defaults.baseURL;
358358
if (baseUrl) {
359-
storage.writeToCoderOutputChannel(
360-
`Logged in to ${baseUrl}; checking credentials`,
361-
);
359+
storage.output.info(`Logged in to ${baseUrl}; checking credentials`);
362360
restClient
363361
.getAuthenticatedUser()
364362
.then(async (user) => {
365363
if (user && user.roles) {
366-
storage.writeToCoderOutputChannel("Credentials are valid");
364+
storage.output.info("Credentials are valid");
367365
vscode.commands.executeCommand(
368366
"setContext",
369367
"coder.authenticated",
@@ -381,17 +379,13 @@ export async function activate(ctx: vscode.ExtensionContext): Promise<void> {
381379
myWorkspacesProvider.fetchAndRefresh();
382380
allWorkspacesProvider.fetchAndRefresh();
383381
} else {
384-
storage.writeToCoderOutputChannel(
385-
`No error, but got unexpected response: ${user}`,
386-
);
382+
storage.output.warn("No error, but got unexpected response", user);
387383
}
388384
})
389385
.catch((error) => {
390386
// This should be a failure to make the request, like the header command
391387
// errored.
392-
storage.writeToCoderOutputChannel(
393-
`Failed to check user authentication: ${error.message}`,
394-
);
388+
storage.output.warn("Failed to check user authentication", error);
395389
vscode.window.showErrorMessage(
396390
`Failed to check user authentication: ${error.message}`,
397391
);
@@ -400,7 +394,7 @@ export async function activate(ctx: vscode.ExtensionContext): Promise<void> {
400394
vscode.commands.executeCommand("setContext", "coder.loaded", true);
401395
});
402396
} else {
403-
storage.writeToCoderOutputChannel("Not currently logged in");
397+
storage.output.info("Not currently logged in");
404398
vscode.commands.executeCommand("setContext", "coder.loaded", true);
405399

406400
// Handle autologin, if not already logged in.

src/headers.test.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,14 @@ import * as os from "os";
22
import { it, expect, describe, beforeEach, afterEach, vi } from "vitest";
33
import { WorkspaceConfiguration } from "vscode";
44
import { getHeaderCommand, getHeaders } from "./headers";
5-
6-
const logger = {
7-
writeToCoderOutputChannel() {
8-
// no-op
9-
},
5+
import { Logger } from "./logger";
6+
7+
const logger: Logger = {
8+
trace: () => {},
9+
debug: () => {},
10+
info: () => {},
11+
warn: () => {},
12+
error: () => {},
1013
};
1114

1215
it("should return no headers", async () => {

src/headers.ts

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,9 @@ import * as cp from "child_process";
22
import * as os from "os";
33
import * as util from "util";
44
import type { WorkspaceConfiguration } from "vscode";
5+
import { Logger } from "./logger";
56
import { escapeCommandArg } from "./util";
67

7-
export interface Logger {
8-
writeToCoderOutputChannel(message: string): void;
9-
}
10-
118
interface ExecException {
129
code?: number;
1310
stderr?: string;
@@ -78,11 +75,9 @@ export async function getHeaders(
7875
});
7976
} catch (error) {
8077
if (isExecException(error)) {
81-
logger.writeToCoderOutputChannel(
82-
`Header command exited unexpectedly with code ${error.code}`,
83-
);
84-
logger.writeToCoderOutputChannel(`stdout: ${error.stdout}`);
85-
logger.writeToCoderOutputChannel(`stderr: ${error.stderr}`);
78+
logger.warn("Header command exited unexpectedly with code", error.code);
79+
logger.warn("stdout:", error.stdout);
80+
logger.warn("stderr:", error.stderr);
8681
throw new Error(
8782
`Header command exited unexpectedly with code ${error.code}`,
8883
);

src/inbox.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ export class Inbox implements vscode.Disposable {
6363
});
6464

6565
this.#socket.on("open", () => {
66-
this.#storage.writeToCoderOutputChannel("Listening to Coder Inbox");
66+
this.#storage.output.info("Listening to Coder Inbox");
6767
});
6868

6969
this.#socket.on("error", (error) => {
@@ -86,9 +86,7 @@ export class Inbox implements vscode.Disposable {
8686

8787
dispose() {
8888
if (!this.#disposed) {
89-
this.#storage.writeToCoderOutputChannel(
90-
"No longer listening to Coder Inbox",
91-
);
89+
this.#storage.output.info("No longer listening to Coder Inbox");
9290
this.#socket.close();
9391
this.#disposed = true;
9492
}
@@ -99,6 +97,6 @@ export class Inbox implements vscode.Disposable {
9997
error,
10098
"Got empty error while monitoring Coder Inbox",
10199
);
102-
this.#storage.writeToCoderOutputChannel(message);
100+
this.#storage.output.error(message);
103101
}
104102
}

src/logger.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export interface Logger {
2+
trace(message: string, ...args: unknown[]): void;
3+
debug(message: string, ...args: unknown[]): void;
4+
info(message: string, ...args: unknown[]): void;
5+
warn(message: string, ...args: unknown[]): void;
6+
error(message: string, ...args: unknown[]): void;
7+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy