Skip to content

Commit 094cc94

Browse files
committed
Expand logging coverage across entire extension
- Add leveled logging to all commands, services, utilities, and providers - Add diagnostic detail logging with severity, line number, and message - Fix log flooding from repeated running state checks (only log on change) - Fix feedback loop from Output Channel document events triggering diagnostics - Promote key startup logs from debug to info/warn level - Update CHANGELOG to reflect expanded logging scope and bug fixes
1 parent 6be82ef commit 094cc94

20 files changed

Lines changed: 178 additions & 59 deletions

CHANGELOG.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1111

1212
### Added:
1313

14-
- Logging: Added debug logging to Output panel (`Dev Proxy Toolkit`) to help investigate issues
14+
- Logging: Added leveled logging to Output panel (`Dev Proxy Toolkit`) across the entire extension covering commands, diagnostics, services, and utilities to help investigate issues
1515
- Quick Fixes: Enable local language model fix now adds or updates `languageModel.enabled: true` for supported plugins only
1616

1717
### Fixed:
1818

19+
- Logging: Fixed log flooding from repeated running state checks by only logging on state changes
20+
- Logging: Fixed log feedback loop caused by Output Channel document events triggering diagnostics
21+
1922
- Diagnostics: Language model diagnostic now correctly targets plugins that can use a local language model (OpenAIMockResponsePlugin, OpenApiSpecGeneratorPlugin, TypeSpecGeneratorPlugin) and shows as an informational hint instead of a warning
2023

2124
## [1.12.0] - 2026-01-29

src/code-actions.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import parse from 'json-to-ast';
44
import { getASTNode, getRangeFromASTNode } from './utils';
55
import { pluginSnippets } from './data';
66
import snippetsJson from './snippets/json-snippets.json';
7+
import * as logger from './logger';
78

89
/**
910
* Extract the diagnostic code value from the object format.
@@ -60,6 +61,7 @@ export const registerCodeActions = (context: vscode.ExtensionContext) => {
6061
context.globalState.get<DevProxyInstall>('devProxyInstall');
6162

6263
if (!devProxyInstall) {
64+
logger.debug('Dev Proxy install not found, code actions disabled');
6365
return;
6466
}
6567

@@ -123,8 +125,8 @@ export function extractSchemaFilename(schemaUrl: string): string {
123125
if (match) {
124126
return match[1];
125127
}
126-
} catch {
127-
// Fall through to default
128+
} catch (error) {
129+
logger.warn('Failed to extract schema filename, using default', { schemaUrl, error });
128130
}
129131

130132
return defaultSchema;

src/code-lens.ts

Lines changed: 39 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import * as vscode from 'vscode';
22
import { isConfigFile, getASTNode, getRangeFromASTNode } from './utils';
33
import parse from 'json-to-ast';
44
import { pluginSnippets } from './data';
5+
import * as logger from './logger';
56

67
export const registerCodeLens = (context: vscode.ExtensionContext) => {
78
context.subscriptions.push(
@@ -31,44 +32,48 @@ export const pluginLensProvider: vscode.CodeLensProvider = {
3132
export const createCodeLensForPluginNodes = (document: vscode.TextDocument) => {
3233
const codeLens: vscode.CodeLens[] = [];
3334
if (isConfigFile(document)) {
34-
const documentNode = parse(document.getText()) as parse.ObjectNode;
35-
const pluginsNode = getASTNode(
36-
documentNode.children,
37-
'Identifier',
38-
'plugins'
39-
);
35+
try {
36+
const documentNode = parse(document.getText()) as parse.ObjectNode;
37+
const pluginsNode = getASTNode(
38+
documentNode.children,
39+
'Identifier',
40+
'plugins'
41+
);
4042

41-
if (
42-
pluginsNode &&
43-
(pluginsNode.value as parse.ArrayNode).children.length !== 0
44-
) {
45-
const pluginNodes = (pluginsNode.value as parse.ArrayNode)
46-
.children as parse.ObjectNode[];
43+
if (
44+
pluginsNode &&
45+
(pluginsNode.value as parse.ArrayNode).children.length !== 0
46+
) {
47+
const pluginNodes = (pluginsNode.value as parse.ArrayNode)
48+
.children as parse.ObjectNode[];
4749

48-
pluginNodes.forEach((pluginNode: parse.ObjectNode) => {
49-
const pluginNameNode = getASTNode(
50-
pluginNode.children,
51-
'Identifier',
52-
'name'
53-
);
54-
if (!pluginNameNode) {
55-
return;
56-
}
57-
const pluginName = (pluginNameNode?.value as parse.LiteralNode)
58-
.value as string;
50+
pluginNodes.forEach((pluginNode: parse.ObjectNode) => {
51+
const pluginNameNode = getASTNode(
52+
pluginNode.children,
53+
'Identifier',
54+
'name'
55+
);
56+
if (!pluginNameNode) {
57+
return;
58+
}
59+
const pluginName = (pluginNameNode?.value as parse.LiteralNode)
60+
.value as string;
5961

60-
const isValidName = pluginSnippets[pluginName];
62+
const isValidName = pluginSnippets[pluginName];
6163

62-
if (isValidName) {
63-
codeLens.push(
64-
new vscode.CodeLens(getRangeFromASTNode(pluginNameNode), {
65-
title: `📄 ${pluginName}`,
66-
command: 'dev-proxy-toolkit.openPluginDoc',
67-
arguments: [pluginName],
68-
})
69-
);
70-
}
71-
});
64+
if (isValidName) {
65+
codeLens.push(
66+
new vscode.CodeLens(getRangeFromASTNode(pluginNameNode), {
67+
title: `📄 ${pluginName}`,
68+
command: 'dev-proxy-toolkit.openPluginDoc',
69+
arguments: [pluginName],
70+
})
71+
);
72+
}
73+
});
74+
}
75+
} catch (error) {
76+
logger.warn('Failed to parse config file for code lens generation', { file: document.fileName, error });
7277
}
7378
}
7479

src/commands/config.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Commands } from '../constants';
33
import { executeCommand } from '../utils/shell';
44
import { getDevProxyExe } from '../detect';
55
import { VersionPreference } from '../enums';
6+
import * as logger from '../logger';
67

78
/**
89
* Configuration file commands: open, create new.
@@ -25,6 +26,7 @@ export function registerConfigCommands(
2526
}
2627

2728
async function openConfig(devProxyExe: string): Promise<void> {
29+
logger.debug('Opening Dev Proxy config', { devProxyExe });
2830
await executeCommand(`${devProxyExe} config open`);
2931
}
3032

@@ -36,15 +38,18 @@ async function createNewConfig(devProxyExe: string): Promise<void> {
3638

3739
const workspaceFolder = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath;
3840
if (!workspaceFolder) {
41+
logger.warn('Cannot create config: no workspace folder open');
3942
vscode.window.showErrorMessage('No workspace folder open');
4043
return;
4144
}
45+
logger.info('Creating new config file', { fileName, workspaceFolder });
4246

4347
const devProxyFolder = vscode.Uri.file(`${workspaceFolder}/.devproxy`);
4448
const configUri = vscode.Uri.file(`${workspaceFolder}/.devproxy/${fileName}`);
4549

4650
// Check if file already exists
4751
if (await fileExists(configUri)) {
52+
logger.warn('Config file already exists', { path: configUri.fsPath });
4853
vscode.window.showErrorMessage('A file with that name already exists');
4954
return;
5055
}
@@ -67,9 +72,11 @@ async function createNewConfig(devProxyExe: string): Promise<void> {
6772
);
6873

6974
// Open the newly created file
75+
logger.info('Config file created', { path: configUri.fsPath });
7076
const document = await vscode.workspace.openTextDocument(configUri);
7177
await vscode.window.showTextDocument(document);
7278
} catch (error) {
79+
logger.error('Failed to create new config file', error);
7380
vscode.window.showErrorMessage('Failed to create new config file');
7481
}
7582
}

src/commands/discovery.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Commands } from '../constants';
33
import { TerminalService } from '../services/terminal';
44
import { getDevProxyExe } from '../detect';
55
import { VersionPreference } from '../enums';
6+
import * as logger from '../logger';
67

78
/**
89
* URL discovery command.
@@ -40,10 +41,12 @@ async function discoverUrlsToWatch(
4041

4142
// User cancelled
4243
if (processNames === undefined) {
44+
logger.debug('URL discovery cancelled by user');
4345
return;
4446
}
4547

4648
const command = buildDiscoverCommand(devProxyExe, processNames);
49+
logger.info('Starting URL discovery', { processNames: processNames || '(all processes)', command });
4750
terminalService.sendCommand(terminal, command);
4851
}
4952

src/commands/docs.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Commands } from '../constants';
33
import { pluginDocs } from '../data';
44
import parse from 'json-to-ast';
55
import { getASTNode, getRangeFromASTNode } from '../utils/ast';
6+
import * as logger from '../logger';
67

78
/**
89
* Documentation and language model configuration commands.
@@ -21,8 +22,11 @@ export function registerDocCommands(context: vscode.ExtensionContext): void {
2122
function openPluginDocumentation(pluginName: string): void {
2223
const doc = pluginDocs[pluginName];
2324
if (doc) {
25+
logger.debug('Opening plugin docs', { pluginName, url: doc.url });
2426
const target = vscode.Uri.parse(doc.url);
2527
vscode.env.openExternal(target);
28+
} else {
29+
logger.warn('Plugin docs not found', { pluginName });
2630
}
2731
}
2832

@@ -42,11 +46,13 @@ async function addLanguageModelConfig(uri: vscode.Uri): Promise<void> {
4246
}
4347
} catch (error) {
4448
// Fallback to simple text-based insertion
49+
logger.debug('Failed to parse document with json-to-ast, using text-based fallback', error);
4550
addLanguageModelFallback(document, edit, uri);
4651
}
4752

4853
await vscode.workspace.applyEdit(edit);
4954
await document.save();
55+
logger.info('Language model configuration added');
5056

5157
vscode.window.showInformationMessage('Language model configuration added');
5258
}

src/commands/install.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
openUpgradeDocumentation,
88
} from '../utils/shell';
99
import { PackageManager, VersionPreference } from '../enums';
10+
import * as logger from '../logger';
1011

1112
/**
1213
* Installation and upgrade commands.
@@ -33,6 +34,7 @@ async function installDevProxy(
3334
): Promise<void> {
3435
const message = vscode.window.setStatusBarMessage('Installing Dev Proxy...');
3536
const versionPreference = configuration.get('version') as VersionPreference;
37+
logger.info('Installing Dev Proxy', { platform, versionPreference });
3638

3739
try {
3840
if (platform === 'win32') {
@@ -41,6 +43,7 @@ async function installDevProxy(
4143
await installOnMac(versionPreference);
4244
} else if (platform === 'linux') {
4345
// Linux requires manual installation
46+
logger.info('Linux detected, opening install docs');
4447
vscode.env.openExternal(vscode.Uri.parse(Urls.linuxInstall));
4548
}
4649
} finally {
@@ -55,17 +58,21 @@ async function installOnWindows(versionPreference: VersionPreference): Promise<v
5558
try {
5659
await executeCommand('winget --version');
5760
} catch {
61+
logger.warn('Winget not found on PATH');
5862
vscode.window.showErrorMessage('Winget is not installed. Please install winget and try again.');
5963
return;
6064
}
6165

6266
try {
67+
logger.info('Installing Dev Proxy via winget', { packageId });
6368
await executeCommand(`winget install ${packageId} --silent`);
69+
logger.info('Dev Proxy installed successfully via winget');
6470
const result = await vscode.window.showInformationMessage('Dev Proxy installed.', 'Reload');
6571
if (result === 'Reload') {
6672
await vscode.commands.executeCommand('workbench.action.reloadWindow');
6773
}
6874
} catch (error) {
75+
logger.error('Failed to install Dev Proxy via winget', error);
6976
vscode.window.showErrorMessage(`Failed to install Dev Proxy.\n${error}`);
7077
}
7178
}
@@ -77,18 +84,22 @@ async function installOnMac(versionPreference: VersionPreference): Promise<void>
7784
try {
7885
await executeCommand('brew --version');
7986
} catch {
87+
logger.warn('Homebrew not found on PATH');
8088
vscode.window.showErrorMessage('Homebrew is not installed. Please install brew and try again.');
8189
return;
8290
}
8391

8492
try {
93+
logger.info('Installing Dev Proxy via Homebrew', { packageId });
8594
await executeCommand('brew tap dotnet/dev-proxy');
8695
await executeCommand(`brew install ${packageId}`);
96+
logger.info('Dev Proxy installed successfully via Homebrew');
8797
const result = await vscode.window.showInformationMessage('Dev Proxy installed.', 'Reload');
8898
if (result === 'Reload') {
8999
await vscode.commands.executeCommand('workbench.action.reloadWindow');
90100
}
91101
} catch (error) {
102+
logger.error('Failed to install Dev Proxy via Homebrew', error);
92103
vscode.window.showErrorMessage(`Failed to install Dev Proxy.\n${error}`);
93104
}
94105
}
@@ -97,9 +108,11 @@ async function upgradeDevProxy(configuration: vscode.WorkspaceConfiguration): Pr
97108
const platform = process.platform;
98109
const versionPreference = configuration.get('version') as VersionPreference;
99110
const isBeta = versionPreference === VersionPreference.Beta;
111+
logger.info('Upgrading Dev Proxy', { platform, versionPreference });
100112

101113
// Linux always redirects to documentation
102114
if (platform === 'linux') {
115+
logger.info('Linux detected, opening upgrade docs');
103116
openUpgradeDocumentation();
104117
return;
105118
}
@@ -119,6 +132,7 @@ async function upgradeDevProxy(configuration: vscode.WorkspaceConfiguration): Pr
119132
isBeta
120133
);
121134
if (!upgraded) {
135+
logger.warn('Upgrade via winget failed, opening upgrade docs');
122136
openUpgradeDocumentation();
123137
}
124138
return;
@@ -139,11 +153,13 @@ async function upgradeDevProxy(configuration: vscode.WorkspaceConfiguration): Pr
139153
isBeta
140154
);
141155
if (!upgraded) {
156+
logger.warn('Upgrade via Homebrew failed, opening upgrade docs');
142157
openUpgradeDocumentation();
143158
}
144159
return;
145160
}
146161

147162
// Unknown platform
163+
logger.warn('Unknown platform, opening upgrade docs', { platform });
148164
openUpgradeDocumentation();
149165
}

src/commands/jwt.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Commands } from '../constants';
33
import { executeCommand } from '../utils/shell';
44
import { getDevProxyExe } from '../detect';
55
import { VersionPreference } from '../enums';
6+
import * as logger from '../logger';
67

78
/**
89
* JWT (JSON Web Token) generation commands.
@@ -36,8 +37,10 @@ interface JwtParams {
3637
async function createJwt(devProxyExe: string): Promise<void> {
3738
const params = await collectJwtParams();
3839
if (!params) {
40+
logger.debug('JWT creation cancelled by user');
3941
return; // User cancelled
4042
}
43+
logger.info('Generating JWT', { name: params.name, issuer: params.issuer, audiences: params.audiences.length, roles: params.roles.length, scopes: params.scopes.length, validFor: params.validFor });
4144

4245
await vscode.window.withProgress(
4346
{
@@ -50,8 +53,10 @@ async function createJwt(devProxyExe: string): Promise<void> {
5053
const command = buildJwtCommand(devProxyExe, params);
5154
const result = await executeCommand(command);
5255
const token = extractToken(result);
56+
logger.info('JWT token generated successfully');
5357
await presentToken(token, command);
5458
} catch (error) {
59+
logger.error('Failed to generate JWT token', error);
5560
vscode.window.showErrorMessage(`Failed to generate JWT token: ${error}`);
5661
}
5762
}

src/commands/proxy.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,12 @@ async function stopDevProxy(
6262

6363
const closeTerminal = configuration.get<boolean>('closeTerminal', true);
6464
if (closeTerminal) {
65+
logger.debug('Waiting for Dev Proxy to stop before closing terminal');
6566
await waitForProxyToStop(apiClient);
6667

6768
const terminalService = TerminalService.fromConfiguration();
6869
terminalService.disposeDevProxyTerminals();
70+
logger.debug('Dev Proxy terminals disposed');
6971
}
7072
}
7173

@@ -88,7 +90,9 @@ async function restartDevProxy(
8890
: devProxyExe;
8991

9092
terminalService.sendCommand(terminal, command);
91-
} catch {
93+
logger.debug('Dev Proxy restart command sent');
94+
} catch (error) {
95+
logger.error('Failed to restart Dev Proxy', error);
9296
vscode.window.showErrorMessage('Failed to restart Dev Proxy');
9397
}
9498
}

0 commit comments

Comments
 (0)