// src/services/logger.ts export enum LogLevel { ERROR = 0, WARN = 1, INFO = 2, DEBUG = 3 } interface LogContext { [key: string]: any; } class Logger { private logLevel: LogLevel; private enabledContexts: Set; constructor() { this.logLevel = this.getLogLevel(); this.enabledContexts = this.getEnabledContexts(); } private getLogLevel(): LogLevel { const level = process.env.LOG_LEVEL?.toUpperCase() || 'INFO'; switch (level) { case 'ERROR': return LogLevel.ERROR; case 'WARN': return LogLevel.WARN; case 'DEBUG': return LogLevel.DEBUG; default: return LogLevel.INFO; } } private getEnabledContexts(): Set { const contexts = process.env.LOG_CONTEXTS?.split(',') || []; return new Set(contexts.map(c => c.trim().toLowerCase())); } private shouldLog(level: LogLevel, context: string): boolean { if (level > this.logLevel) return false; if (this.enabledContexts.size > 0 && !this.enabledContexts.has(context.toLowerCase())) { return false; } return true; } private formatMessage(level: string, context: string, message: string, data?: LogContext): string { const timestamp = new Date().toISOString(); const contextStr = context.toUpperCase().padEnd(12); let logMessage = `[${timestamp}] ${level.padEnd(5)} [${contextStr}] ${message}`; if (data && Object.keys(data).length > 0) { const dataStr = Object.entries(data) .map(([key, value]) => `${key}=${typeof value === 'object' ? JSON.stringify(value) : value}`) .join(' '); logMessage += ` | ${dataStr}`; } return logMessage; } error(context: string, message: string, error?: Error, data?: LogContext): void { if (!this.shouldLog(LogLevel.ERROR, context)) return; const logData = { ...data }; if (error) { logData.error = error.message; logData.stack = error.stack; } console.error(this.formatMessage('ERROR', context, message, logData)); } warn(context: string, message: string, data?: LogContext): void { if (!this.shouldLog(LogLevel.WARN, context)) return; console.warn(this.formatMessage('WARN', context, message, data)); } info(context: string, message: string, data?: LogContext): void { if (!this.shouldLog(LogLevel.INFO, context)) return; console.log(this.formatMessage('INFO', context, message, data)); } debug(context: string, message: string, data?: LogContext): void { if (!this.shouldLog(LogLevel.DEBUG, context)) return; console.log(this.formatMessage('DEBUG', context, message, data)); } // Specialized logging methods for common contexts pipeline(phase: string, action: string, details?: LogContext): void { this.info('pipeline', `${phase}/${action}`, details); } audit(phase: string, action: string, confidence: number, timeMs: number, metadata?: LogContext): void { this.info('audit', `${phase}/${action} completed`, { confidence: `${confidence}%`, duration: `${timeMs}ms`, ...metadata }); } api(method: string, path: string, status: number, details?: LogContext): void { const level = status >= 400 ? LogLevel.ERROR : status >= 300 ? LogLevel.WARN : LogLevel.INFO; if (level === LogLevel.ERROR) { this.error('api', `${method} ${path}`, undefined, { status, ...details }); } else if (level === LogLevel.WARN) { this.warn('api', `${method} ${path}`, { status, ...details }); } else { this.info('api', `${method} ${path}`, { status, ...details }); } } embedding(action: string, details?: LogContext): void { this.info('embedding', action, details); } selection(method: string, toolCount: number, conceptCount: number, details?: LogContext): void { this.info('selection', `${method} selection completed`, { tools: toolCount, concepts: conceptCount, ...details }); } confidence(toolName: string, overall: number, semantic: number, suitability: number): void { this.debug('confidence', `${toolName} scored`, { overall: `${overall}%`, semantic: `${semantic}%`, suitability: `${suitability}%` }); } queue(action: string, details?: LogContext): void { this.info('queue', action, details); } // Performance timing time(context: string, label: string): string { const timerId = `${context}:${label}:${Date.now()}`; console.time(timerId); return timerId; } timeEnd(timerId: string, message?: string): void { console.timeEnd(timerId); if (message) { const [context] = timerId.split(':'); this.debug(context, message); } } } export const logger = new Logger(); // Export convenience functions for common patterns export const logPipeline = logger.pipeline.bind(logger); export const logAudit = logger.audit.bind(logger); export const logAPI = logger.api.bind(logger); export const logError = logger.error.bind(logger); export const logInfo = logger.info.bind(logger);