import fs from 'fs';
import path from 'path';
export interface LogEntry {
timestamp: string;
level: 'info' | 'error' | 'debug' | 'warn';
message: string;
data?: any;
projectPath?: string; // Optional: which project this log is from
}
export class LogManager {
private baseLogPath: string;
constructor(baseLogPath: string = '.debug/debug.log') {
this.baseLogPath = baseLogPath;
}
/**
* Get the full log file path for a specific project
*/
private getLogFilePath(projectPath?: string): string {
if (!projectPath) {
// Fallback to MCP directory if no project path specified
return this.baseLogPath;
}
// Resolve the log file path relative to the project directory
// projectPath can be absolute or relative
const resolvedProjectPath = path.resolve(projectPath);
return path.join(resolvedProjectPath, '.debug', 'debug.log');
}
private ensureLogDirectory(logFilePath: string): void {
const logDir = path.dirname(logFilePath);
if (!fs.existsSync(logDir)) {
fs.mkdirSync(logDir, { recursive: true });
}
}
appendLog(entry: LogEntry, projectPath?: string): boolean {
try {
const logFilePath = this.getLogFilePath(projectPath);
this.ensureLogDirectory(logFilePath);
const logLine = JSON.stringify(entry) + '\n';
fs.appendFileSync(logFilePath, logLine, 'utf8');
return true;
} catch (error) {
console.error('Failed to write log:', error);
return false;
}
}
readLogs(lastLines: number = 100, projectPath?: string): LogEntry[] {
try {
const logFilePath = this.getLogFilePath(projectPath);
if (!fs.existsSync(logFilePath)) {
return [];
}
const content = fs.readFileSync(logFilePath, 'utf8');
const lines = content.trim().split('\n');
const logs = lines
.filter(line => line.trim())
.slice(-lastLines)
.map(line => {
try {
return JSON.parse(line) as LogEntry;
} catch {
return null;
}
})
.filter((log): log is LogEntry => log !== null);
return logs;
} catch (error) {
console.error('Failed to read logs:', error);
return [];
}
}
clearLogs(projectPath?: string): { success: boolean; deletedCount: number } {
try {
const logFilePath = this.getLogFilePath(projectPath);
if (!fs.existsSync(logFilePath)) {
return { success: true, deletedCount: 0 };
}
const content = fs.readFileSync(logFilePath, 'utf8');
const lineCount = content.trim().split('\n').length;
fs.writeFileSync(logFilePath, '', 'utf8');
return { success: true, deletedCount: lineCount };
} catch (error) {
console.error('Failed to clear logs:', error);
return { success: false, deletedCount: 0 };
}
}
getLogStats(projectPath?: string): { totalLines: number; fileSize: number; logFilePath: string } {
try {
const logFilePath = this.getLogFilePath(projectPath);
if (!fs.existsSync(logFilePath)) {
return { totalLines: 0, fileSize: 0, logFilePath };
}
const content = fs.readFileSync(logFilePath, 'utf8');
const lines = content.trim().split('\n');
return {
totalLines: lines.filter(line => line.trim()).length,
fileSize: content.length,
logFilePath
};
} catch (error) {
const logFilePath = this.getLogFilePath(projectPath);
return { totalLines: 0, fileSize: 0, logFilePath };
}
}
}