/** * Structured logging module for enhanced observability * Logs with severity levels, component tracking, and metrics */ type LogLevel = 'debug' | 'info' | 'warn' | 'error'; interface LogEntry { timestamp: string; level: LogLevel; component: string; message: string; duration?: number; // milliseconds error?: string; metadata?: Record; } class Logger { private isProduction = typeof process !== 'undefined' && process.env.NODE_ENV === 'production'; /** * Log a message with timestamp and component context */ private log(level: LogLevel, component: string, message: string, metadata?: Record) { const entry: LogEntry = { timestamp: new Date().toISOString(), level, component, message, metadata, }; const prefix = `[${component}]`; const formattedMessage = `${prefix} ${message}`; const logWithMetadata = (consoleFn: typeof console.error) => { if (metadata && Object.keys(metadata).length > 0) { consoleFn(formattedMessage, metadata); } else { consoleFn(formattedMessage); } }; switch (level) { case 'error': logWithMetadata(console.error); break; case 'warn': logWithMetadata(console.warn); break; case 'info': if (!this.isProduction) { logWithMetadata(console.log); } break; case 'debug': if (!this.isProduction) { logWithMetadata(console.debug); } break; } } /** * Log debug message (development only) */ debug(component: string, message: string, metadata?: Record) { this.log('debug', component, message, metadata); } /** * Log info message */ info(component: string, message: string, metadata?: Record) { this.log('info', component, message, metadata); } /** * Log warning message */ warn(component: string, message: string, metadata?: Record) { this.log('warn', component, message, metadata); } /** * Log error with optional error object */ error(component: string, message: string, error?: Error | unknown, metadata?: Record) { const errorMetadata = { ...metadata, ...(error instanceof Error && { errorMessage: error.message, errorStack: error.stack, }), }; this.log('error', component, message, errorMetadata); } /** * Measure performance of an async function and log duration */ async measureAsync( component: string, operationName: string, fn: () => Promise, ): Promise { const start = performance.now(); try { const result = await fn(); const duration = performance.now() - start; this.debug(component, `${operationName} completed`, { duration: `${duration.toFixed(2)}ms` }); return result; } catch (error) { const duration = performance.now() - start; this.error(component, `${operationName} failed`, error, { duration: `${duration.toFixed(2)}ms` }); throw error; } } /** * Measure performance of a sync function and log duration */ measureSync( component: string, operationName: string, fn: () => T, ): T { const start = performance.now(); try { const result = fn(); const duration = performance.now() - start; this.debug(component, `${operationName} completed`, { duration: `${duration.toFixed(2)}ms` }); return result; } catch (error) { const duration = performance.now() - start; this.error(component, `${operationName} failed`, error, { duration: `${duration.toFixed(2)}ms` }); throw error; } } } // Export singleton instance export const logger = new Logger(); /** * Helper for tracking async operation metrics */ export function createAsyncMetricsTracker(component: string, operationName: string) { const start = performance.now(); return { success: (metadata?: Record) => { const duration = performance.now() - start; logger.info(component, `${operationName} succeeded`, { duration: `${duration.toFixed(2)}ms`, ...metadata }); }, error: (error: Error | unknown, metadata?: Record) => { const duration = performance.now() - start; logger.error(component, `${operationName} failed`, error, { duration: `${duration.toFixed(2)}ms`, ...metadata }); }, }; }