diff --git a/.vscode/launch.json b/.vscode/launch.json index ed76356799c..384e8a86605 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -141,9 +141,13 @@ // Set the telemetry key environment variable to use if you dont want to set it in package.json //"TYPESPEC_VSCODE_TELEMETRY_KEY": "{The instrumentation key of your Application Insights}", - //"ENABLE_SERVER_COMPILE_LOGGING": "true", - //"ENABLE_UPDATE_MANAGER_LOGGING": "true", - //"ENABLE_LM_LOGGING": "true", + // Enable debug logging for specific areas using DEBUG environment variable + // Examples: + // "DEBUG": "typespec:server_compile" - Enable server compilation debug logs + // "DEBUG": "typespec:lm" - Enable Language Model debug logs + // "DEBUG": "typespec:*" - Enable all typespec debug logs + // "DEBUG": "typespec:server_compile,typespec:compile_config" - Enable multiple areas + //"DEBUG": "typespec:server_compile,typespec:update_manager,typespec:compile_config,typespec:lm", "TYPESPEC_SERVER_NODE_OPTIONS": "--nolazy --inspect-brk=4242", "TYPESPEC_DEVELOPMENT_MODE": "true" diff --git a/packages/compiler/package.json b/packages/compiler/package.json index bd834faa5fa..cf791847742 100644 --- a/packages/compiler/package.json +++ b/packages/compiler/package.json @@ -109,6 +109,7 @@ "@inquirer/prompts": "^8.0.1", "ajv": "~8.17.1", "change-case": "~5.4.4", + "debug": "~4.4.0", "env-paths": "^3.0.0", "globby": "~16.0.0", "is-unicode-supported": "^2.1.0", @@ -125,6 +126,7 @@ }, "devDependencies": { "@types/babel__code-frame": "~7.0.6", + "@types/debug": "~4.1.12", "@types/mustache": "~4.2.5", "@types/node": "~25.0.2", "@types/semver": "^7.5.8", diff --git a/packages/compiler/src/server/compile-service.ts b/packages/compiler/src/server/compile-service.ts index 2c41249149e..8db4db2da72 100644 --- a/packages/compiler/src/server/compile-service.ts +++ b/packages/compiler/src/server/compile-service.ts @@ -19,7 +19,7 @@ import { deepClone, distinctArray } from "../utils/misc.js"; import { getLocationInYamlScript } from "../yaml/diagnostics.js"; import { parseYaml } from "../yaml/parser.js"; import { ClientConfigProvider } from "./client-config-provider.js"; -import { serverOptions } from "./constants.js"; +import { debugLoggers, serverOptions } from "./constants.js"; import { resolveEntrypointFile } from "./entrypoint-resolver.js"; import { FileService } from "./file-service.js"; import { FileSystemCache } from "./file-system-cache.js"; @@ -90,6 +90,8 @@ export function createCompileService({ const eventListeners = new Map void | Promise>(); const compileManager = new ServerCompileManager(updateManager, compilerHost, log); let configFilePath: string | undefined; + const debug = debugLoggers.compileConfig; + const logDebug = debug.enabled ? log : () => {}; return { compile, getScript, on, notifyChange, getMainFileForDocument }; @@ -129,7 +131,7 @@ export function createCompileService({ } const mainFile = await getMainFileForDocument(path); if (mainFile === undefined) { - log({ level: "debug", message: `failed to resolve main file for ${path}` }); + logDebug({ level: "debug", message: `failed to resolve main file for ${path}` }); return undefined; } if (!mainFile.endsWith(".tsp")) { @@ -137,7 +139,7 @@ export function createCompileService({ } const config = await getConfig(mainFile); configFilePath = config.filename; - log({ level: "debug", message: `config resolved`, detail: config }); + logDebug({ level: "debug", message: `config resolved`, detail: config }); const [optionsFromConfig, _] = resolveOptionsFromConfig(config, { cwd: getDirectoryPath(path), }); @@ -217,7 +219,7 @@ export function createCompileService({ ) { // If the file that changed wasn't imported by anything from the main // file, retry using the file itself as the main file. - log({ + logDebug({ level: "debug", message: `target file was not included in compiling, try to compile ${path} as main file directly`, }); @@ -246,7 +248,7 @@ export function createCompileService({ const [yamlScript] = parseYaml(await serverHost.compilerHost.readFile(configFilePath)); const target = getLocationInYamlScript(yamlScript, ["emit", emitterName], "key"); if (target.pos === 0) { - log({ + logDebug({ level: "debug", message: `Unexpected situation, can't find emitter '${emitterName}' in config file '${configFilePath}'`, }); @@ -286,7 +288,7 @@ export function createCompileService({ const lookupDir = entrypointStat.isDirectory() ? mainFile : getDirectoryPath(mainFile); const configPath = await findTypeSpecConfigPath(compilerHost, lookupDir, true); if (!configPath) { - log({ + logDebug({ level: "debug", message: `can't find path with config file, try to use default config`, }); @@ -337,7 +339,7 @@ export function createCompileService({ */ async function getMainFileForDocument(path: string) { if (path.startsWith("untitled:")) { - log({ level: "debug", message: `untitled document treated as its own main file: ${path}` }); + logDebug({ level: "debug", message: `untitled document treated as its own main file: ${path}` }); return path; } diff --git a/packages/compiler/src/server/constants.ts b/packages/compiler/src/server/constants.ts index 1bb0333bd04..70e99a7b2a8 100644 --- a/packages/compiler/src/server/constants.ts +++ b/packages/compiler/src/server/constants.ts @@ -1,4 +1,5 @@ import { CompilerOptions } from "../core/options.js"; +import createDebug from "debug"; export const serverOptions: CompilerOptions = { dryRun: true, @@ -14,7 +15,16 @@ export const Commands = { }; /** - * Environment variables to enable some logging when needed + * Debug loggers for different areas. Can be enabled via DEBUG environment variable. + * Usage: DEBUG=typespec:server_compile,typespec:compile_config + * + * Examples: + * DEBUG=typespec:server_compile - Enable server compilation debug logs + * DEBUG=typespec:* - Enable all typespec debug logs + * DEBUG=typespec:server_compile,typespec:compile_config - Enable multiple areas */ -export const ENABLE_SERVER_COMPILE_LOGGING = "ENABLE_SERVER_COMPILE_LOGGING"; -export const ENABLE_UPDATE_MANAGER_LOGGING = "ENABLE_UPDATE_MANAGER_LOGGING"; +export const debugLoggers = { + serverCompile: createDebug("typespec:server_compile"), + updateManager: createDebug("typespec:update_manager"), + compileConfig: createDebug("typespec:compile_config"), +} as const; diff --git a/packages/compiler/src/server/entrypoint-resolver.ts b/packages/compiler/src/server/entrypoint-resolver.ts index 4865b44d4c6..0885021f3e2 100644 --- a/packages/compiler/src/server/entrypoint-resolver.ts +++ b/packages/compiler/src/server/entrypoint-resolver.ts @@ -3,6 +3,7 @@ import { getDirectoryPath, joinPaths } from "../core/path-utils.js"; import { SystemHost, Diagnostic as TypeSpecDiagnostic } from "../core/types.js"; import { doIO, loadFile } from "../utils/io.js"; import { resolveTspMain } from "../utils/misc.js"; +import { debugLoggers } from "./constants.js"; import { FileSystemCache } from "./file-system-cache.js"; import { ServerLog } from "./types.js"; @@ -14,6 +15,8 @@ export async function resolveEntrypointFile( log: (log: ServerLog) => void, ): Promise { const options = { allowFileNotFound: true }; + const debug = debugLoggers.compileConfig; + const logDebug = debug.enabled ? log : () => {}; const pathStat = await doIO(() => host.stat(path), path, logMainFileSearchDiagnostic, options); const isFilePath = pathStat?.isFile() ?? false; @@ -36,14 +39,14 @@ export async function resolveEntrypointFile( const tspMain = resolveTspMain(pkg); if (typeof tspMain === "string") { - log({ + logDebug({ level: "debug", message: `tspMain resolved from package.json (${pkgPath}) as ${tspMain}`, }); const packageJsonEntrypoint = await existingFile(dir, tspMain); if (packageJsonEntrypoint) { - log({ level: "debug", message: `entrypoint file found as ${packageJsonEntrypoint}` }); + logDebug({ level: "debug", message: `entrypoint file found as ${packageJsonEntrypoint}` }); return packageJsonEntrypoint; } } @@ -51,7 +54,7 @@ export async function resolveEntrypointFile( for (const entrypoint of entrypoints) { const candidate = await existingFile(dir, entrypoint); if (candidate) { - log({ + logDebug({ level: "debug", message: `main file found using client provided entrypoint: ${candidate}`, }); @@ -67,7 +70,7 @@ export async function resolveEntrypointFile( dir = parentDir; } - log({ level: "debug", message: `reached directory root, using '${path}' as main file` }); + logDebug({ level: "debug", message: `reached directory root, using '${path}' as main file` }); return isFilePath ? path : undefined; function logMainFileSearchDiagnostic(diagnostic: TypeSpecDiagnostic) { diff --git a/packages/compiler/src/server/server-compile-manager.ts b/packages/compiler/src/server/server-compile-manager.ts index 552c0551893..d600c4825d6 100644 --- a/packages/compiler/src/server/server-compile-manager.ts +++ b/packages/compiler/src/server/server-compile-manager.ts @@ -9,7 +9,7 @@ import { ServerLog, } from "../index.js"; import { getEnvironmentVariable } from "../utils/misc.js"; -import { ENABLE_SERVER_COMPILE_LOGGING } from "./constants.js"; +import { debugLoggers } from "./constants.js"; import { trackActionFunc } from "./server-track-action-task.js"; import { UpdateManager } from "./update-manager.js"; @@ -45,10 +45,10 @@ export class ServerCompileManager { private compilerHost: CompilerHost, private log: (log: ServerLog) => void, ) { - this.logDebug = - getEnvironmentVariable(ENABLE_SERVER_COMPILE_LOGGING)?.toLowerCase() === "true" - ? (msg) => this.log({ level: "debug", message: msg }) - : () => {}; + const debug = debugLoggers.serverCompile; + this.logDebug = debug.enabled + ? (msg) => this.log({ level: "debug", message: msg }) + : () => {}; } async compile( diff --git a/packages/compiler/src/server/serverlib.ts b/packages/compiler/src/server/serverlib.ts index 50ad2b12057..ea6ee86823b 100644 --- a/packages/compiler/src/server/serverlib.ts +++ b/packages/compiler/src/server/serverlib.ts @@ -104,7 +104,7 @@ import { getSemanticTokens } from "./classify.js"; import { ClientConfigProvider } from "./client-config-provider.js"; import { createCompileService } from "./compile-service.js"; import { resolveCompletion } from "./completion.js"; -import { Commands } from "./constants.js"; +import { Commands, debugLoggers } from "./constants.js"; import { convertDiagnosticToLsp } from "./diagnostics.js"; import { createFileService } from "./file-service.js"; import { createFileSystemCache } from "./file-system-cache.js"; @@ -1138,7 +1138,7 @@ export function createServer( compilerHost, emitterProvider, linterProvider, - log, + log: logCompileConfig, }); return CompletionList.create(items); } @@ -1412,6 +1412,12 @@ export function createServer( host.log(log); } + function logCompileConfig(logMessage: ServerLog) { + if (debugLoggers.compileConfig.enabled) { + log(logMessage); + } + } + function sendDiagnostics(document: TextDocument, diagnostics: VSDiagnostic[]) { host.sendDiagnostics({ uri: document.uri, diff --git a/packages/compiler/src/server/update-manager.ts b/packages/compiler/src/server/update-manager.ts index 0d650dbc359..d3e1aac41e7 100644 --- a/packages/compiler/src/server/update-manager.ts +++ b/packages/compiler/src/server/update-manager.ts @@ -1,7 +1,7 @@ import { TextDocumentIdentifier } from "vscode-languageserver"; import { TextDocument } from "vscode-languageserver-textdocument"; import { getEnvironmentVariable } from "../utils/misc.js"; -import { ENABLE_UPDATE_MANAGER_LOGGING } from "./constants.js"; +import { debugLoggers } from "./constants.js"; import { ServerLog } from "./types.js"; interface PendingUpdate { @@ -43,12 +43,12 @@ export class UpdateManager { log: (sl: ServerLog) => void, getDebounceDelay?: () => number, ) { - this._log = - getEnvironmentVariable(ENABLE_UPDATE_MANAGER_LOGGING)?.toLowerCase() === "true" - ? (sl: ServerLog) => { - log({ ...sl, message: `#FromUpdateManager(${this.name}): ${sl.message}` }); - } - : () => {}; + const debug = debugLoggers.updateManager; + this._log = debug.enabled + ? (sl: ServerLog) => { + log({ ...sl, message: `#FromUpdateManager(${this.name}): ${sl.message}` }); + } + : () => {}; // Set the debounce delay function once during construction this.getDebounceDelay = getDebounceDelay ?? this.getAdaptiveDebounceDelay; diff --git a/packages/typespec-vscode/package.json b/packages/typespec-vscode/package.json index 7fb914d9b4f..f31f8da8b3a 100644 --- a/packages/typespec-vscode/package.json +++ b/packages/typespec-vscode/package.json @@ -309,6 +309,10 @@ "vitest": "^4.0.15", "vscode-languageclient": "~9.0.1", "which": "^6.0.0", - "yaml": "~2.8.2" + "yaml": "~2.8.2", + "@types/debug": "~4.1.12" + }, + "dependencies": { + "debug": "~4.4.0" } } diff --git a/packages/typespec-vscode/src/const.ts b/packages/typespec-vscode/src/const.ts index 39df7d9279c..e0726239f8e 100644 --- a/packages/typespec-vscode/src/const.ts +++ b/packages/typespec-vscode/src/const.ts @@ -1,5 +1,14 @@ +import createDebug from "debug"; + export const StartFileName = "main.tsp"; export const TspConfigFileName = "tspconfig.yaml"; export const EmptyGuid = "00000000-0000-0000-0000-000000000000"; -export const ENABLE_LM_LOGGING = "ENABLE_LM_LOGGING"; +/** + * Debug logger for Language Model operations. + * Can be enabled via DEBUG environment variable. + * Usage: DEBUG=typespec:lm + */ +export const debugLoggers = { + lm: createDebug("typespec:lm"), +} as const; diff --git a/packages/typespec-vscode/src/lm/language-model.ts b/packages/typespec-vscode/src/lm/language-model.ts index 3dfb8d581d6..064358199f3 100644 --- a/packages/typespec-vscode/src/lm/language-model.ts +++ b/packages/typespec-vscode/src/lm/language-model.ts @@ -1,6 +1,6 @@ import { inspect } from "util"; import { LanguageModelChat, LanguageModelChatMessage, lm } from "vscode"; -import { ENABLE_LM_LOGGING } from "../const"; +import { debugLoggers } from "../const"; import logger, { LogItem } from "../log/logger"; import { RetryResult, runWithRetry, runWithTimingLog } from "../utils"; @@ -23,7 +23,8 @@ export async function sendLmChatRequest( /** Only for logging purpose */ id?: string, ): Promise { - const logEnabled = process.env[ENABLE_LM_LOGGING] === "true"; + const debug = debugLoggers.lm; + const logEnabled = debug.enabled; const lmLog = (item: LogItem) => { if (logEnabled || item.level === "error" || item.level === "warning") { logger.log( diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8af70bccd41..eaa178f7ede 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -314,6 +314,9 @@ importers: change-case: specifier: ~5.4.4 version: 5.4.4 + debug: + specifier: ~4.4.0 + version: 4.4.3(supports-color@8.1.1) env-paths: specifier: ^3.0.0 version: 3.0.0 @@ -357,6 +360,9 @@ importers: '@types/babel__code-frame': specifier: ~7.0.6 version: 7.0.6 + '@types/debug': + specifier: ~4.1.12 + version: 4.1.12 '@types/mustache': specifier: ~4.2.5 version: 4.2.6 @@ -2355,10 +2361,17 @@ importers: version: link:../typespec-vscode packages/typespec-vscode: + dependencies: + debug: + specifier: ~4.4.0 + version: 4.4.3(supports-color@8.1.1) devDependencies: '@types/cross-spawn': specifier: ~6.0.6 version: 6.0.6 + '@types/debug': + specifier: ~4.1.12 + version: 4.1.12 '@types/mocha': specifier: ^10.0.9 version: 10.0.10