diff --git a/src/commands.ts b/src/commands.ts index 5c05542..c84a00e 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -1,6 +1,7 @@ import * as vscode from 'vscode'; import { generateCommitMsg } from './generate-commit-msg'; import { ConfigurationManager } from './config'; +import { Logger } from './logger'; /** * Manages the registration and disposal of commands. @@ -55,8 +56,10 @@ export class CommandManager { private registerCommand(command: string, handler: (...args: any[]) => any) { const disposable = vscode.commands.registerCommand(command, async (...args) => { try { + Logger.info(`Executing command: ${command}`); await handler(...args); } catch (error) { + Logger.error(`Command '${command}' failed:`, error); const result = await vscode.window.showErrorMessage( `Failed: ${error.message}`, 'Retry', diff --git a/src/config.ts b/src/config.ts index df0194a..be7abf8 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,6 +1,7 @@ import * as vscode from 'vscode'; import { createOpenAIApi } from './openai-utils'; import { createGeminiAPIClient } from './gemini-utils'; +import { Logger } from './logger'; /** * Configuration keys used in the AI commit extension. @@ -91,7 +92,7 @@ export class ConfigurationManager { await config.update('OPENAI_MODEL', 'gpt-4', vscode.ConfigurationTarget.Global); } } catch (error) { - console.error('Failed to fetch OpenAI models:', error); + Logger.error('Failed to fetch OpenAI models:', error); } } diff --git a/src/extension.ts b/src/extension.ts index 8fcb66b..9911536 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,6 +1,7 @@ import * as vscode from 'vscode'; import { CommandManager } from './commands'; import { ConfigurationManager } from './config'; +import { Logger } from './logger'; /** * Activates the extension and registers commands. @@ -9,6 +10,9 @@ import { ConfigurationManager } from './config'; */ export async function activate(context: vscode.ExtensionContext) { try { + Logger.initialize(); + Logger.info('Activating AI Commit extension...'); + const configManager = ConfigurationManager.getInstance(context); const commandManager = new CommandManager(context); @@ -18,9 +22,12 @@ export async function activate(context: vscode.ExtensionContext) { dispose: () => { configManager.dispose(); commandManager.dispose(); + Logger.dispose(); } }); + Logger.info('AI Commit extension activated successfully'); + const apiKey = configManager.getConfig('OPENAI_API_KEY'); if (!apiKey) { const result = await vscode.window.showWarningMessage( @@ -37,7 +44,7 @@ export async function activate(context: vscode.ExtensionContext) { } } } catch (error) { - console.error('Failed to activate extension:', error); + Logger.error('Failed to activate extension:', error); throw error; } } diff --git a/src/gemini-utils.ts b/src/gemini-utils.ts index 4118d46..7d3ee67 100644 --- a/src/gemini-utils.ts +++ b/src/gemini-utils.ts @@ -1,5 +1,6 @@ import { GoogleGenerativeAI } from "@google/generative-ai"; import { ConfigKeys, ConfigurationManager } from './config'; +import { Logger } from './logger'; /** * Creates and returns a Gemini API configuration object. @@ -58,7 +59,7 @@ export async function GeminiAPI(messages: any[]) { return text; } catch (error) { - console.error('Gemini API call failed:', error); + Logger.error('Gemini API call failed:', error); throw error; } } \ No newline at end of file diff --git a/src/generate-commit-msg.ts b/src/generate-commit-msg.ts index 397ea36..51f069b 100644 --- a/src/generate-commit-msg.ts +++ b/src/generate-commit-msg.ts @@ -7,6 +7,7 @@ import { ChatGPTAPI } from './openai-utils'; import { getMainCommitPrompt } from './prompts'; import { ProgressHandler } from './utils'; import { GeminiAPI } from './gemini-utils'; +import { Logger } from './logger'; /** * Generates a chat completion prompt for the commit message based on the provided diff. @@ -74,6 +75,7 @@ export async function generateCommitMsg(arg) { const repo = await getRepo(arg); const aiProvider = configManager.getConfig(ConfigKeys.AI_PROVIDER, 'openai'); + Logger.info(`Using AI provider: ${aiProvider}`); progress.report({ message: 'Getting staged changes...' }); const { diff, error } = await getDiffStaged(repo); @@ -127,27 +129,33 @@ export async function generateCommitMsg(arg) { if (commitMessage) { + Logger.info('Commit message generated successfully'); scmInputBox.value = commitMessage; } else { throw new Error('Failed to generate commit message'); } } catch (err) { - let errorMessage = 'An unexpected error occurred'; - - if (aiProvider === 'openai' && err.response?.status) { - switch (err.response.status) { - case 401: - errorMessage = 'Invalid OpenAI API key or unauthorized access'; - break; - case 429: - errorMessage = 'Rate limit exceeded. Please try again later'; - break; - case 500: - errorMessage = 'OpenAI server error. Please try again later'; - break; - case 503: - errorMessage = 'OpenAI service is temporarily unavailable'; - break; + Logger.error(`${aiProvider} API call failed:`, err); + + let errorMessage = err.message || 'An unexpected error occurred'; + + if (aiProvider === 'openai') { + const status = err.response?.status || err.status; + if (status) { + switch (status) { + case 401: + errorMessage = 'Invalid OpenAI API key or unauthorized access'; + break; + case 429: + errorMessage = 'Rate limit exceeded. Please try again later'; + break; + case 500: + errorMessage = 'OpenAI server error. Please try again later'; + break; + case 503: + errorMessage = 'OpenAI service is temporarily unavailable'; + break; + } } } else if (aiProvider === 'gemini') { errorMessage = `Gemini API error: ${err.message}`; @@ -156,6 +164,7 @@ export async function generateCommitMsg(arg) { throw new Error(errorMessage); } } catch (error) { + Logger.error('Failed to generate commit message:', error); throw error; } }); diff --git a/src/git-utils.ts b/src/git-utils.ts index 41f3749..4946cfa 100644 --- a/src/git-utils.ts +++ b/src/git-utils.ts @@ -1,5 +1,6 @@ import simpleGit from 'simple-git'; import * as vscode from 'vscode'; +import { Logger } from './logger'; /** * Retrieves the staged changes from the Git repository. @@ -23,7 +24,7 @@ export async function getDiffStaged( error: null }; } catch (error) { - console.error('Error reading Git diff:', error); + Logger.error('Error reading Git diff:', error); return { diff: '', error: error.message }; } } diff --git a/src/logger.ts b/src/logger.ts new file mode 100644 index 0000000..9fbc219 --- /dev/null +++ b/src/logger.ts @@ -0,0 +1,52 @@ +import * as vscode from 'vscode'; + +class Logger { + private static outputChannel: vscode.OutputChannel; + + static initialize() { + this.outputChannel = vscode.window.createOutputChannel('AI Commit'); + } + + static info(message: string, ...args: any[]) { + this.log('INFO', message, ...args); + } + + static warn(message: string, ...args: any[]) { + this.log('WARN', message, ...args); + } + + static error(message: string, ...args: any[]) { + this.log('ERROR', message, ...args); + } + + private static log(level: string, message: string, ...args: any[]) { + const timestamp = new Date().toISOString(); + const formattedMessage = `[${timestamp}] [${level}] ${message}`; + + if (args.length > 0) { + this.outputChannel.appendLine(`${formattedMessage} ${args.map(a => this.formatArg(a)).join(' ')}`); + } else { + this.outputChannel.appendLine(formattedMessage); + } + } + + private static formatArg(a: any): string { + if (a instanceof Error) { + return `${a.message}\n${a.stack || ''}`; + } + if (typeof a === 'object') { + return JSON.stringify(a, null, 2); + } + return String(a); + } + + static show() { + this.outputChannel?.show(true); + } + + static dispose() { + this.outputChannel?.dispose(); + } +} + +export { Logger };