From 6cdcaa9ad4a2b3b2fed44af3b70da052ef568b86 Mon Sep 17 00:00:00 2001 From: Karthik Nadig Date: Wed, 7 May 2025 13:18:03 -0700 Subject: [PATCH] chore: add logging functions that log to output channel --- src/common/logging.ts | 57 ++++++++++++++++++++++++++++++++++++++++ src/common/scope-map.ts | 5 ++-- src/extension.ts | 6 +++-- src/hatch-cli.ts | 3 ++- src/hatch-env-manager.ts | 25 ++++++++---------- 5 files changed, 77 insertions(+), 19 deletions(-) create mode 100644 src/common/logging.ts diff --git a/src/common/logging.ts b/src/common/logging.ts new file mode 100644 index 0000000..a08aa0c --- /dev/null +++ b/src/common/logging.ts @@ -0,0 +1,57 @@ +import * as util from 'node:util' +import type { Disposable, LogOutputChannel } from 'vscode' + +type Arguments = unknown[] +class OutputChannelLogger { + constructor(private readonly channel: LogOutputChannel) {} + + public traceLog(...data: Arguments): void { + this.channel.appendLine(util.format(...data)) + } + + public traceError(...data: Arguments): void { + this.channel.error(util.format(...data)) + } + + public traceWarn(...data: Arguments): void { + this.channel.warn(util.format(...data)) + } + + public traceInfo(...data: Arguments): void { + this.channel.info(util.format(...data)) + } + + public traceVerbose(...data: Arguments): void { + this.channel.debug(util.format(...data)) + } +} + +let channel: OutputChannelLogger | undefined +export function registerLogger(logChannel: LogOutputChannel): Disposable { + channel = new OutputChannelLogger(logChannel) + return { + dispose: () => { + channel = undefined + }, + } +} + +export function traceLog(...args: Arguments): void { + channel?.traceLog(...args) +} + +export function traceError(...args: Arguments): void { + channel?.traceError(...args) +} + +export function traceWarn(...args: Arguments): void { + channel?.traceWarn(...args) +} + +export function traceInfo(...args: Arguments): void { + channel?.traceInfo(...args) +} + +export function traceVerbose(...args: Arguments): void { + channel?.traceVerbose(...args) +} diff --git a/src/common/scope-map.ts b/src/common/scope-map.ts index 913b615..decd009 100644 --- a/src/common/scope-map.ts +++ b/src/common/scope-map.ts @@ -1,6 +1,7 @@ import paths from 'node:path' import { Uri } from 'vscode' import type { PythonEnvironment } from '../vscode-python-environments' +import { traceLog } from './logging' export class ScopeMap { map: Map @@ -23,10 +24,10 @@ export class ScopeMap { while (key && !this.map.has(key.fsPath)) { const parent = paths.dirname(key.fsPath) if (parent === key.fsPath) { - console.log('hit root from', keyOrig?.fsPath) + traceLog('hit root from', keyOrig?.fsPath) break } - console.log('no env for %s, trying %s', key.fsPath, parent) + traceLog(`no env for ${key.fsPath}, trying ${parent}`) key = Uri.file(parent) } return this.map.get(key?.fsPath) diff --git a/src/extension.ts b/src/extension.ts index 5bffe9b..b3d7f12 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,13 +1,15 @@ import { type ExtensionContext, window } from 'vscode' +import { registerLogger } from './common/logging' import { HatchEnvManager } from './hatch-env-manager' import { getEnvExtApi } from './python-envs-api' export async function activate(context: ExtensionContext) { const log = window.createOutputChannel('Hatch', { log: true }) - context.subscriptions.push(log) + + context.subscriptions.push(log, registerLogger(log)) const api = await getEnvExtApi() - const envManager = new HatchEnvManager(api, log) + const envManager = new HatchEnvManager(api) context.subscriptions.push(api.registerEnvironmentManager(envManager)) } diff --git a/src/hatch-cli.ts b/src/hatch-cli.ts index f0fbf1f..7ef0234 100644 --- a/src/hatch-cli.ts +++ b/src/hatch-cli.ts @@ -5,6 +5,7 @@ import { } from 'node:child_process' import { promisify } from 'node:util' import type { Uri } from 'vscode' +import { traceError } from './common/logging' const execFile = promisify(execFileCb) @@ -66,7 +67,7 @@ async function run(cmd: string, args: string[], opts: ProcessEnvOptions): Promis return stdout } catch (e) { const err = e as ExecFileException - console.error(err.stderr) + traceError(err.stderr) throw err } } diff --git a/src/hatch-env-manager.ts b/src/hatch-env-manager.ts index 7eb0499..20fa87b 100644 --- a/src/hatch-env-manager.ts +++ b/src/hatch-env-manager.ts @@ -3,7 +3,6 @@ import * as fs from 'fs-extra' import { EventEmitter, type IconPath, - type LogOutputChannel, type MarkdownString, ProgressLocation, Uri, @@ -11,6 +10,7 @@ import { } from 'vscode' import { HATCH_ID, HATCH_NAME } from './common/constants' import { type Deferred, createDeferred } from './common/deferred' +import { traceError, traceInfo, traceVerbose } from './common/logging' import { isWindows } from './common/platform' import { ScopeMap } from './common/scope-map' import * as hatch from './hatch-cli' @@ -48,10 +48,7 @@ export class HatchEnvManager implements EnvironmentManager { path2envs: Map> activeEnvs: ScopeMap - constructor( - private readonly api: PythonEnvironmentApi, - readonly log?: LogOutputChannel, - ) { + constructor(private readonly api: PythonEnvironmentApi) { this.path2envs = new Map() this.activeEnvs = new ScopeMap() } @@ -93,7 +90,7 @@ export class HatchEnvManager implements EnvironmentManager { } else { const project = this.api.getPythonProject(scope) if (project) { - this.log?.info('Refreshing project %s', project.uri.fsPath) + traceInfo('Refreshing project %s', project.uri.fsPath) await this.fetchEnvsForProjects([project]) } } @@ -105,16 +102,16 @@ export class HatchEnvManager implements EnvironmentManager { await this.initialize() if (scope === 'global') { - this.log?.debug("getEnvironments called with scope 'global'") + traceVerbose("getEnvironments called with scope 'global'") return [] // TODO: maybe create shims for Hatch-downloadable Pythons? } if (scope === 'all') { - this.log?.debug("getEnvironments called with scope 'all'") + traceVerbose("getEnvironments called with scope 'all'") const allEnvs = Array.from(this.path2envs.values()).flatMap((envs) => Array.from(envs.values()), ) - this.log?.debug('Found %d environments in cache', allEnvs.length) + traceVerbose('Found %d environments in cache', allEnvs.length) return allEnvs } @@ -126,7 +123,7 @@ export class HatchEnvManager implements EnvironmentManager { const cachedEnvs = Array.from(this.path2envs.get(project.uri.fsPath)?.values() ?? []) const uncached = !this.path2envs.has(project.uri.fsPath) if (!uncached) { - this.log?.info('Found %d cached envs', cachedEnvs.length) + traceInfo('Found %d cached envs', cachedEnvs.length) return cachedEnvs } await this.fetchEnvsForProjects([project]) @@ -136,10 +133,10 @@ export class HatchEnvManager implements EnvironmentManager { async set(scope: SetEnvironmentScope, env?: PythonEnvironment): Promise { for (const uri of Array.isArray(scope) ? scope : [scope]) { if (!env) { - this.log?.info('unsetting env for scope %s', uri?.fsPath) + traceInfo('unsetting env for scope %s', uri?.fsPath) this.activeEnvs.delete(uri) } else { - this.log?.info('setting env %s for scope %s', env.displayName, uri?.fsPath) + traceInfo('setting env %s for scope %s', env.displayName, uri?.fsPath) const old = this.activeEnvs.get(uri) this.activeEnvs.set(uri, env) if (old?.envId.id !== env.envId.id) { @@ -174,12 +171,12 @@ export class HatchEnvManager implements EnvironmentManager { try { await hatch.createEnv(env.name, scope) } catch (error) { - this.log?.error('Failed to create env for scope %s: %s', scope?.fsPath, error) + traceError('Failed to create env for scope %s: %s', scope?.fsPath, error) return undefined } } - this.log?.info(`got env ${env?.displayName} for scope ${scope?.fsPath}`) + traceInfo(`got env ${env?.displayName} for scope ${scope?.fsPath}`) return env } //onDidChangeEnvironment: Event | undefined