/** * Structured logging utility */ type LogLevel = 'debug' | 'info' | 'warn' | 'error' interface LogEntry { level: LogLevel message: string timestamp: string context?: Record error?: Error } class Logger { private logLevel: LogLevel constructor() { this.logLevel = (process.env.NODE_ENV === 'development' ? 'debug' : 'info') as LogLevel } private shouldLog(level: LogLevel): boolean { const levels: LogLevel[] = ['debug', 'info', 'warn', 'error'] return levels.indexOf(level) >= levels.indexOf(this.logLevel) } private formatEntry(entry: LogEntry): string { const { level, message, timestamp, context, error } = entry const parts = [`[${timestamp}]`, `[${level.toUpperCase()}]`, message] if (context && Object.keys(context).length > 0) { parts.push(JSON.stringify(context)) } if (error) { parts.push(`\nError: ${error.message}`) if (error.stack) { parts.push(`\nStack: ${error.stack}`) } } return parts.join(' ') } private log(level: LogLevel, message: string, context?: Record, error?: Error) { if (!this.shouldLog(level)) { return } const entry: LogEntry = { level, message, timestamp: new Date().toISOString(), context, error, } const formatted = this.formatEntry(entry) switch (level) { case 'debug': console.debug(formatted) break case 'info': console.info(formatted) break case 'warn': console.warn(formatted) break case 'error': console.error(formatted) break } } debug(message: string, context?: Record) { this.log('debug', message, context) } info(message: string, context?: Record) { this.log('info', message, context) } warn(message: string, context?: Record) { this.log('warn', message, context) } error(message: string, error?: Error, context?: Record) { this.log('error', message, context, error) } } export const logger = new Logger()