UNPKG

murmuraba

Version:

Real-time audio noise reduction with advanced chunked processing for web applications

321 lines (320 loc) 13 kB
/** * Centralized Logging Utility * Replaces scattered console.log statements with structured logging */ // Log Levels export var LogLevel; (function (LogLevel) { LogLevel[LogLevel["DEBUG"] = 0] = "DEBUG"; LogLevel[LogLevel["INFO"] = 1] = "INFO"; LogLevel[LogLevel["WARN"] = 2] = "WARN"; LogLevel[LogLevel["ERROR"] = 3] = "ERROR"; LogLevel[LogLevel["SILENT"] = 4] = "SILENT"; })(LogLevel || (LogLevel = {})); // Log Categories export var LogCategory; (function (LogCategory) { LogCategory["AUDIO"] = "AUDIO"; LogCategory["RECORDING"] = "RECORDING"; LogCategory["PROCESSING"] = "PROCESSING"; LogCategory["WASM"] = "WASM"; LogCategory["UI"] = "UI"; LogCategory["API"] = "API"; LogCategory["PERFORMANCE"] = "PERFORMANCE"; LogCategory["SYSTEM"] = "SYSTEM"; LogCategory["TEST"] = "TEST"; })(LogCategory || (LogCategory = {})); // Default Configuration const DEFAULT_CONFIG = { level: process.env.NODE_ENV === 'development' ? LogLevel.DEBUG : LogLevel.INFO, enableConsole: true, enableStorage: true, maxStoredEntries: 1000, enableTimestamps: true, enableColors: true, categories: Object.values(LogCategory), }; // Logger Class class Logger { constructor(config = {}) { this.entries = []; this.listeners = []; this.config = { ...DEFAULT_CONFIG, ...config }; } // Core logging method log(level, category, message, data, context) { // Check if this log level should be processed if (level < this.config.level) return; // Check if this category is enabled if (!this.config.categories.includes(category)) return; const entry = { timestamp: Date.now(), level, category, message, data, source: this.getCallerInfo(), context, }; // Store entry if enabled if (this.config.enableStorage) { this.entries.push(entry); // Maintain max entries limit if (this.entries.length > this.config.maxStoredEntries) { this.entries.shift(); } } // Console output if enabled if (this.config.enableConsole) { this.outputToConsole(entry); } // Notify listeners this.listeners.forEach(listener => { try { listener(entry); } catch (error) { console.error('Error in log listener:', error); } }); } // Debug logging debug(category, message, data, context) { this.log(LogLevel.DEBUG, category, message, data, context); } // Info logging info(category, message, data, context) { this.log(LogLevel.INFO, category, message, data, context); } // Warning logging warn(category, message, data, context) { this.log(LogLevel.WARN, category, message, data, context); } // Error logging error(category, message, data, context) { this.log(LogLevel.ERROR, category, message, data, context); } // Performance logging time(category, label, context) { const startTime = performance.now(); this.debug(category, `Timer started: ${label}`, { startTime }, context); return { end: () => { const endTime = performance.now(); const duration = endTime - startTime; this.info(category, `Timer ended: ${label}`, { startTime, endTime, duration: `${duration.toFixed(2)}ms` }, context); return duration; } }; } // Group logging group(category, title, context) { this.info(category, `Group Start: ${title}`, undefined, context); return { log: (message, data) => this.info(category, ` ${message}`, data, context), end: () => this.info(category, `Group End: ${title}`, undefined, context), }; } // Output to console with formatting outputToConsole(entry) { const { level, category, message, data, timestamp } = entry; // Format timestamp const timeStr = this.config.enableTimestamps ? `[${new Date(timestamp).toISOString()}]` : ''; // Format category const categoryStr = `[${category}]`; // Choose console method based on level const consoleMethod = this.getConsoleMethod(level); // Format message with colors if enabled const formattedMessage = this.config.enableColors ? this.colorizeMessage(level, `${timeStr}${categoryStr} ${message}`) : `${timeStr}${categoryStr} ${message}`; // Output to console if (data !== undefined) { consoleMethod(formattedMessage, data); } else { consoleMethod(formattedMessage); } } // Get appropriate console method getConsoleMethod(level) { switch (level) { case LogLevel.DEBUG: return console.debug; case LogLevel.INFO: return console.info; case LogLevel.WARN: return console.warn; case LogLevel.ERROR: return console.error; default: return console.log; } } // Colorize message based on level colorizeMessage(level, message) { if (typeof window !== 'undefined') { // Browser environment - use CSS styles switch (level) { case LogLevel.DEBUG: return `%c${message}`; // Will need CSS styling case LogLevel.INFO: return `%c${message}`; case LogLevel.WARN: return `%c${message}`; case LogLevel.ERROR: return `%c${message}`; default: return message; } } else { // Node.js environment - use ANSI colors const colors = { [LogLevel.DEBUG]: '\x1b[36m', // Cyan [LogLevel.INFO]: '\x1b[32m', // Green [LogLevel.WARN]: '\x1b[33m', // Yellow [LogLevel.ERROR]: '\x1b[31m', // Red [LogLevel.SILENT]: '\x1b[37m', // White/Default }; const reset = '\x1b[0m'; const color = colors[level] || ''; return `${color}${message}${reset}`; } } // Get caller info for source tracking getCallerInfo() { const stack = new Error().stack; if (!stack) return 'unknown'; const stackLines = stack.split('\n'); // Skip the first few lines (Error, this method, log method) const callerLine = stackLines[4] || stackLines[3] || ''; // Extract file and line number const match = callerLine.match(/at\s+(.+)\s+\((.+):(\d+):(\d+)\)/); if (match) { const [, , file, line] = match; const fileName = file?.split('/').pop() || file || 'unknown'; return `${fileName}:${line}`; } return 'unknown'; } // Configuration methods setLevel(level) { this.config.level = level; } setCategories(categories) { this.config.categories = categories; } enableCategory(category) { if (!this.config.categories.includes(category)) { this.config.categories.push(category); } } disableCategory(category) { this.config.categories = this.config.categories.filter(c => c !== category); } // Listener management addListener(listener) { this.listeners.push(listener); return () => this.removeListener(listener); } removeListener(listener) { const index = this.listeners.indexOf(listener); if (index > -1) { this.listeners.splice(index, 1); } } // Export/Import logs exportLogs() { return [...this.entries]; } clearLogs() { this.entries = []; } // Search logs searchLogs(query) { return this.entries.filter(entry => { if (query.level !== undefined && entry.level < query.level) return false; if (query.category !== undefined && entry.category !== query.category) return false; if (query.message && !entry.message.toLowerCase().includes(query.message.toLowerCase())) return false; if (query.since && entry.timestamp < query.since) return false; if (query.until && entry.timestamp > query.until) return false; return true; }); } // Get stats getStats() { const stats = { total: this.entries.length, byLevel: {}, byCategory: {}, timeRange: { oldest: this.entries[0]?.timestamp, newest: this.entries[this.entries.length - 1]?.timestamp, }, }; this.entries.forEach(entry => { stats.byLevel[entry.level] = (stats.byLevel[entry.level] || 0) + 1; stats.byCategory[entry.category] = (stats.byCategory[entry.category] || 0) + 1; }); return stats; } } // Create singleton instance const logger = new Logger(); // Category-specific logger interfaces export const AudioLogger = { debug: (message, data, context) => logger.debug(LogCategory.AUDIO, message, data, context), info: (message, data, context) => logger.info(LogCategory.AUDIO, message, data, context), warn: (message, data, context) => logger.warn(LogCategory.AUDIO, message, data, context), error: (message, data, context) => logger.error(LogCategory.AUDIO, message, data, context), time: (label, context) => logger.time(LogCategory.AUDIO, label, context), group: (title, context) => logger.group(LogCategory.AUDIO, title, context), }; export const RecordingLogger = { debug: (message, data, context) => logger.debug(LogCategory.RECORDING, message, data, context), info: (message, data, context) => logger.info(LogCategory.RECORDING, message, data, context), warn: (message, data, context) => logger.warn(LogCategory.RECORDING, message, data, context), error: (message, data, context) => logger.error(LogCategory.RECORDING, message, data, context), time: (label, context) => logger.time(LogCategory.RECORDING, label, context), group: (title, context) => logger.group(LogCategory.RECORDING, title, context), }; export const ProcessingLogger = { debug: (message, data, context) => logger.debug(LogCategory.PROCESSING, message, data, context), info: (message, data, context) => logger.info(LogCategory.PROCESSING, message, data, context), warn: (message, data, context) => logger.warn(LogCategory.PROCESSING, message, data, context), error: (message, data, context) => logger.error(LogCategory.PROCESSING, message, data, context), time: (label, context) => logger.time(LogCategory.PROCESSING, label, context), group: (title, context) => logger.group(LogCategory.PROCESSING, title, context), }; export const WASMLogger = { debug: (message, data, context) => logger.debug(LogCategory.WASM, message, data, context), info: (message, data, context) => logger.info(LogCategory.WASM, message, data, context), warn: (message, data, context) => logger.warn(LogCategory.WASM, message, data, context), error: (message, data, context) => logger.error(LogCategory.WASM, message, data, context), time: (label, context) => logger.time(LogCategory.WASM, label, context), group: (title, context) => logger.group(LogCategory.WASM, title, context), }; export const UILogger = { debug: (message, data, context) => logger.debug(LogCategory.UI, message, data, context), info: (message, data, context) => logger.info(LogCategory.UI, message, data, context), warn: (message, data, context) => logger.warn(LogCategory.UI, message, data, context), error: (message, data, context) => logger.error(LogCategory.UI, message, data, context), time: (label, context) => logger.time(LogCategory.UI, label, context), group: (title, context) => logger.group(LogCategory.UI, title, context), }; export const PerformanceLogger = { debug: (message, data, context) => logger.debug(LogCategory.PERFORMANCE, message, data, context), info: (message, data, context) => logger.info(LogCategory.PERFORMANCE, message, data, context), warn: (message, data, context) => logger.warn(LogCategory.PERFORMANCE, message, data, context), error: (message, data, context) => logger.error(LogCategory.PERFORMANCE, message, data, context), time: (label, context) => logger.time(LogCategory.PERFORMANCE, label, context), group: (title, context) => logger.group(LogCategory.PERFORMANCE, title, context), }; // Export main logger and utilities export { logger as Logger }; export default logger;