/** * Migration Logging Utilities * * Provides comprehensive logging functionality for migration operations * with different log levels, structured output, and file persistence. */ import { writeFileSync, appendFileSync, existsSync, mkdirSync } from "node:fs"; import { join } from "node:path"; export enum LogLevel { DEBUG = 0, INFO = 1, WARN = 2, ERROR = 3, CRITICAL = 4, } export interface MigrationLogEntry { timestamp: string; level: LogLevel; category: string; message: string; data?: Record; duration?: number; error?: Error; } export class MigrationLogger { private logFile: string; private startTime: number; private minLogLevel: LogLevel; constructor( logFile: string = "migration.log", minLogLevel: LogLevel = LogLevel.INFO ) { this.logFile = join(process.cwd(), "logs", logFile); this.minLogLevel = minLogLevel; this.startTime = Date.now(); this.ensureLogDirectory(); this.initializeLog(); } private ensureLogDirectory(): void { const logDir = join(process.cwd(), "logs"); if (!existsSync(logDir)) { mkdirSync(logDir, { recursive: true }); } } private initializeLog(): void { const header = ` ================================================================= MIGRATION LOG SESSION STARTED ================================================================= Time: ${new Date().toISOString()} Process ID: ${process.pid} Node Version: ${process.version} Platform: ${process.platform} Working Directory: ${process.cwd()} ================================================================= `; writeFileSync(this.logFile, header); } private createLogEntry( level: LogLevel, category: string, message: string, data?: Record, error?: Error ): MigrationLogEntry { return { timestamp: new Date().toISOString(), level, category, message, data, duration: Date.now() - this.startTime, error, }; } private writeLog(entry: MigrationLogEntry): void { if (entry.level < this.minLogLevel) return; const levelNames = ["DEBUG", "INFO", "WARN", "ERROR", "CRITICAL"]; const levelName = levelNames[entry.level]; // Console output with colors const colors = { [LogLevel.DEBUG]: "\x1b[36m", // Cyan [LogLevel.INFO]: "\x1b[32m", // Green [LogLevel.WARN]: "\x1b[33m", // Yellow [LogLevel.ERROR]: "\x1b[31m", // Red [LogLevel.CRITICAL]: "\x1b[35m", // Magenta }; const reset = "\x1b[0m"; const color = colors[entry.level]; console.log( `${color}[${entry.timestamp}] ${levelName} [${entry.category}]${reset} ${entry.message}` ); if (entry.data) { console.log(` Data:`, entry.data); } if (entry.error) { console.error(` Error:`, entry.error.message); if (entry.level >= LogLevel.ERROR) { console.error(` Stack:`, entry.error.stack); } } // File output (structured) const logLine = JSON.stringify(entry) + "\n"; appendFileSync(this.logFile, logLine); } debug( category: string, message: string, data?: Record ): void { this.writeLog(this.createLogEntry(LogLevel.DEBUG, category, message, data)); } info( category: string, message: string, data?: Record ): void { this.writeLog(this.createLogEntry(LogLevel.INFO, category, message, data)); } warn( category: string, message: string, data?: Record ): void { this.writeLog(this.createLogEntry(LogLevel.WARN, category, message, data)); } error( category: string, message: string, error?: Error, data?: Record ): void { this.writeLog( this.createLogEntry(LogLevel.ERROR, category, message, data, error) ); } critical( category: string, message: string, error?: Error, data?: Record ): void { this.writeLog( this.createLogEntry(LogLevel.CRITICAL, category, message, data, error) ); } /** * Time a function execution and log the result */ async timeExecution( category: string, operationName: string, operation: () => Promise ): Promise { const startTime = Date.now(); this.info(category, `Starting ${operationName}`); try { const result = await operation(); const duration = Date.now() - startTime; this.info(category, `Completed ${operationName}`, { duration }); return result; } catch (error) { const duration = Date.now() - startTime; this.error(category, `Failed ${operationName}`, error as Error, { duration, }); throw error; } } /** * Create a progress tracker for long-running operations */ createProgressTracker( category: string, total: number, operationName: string ) { let completed = 0; return { increment: (count: number = 1) => { completed += count; const percentage = Math.round((completed / total) * 100); this.info( category, `${operationName} progress: ${completed}/${total} (${percentage}%)` ); }, complete: () => { this.info( category, `${operationName} completed: ${completed}/${total}` ); }, fail: (error: Error) => { this.error( category, `${operationName} failed at ${completed}/${total}`, error ); }, }; } /** * Log migration step start/completion */ startStep(stepName: string, description?: string): void { this.info("MIGRATION_STEP", `🚀 Starting: ${stepName}`, { description }); } completeStep(stepName: string, duration?: number): void { this.info("MIGRATION_STEP", `✅ Completed: ${stepName}`, { duration }); } failStep(stepName: string, error: Error): void { this.error("MIGRATION_STEP", `❌ Failed: ${stepName}`, error); } /** * Log migration phase transitions */ startPhase(phaseName: string, description?: string): void { this.info("MIGRATION_PHASE", `📋 Starting Phase: ${phaseName}`, { description, }); } completePhase(phaseName: string): void { this.info("MIGRATION_PHASE", `🎉 Completed Phase: ${phaseName}`); } /** * Close the log session */ close(): void { const totalDuration = Date.now() - this.startTime; const footer = ` ================================================================= MIGRATION LOG SESSION ENDED ================================================================= Total Duration: ${totalDuration}ms Time: ${new Date().toISOString()} ================================================================= `; appendFileSync(this.logFile, footer); } } // Singleton instance for easy access export const migrationLogger = new MigrationLogger();