Skip to content

Commit 09c8cb4

Browse files
authored
Merge pull request #14 from nventive/refactor--Replace-console.log
refactor: Enhance logging
2 parents 01c15eb + 1bce016 commit 09c8cb4

File tree

6 files changed

+89
-35
lines changed

6 files changed

+89
-35
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22

33
All notable changes to the "promptitude" extension will be documented in this file.
44

5+
## vNext
6+
7+
### Changed
8+
9+
- Enhanced logging and removed console.logs
10+
511
## [1.4.0] - 2025-10-03
612

713
### Added

src/extension.ts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,31 @@ import { ConfigManager } from './configManager';
55
import { Logger } from './utils/logger';
66
import { AzureDevOpsApiManager } from './utils/azureDevOps';
77

8+
89
let syncManager: SyncManager;
910
let statusBarManager: StatusBarManager;
1011
let logger: Logger;
1112

1213
export function activate(context: vscode.ExtensionContext) {
13-
logger = new Logger();
14+
logger = Logger.get('Extension');
1415
logger.info('Promptitude Extension is activating...');
1516

17+
// If debugging and a debugger is attached, auto-select the Output channel without stealing focus
18+
try {
19+
// Dynamically import inspector to avoid bundling issues; optional behavior
20+
const inspector = require('inspector');
21+
const debuggerAttached = !!inspector.url();
22+
if (debuggerAttached) {
23+
// Show output channel but preserve focus (don't steal keyboard focus)
24+
logger.show(true);
25+
}
26+
} catch {
27+
// Ignore if inspector isn't available
28+
}
29+
1630
const configManager = new ConfigManager();
1731
statusBarManager = new StatusBarManager();
18-
syncManager = new SyncManager(configManager, statusBarManager, logger);
32+
syncManager = new SyncManager(configManager, statusBarManager);
1933

2034
// Register commands
2135
const syncNowCommand = vscode.commands.registerCommand('promptitude.syncNow', async () => {
@@ -157,4 +171,5 @@ export function deactivate() {
157171
logger?.info('Promptitude Extension is deactivating...');
158172
syncManager?.dispose();
159173
statusBarManager?.dispose();
174+
Logger.disposeSharedChannel();
160175
}

src/syncManager.ts

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ export interface MultiRepositorySyncResult {
3030

3131
export class SyncManager {
3232
private timer: NodeJS.Timeout | null = null;
33-
private isInitialized = false;
3433
private context: vscode.ExtensionContext | null = null;
3534
private notifications: NotificationManager;
3635
private fileSystem: FileSystemManager;
@@ -39,7 +38,7 @@ export class SyncManager {
3938
constructor(
4039
private config: ConfigManager,
4140
private statusBar: StatusBarManager,
42-
private logger: Logger
41+
private logger: Logger = Logger.get('SyncManager')
4342
) {
4443
this.notifications = new NotificationManager(this.config, undefined, this.logger);
4544
this.fileSystem = new FileSystemManager();
@@ -69,7 +68,6 @@ export class SyncManager {
6968

7069
// Schedule periodic syncs
7170
this.scheduleNextSync();
72-
this.isInitialized = true;
7371

7472
this.logger.info('SyncManager initialized successfully');
7573
}
@@ -161,13 +159,14 @@ export class SyncManager {
161159
// Support more file extensions including .prompt.md
162160
const isRelevantFile = item.path.endsWith('.md') ||
163161
item.path.endsWith('.txt');
164-
165-
this.logger.debug(` ${item.path}: blob=${isBlob}, matchesPath=${matchesPath}, isRelevantFile=${isRelevantFile} (normalized: ${normalizedPath})`);
166-
162+
if(isRelevantFile) {
163+
this.logger.debug(` ${item.path}: blob=${isBlob}, matchesPath=${matchesPath}, (normalized: ${normalizedPath})`);
164+
}
165+
167166
return isBlob && matchesPath && isRelevantFile;
168167
});
169168

170-
console.log(`Filtered result: ${filtered.length} files out of ${tree.length} total`);
169+
this.logger.debug(`Filtered result: ${filtered.length} files out of ${tree.length} total`);
171170
return filtered;
172171
}
173172

@@ -267,11 +266,6 @@ export class SyncManager {
267266
const tree = await gitApi.getRepositoryTree(owner, repo, branch);
268267
this.logger.debug(`Retrieved repository tree with ${tree.tree.length} items for ${repoUrl}`);
269268

270-
// Debug: Log all items in the tree to see what's actually there
271-
console.log('Repository tree contents:');
272-
tree.tree.forEach((item, index) => {
273-
console.log(` ${index + 1}. ${item.path} (type: ${item.type})`);
274-
});
275269

276270
// Filter relevant files
277271
const relevantFiles = this.filterRelevantFiles(tree.tree);
@@ -509,7 +503,6 @@ export class SyncManager {
509503
this.timer = null;
510504
}
511505

512-
this.isInitialized = false;
513506
this.logger.info('SyncManager disposed');
514507
}
515508
}

src/utils/azureDevOps.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,14 @@ export class AzureDevOpsApiManager implements GitApiManager {
1111
private static readonly cacheStorageKey = 'promptitude.azureDevOps.patCache';
1212
private static readonly minPatLength = 8; // Reduced from 20 to be more permissive
1313
private extensionContext: vscode.ExtensionContext;
14+
private logger: Logger;
1415

15-
constructor(context: vscode.ExtensionContext) {
16+
constructor(context: vscode.ExtensionContext, logger: Logger = Logger.get('AzureDevOpsApi')) {
1617
if (!context) {
1718
throw new Error('Extension context is required for Azure DevOps authentication');
1819
}
1920
this.extensionContext = context;
21+
this.logger = logger;
2022
}
2123

2224
getProviderName(): string {
@@ -258,24 +260,24 @@ export class AzureDevOpsApiManager implements GitApiManager {
258260
const headers = this.buildAuthHeaders(pats[cachedIndex]);
259261

260262
if (await this.validateHeaders(headers, organization)) {
261-
console.log(`[Promptitude] Cache hit: PAT validated for '${organization}'`);
263+
this.logger.debug(`Cache hit: PAT validated for '${organization}'`);
262264
return headers;
263265
}
264266

265267
// Cache was stale, remove this entry
266-
console.log(`[Promptitude] Cache miss: cached PAT no longer valid for '${organization}'`);
268+
this.logger.debug(`Cache miss: cached PAT no longer valid for '${organization}'`);
267269
delete cache[organization];
268270
await this.saveCache(cache);
269271
}
270272

271273
// Try all PATs
272-
console.log(`[Promptitude] Trying all PATs (${pats.length} total) for organization '${organization}'`);
274+
this.logger.debug(`Trying all PATs (${pats.length} total) for organization '${organization}'`);
273275
for (let i = 0; i < pats.length; i++) {
274-
console.log(`[Promptitude] Attempting PAT ${i + 1}/${pats.length} for organization '${organization}'`);
276+
this.logger.debug(`Attempting PAT ${i + 1}/${pats.length} for organization '${organization}'`);
275277
const headers = this.buildAuthHeaders(pats[i]);
276278

277279
if (await this.validateHeaders(headers, organization)) {
278-
console.log(`[Promptitude] Success: a PAT successfully validated for '${organization}', updating cache`);
280+
this.logger.debug(`Success: a PAT successfully validated for '${organization}', updating cache`);
279281
// Cache successful PAT
280282
cache[organization] = i;
281283
await this.saveCache(cache);
@@ -320,7 +322,7 @@ export class AzureDevOpsApiManager implements GitApiManager {
320322

321323
return legacyResponse.ok;
322324
} catch (error) {
323-
console.log(`[Promptitude] Validation failed for organization '${organization}': ${error}`);
325+
this.logger.debug(`Validation failed for organization '${organization}': ${error}`);
324326
return false;
325327
}
326328
}

src/utils/github.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
11
import * as vscode from 'vscode';
22
import { GitApiManager, GitTree, RepositoryInfo } from './gitProvider';
3+
import { Logger } from './logger';
34

45
export class GitHubApiManager implements GitApiManager {
56
private baseUrl = 'https://api.github.com';
7+
private logger: Logger;
8+
9+
constructor(logger: Logger = Logger.get('GitHubApi')) {
10+
this.logger = logger;
11+
}
612

713
getProviderName(): string {
814
return 'github';
@@ -51,7 +57,7 @@ export class GitHubApiManager implements GitApiManager {
5157
if (!session) {
5258
throw new Error('GitHub authentication required');
5359
}
54-
console.log(`Fetching file content from ${owner}/${repo}/${path} on branch ${branch}`);
60+
this.logger.debug(`Fetching file content from ${owner}/${repo}/${path} on branch ${branch}`);
5561
const response = await fetch(`${this.baseUrl}/repos/${owner}/${repo}/contents/${path}?ref=${branch}`, {
5662
headers: {
5763
['Authorization']: `Bearer ${session.accessToken}`,

src/utils/logger.ts

Lines changed: 44 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,54 @@
11
import * as vscode from 'vscode';
22

33
export class Logger {
4-
private outputChannel: vscode.OutputChannel;
4+
// Use a single shared OutputChannel across all Logger instances
5+
private static sharedChannel: vscode.OutputChannel | undefined;
6+
private static scopedCache: Map<string, Logger> = new Map();
7+
private scope?: string;
8+
private get outputChannel(): vscode.OutputChannel {
9+
if (!Logger.sharedChannel) {
10+
Logger.sharedChannel = vscode.window.createOutputChannel('Promptitude');
11+
}
12+
return Logger.sharedChannel;
13+
}
14+
15+
constructor(scope?: string) {
16+
this.scope = scope;
17+
}
518

6-
constructor() {
7-
this.outputChannel = vscode.window.createOutputChannel('Promptitude');
19+
static get(scope: string): Logger {
20+
const existing = Logger.scopedCache.get(scope);
21+
if (existing) {
22+
return existing;
23+
}
24+
const logger = new Logger(scope);
25+
Logger.scopedCache.set(scope, logger);
26+
return logger;
827
}
928

1029
private get isDebugEnabled(): boolean {
1130
return vscode.workspace.getConfiguration('promptitude').get('debug', false);
1231
}
1332

1433
private log(level: string, message: string): void {
15-
const timestamp = new Date().toISOString();
16-
const logMessage = `[${timestamp}] [${level}] ${message}`;
34+
const timestamp = this.formatTimestamp(new Date());
35+
const scoped = this.scope ? `[${this.scope}] ${message}` : message;
36+
const logMessage = `[${timestamp}] [${level}] ${scoped}`;
1737

1838
this.outputChannel.appendLine(logMessage);
19-
20-
// Also log to console for debugging
21-
console.log(`Promptitude: ${logMessage}`);
39+
}
40+
41+
private formatTimestamp(date: Date): string {
42+
const pad = (n: number, width: number = 2) => n.toString().padStart(width, '0');
43+
const year = date.getFullYear();
44+
const month = pad(date.getMonth() + 1);
45+
const day = pad(date.getDate());
46+
const hours = pad(date.getHours());
47+
const minutes = pad(date.getMinutes());
48+
const seconds = pad(date.getSeconds());
49+
const millis = pad(date.getMilliseconds(), 3);
50+
// Format: 2025-10-08 01:16:21.132 (local time)
51+
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}.${millis}`;
2252
}
2353

2454
debug(message: string): void {
@@ -46,11 +76,13 @@ export class Logger {
4676
this.log('ERROR', errorMessage);
4777
}
4878

49-
show(): void {
50-
this.outputChannel.show();
79+
show(preserveFocus: boolean = false): void {
80+
this.outputChannel.show(preserveFocus);
5181
}
5282

53-
dispose(): void {
54-
this.outputChannel.dispose();
83+
static disposeSharedChannel(): void {
84+
Logger.sharedChannel?.dispose();
85+
Logger.sharedChannel = undefined;
86+
Logger.scopedCache.clear();
5587
}
5688
}

0 commit comments

Comments
 (0)