/** * Enhanced logger with self-contained database logging * * Provides comprehensive logging with console and database output. * * @author Matthew Raymer * @version 2.0.0 * @since 2025-01-25 */ import { PlatformServiceFactory } from "@/services/PlatformServiceFactory"; const _memoryLogs: string[] = []; export function appendToMemoryLogs(message: string): void { _memoryLogs.push(`${new Date().toISOString()}: ${message}`); if (_memoryLogs.length > 1000) { _memoryLogs.splice(0, _memoryLogs.length - 1000); } } export function getMemoryLogs(): string[] { return [..._memoryLogs]; } export function safeStringify(obj: unknown) { const seen = new WeakSet(); return JSON.stringify(obj, (_key, value) => { if (typeof value === "object" && value !== null) { if (seen.has(value)) { return "[Circular]"; } seen.add(value); } if (typeof value === "function") { return `[Function: ${value.name || "anonymous"}]`; } return value; }); } // Determine if we should suppress verbose logging (for Electron) const isElectron = process.env.VITE_PLATFORM === "electron"; const isProduction = process.env.NODE_ENV === "production"; // Track initialization state to prevent circular dependencies let isInitializing = true; // Mark initialization as complete after a delay to allow all services to start setTimeout(() => { isInitializing = false; }, 2000); // 2 second delay to allow all services to initialize // Function to check if we should skip database logging during initialization function shouldSkipDatabaseLogging(message: string): boolean { // Skip during initialization phase if (isInitializing) { return true; } // Skip specific initialization-related messages even after initialization const initializationMessages = [ "[PlatformServiceFactory]", "[SQLWorker]", "[WebPlatformService]", "[CapacitorPlatformService]", "[CapacitorMigration]", "[DB-Integrity]", "[Migration]", "[IndexedDBMigrationService]", "Creating singleton instance", "Worker loaded", "Worker initialized", "Platform service", ]; return initializationMessages.some((pattern) => message.includes(pattern)); } // Self-contained database logging function async function logToDatabase( message: string, level: string = "info", ): Promise { // Prevent infinite logging loops if (isInitializing || shouldSkipDatabaseLogging(message)) { return; } try { const platform = PlatformServiceFactory.getInstance(); const todayKey = new Date().toDateString(); await platform.dbExec("INSERT INTO logs (date, message) VALUES (?, ?)", [ todayKey, `[${level.toUpperCase()}] ${message}`, ]); } catch (error) { // Fallback to console if database logging fails // eslint-disable-next-line no-console console.error(`[Logger] Database logging failed: ${error}`); } } // Enhanced logger with self-contained database methods export const logger = { debug: (message: string, ...args: unknown[]) => { // Debug logs are very verbose - only show in development mode for web if (!isProduction && !isElectron) { // eslint-disable-next-line no-console console.debug(message, ...args); } // Don't log debug messages to database to reduce noise }, log: (message: string, ...args: unknown[]) => { // Regular logs - show in development or for capacitor, but quiet for Electron if ( (!isProduction && !isElectron) || process.env.VITE_PLATFORM === "capacitor" ) { // eslint-disable-next-line no-console console.log(message, ...args); } // Database logging const argsString = args.length > 0 ? " - " + safeStringify(args) : ""; logToDatabase(message + argsString, "info"); }, info: (message: string, ...args: unknown[]) => { if ( process.env.NODE_ENV !== "production" || process.env.VITE_PLATFORM === "capacitor" || process.env.VITE_PLATFORM === "electron" ) { // eslint-disable-next-line no-console console.info(message, ...args); } // Database logging const argsString = args.length > 0 ? " - " + safeStringify(args) : ""; logToDatabase(message + argsString, "info"); }, warn: (message: string, ...args: unknown[]) => { // Always show warnings, but for Electron, suppress routine database warnings if (!isElectron || !message.includes("[CapacitorPlatformService]")) { // eslint-disable-next-line no-console console.warn(message, ...args); } // Database logging const argsString = args.length > 0 ? " - " + safeStringify(args) : ""; logToDatabase(message + argsString, "warn"); }, error: (message: string, ...args: unknown[]) => { // Errors will always be logged to console // eslint-disable-next-line no-console console.error(message, ...args); // Database logging const messageString = safeStringify(message); const argsString = args.length > 0 ? safeStringify(args) : ""; logToDatabase(messageString + argsString, "error"); }, // New database-focused methods (self-contained) toDb: async (message: string, level?: string): Promise => { await logToDatabase(message, level || "info"); }, toConsoleAndDb: async (message: string, isError = false): Promise => { // Console output if (isError) { // eslint-disable-next-line no-console console.error(message); } else { // eslint-disable-next-line no-console console.log(message); } // Database output await logToDatabase(message, isError ? "error" : "info"); }, // Component context methods withContext: (componentName?: string) => ({ log: (message: string, level?: string) => logToDatabase(`[${componentName}] ${message}`, level), error: (message: string) => logToDatabase(`[${componentName}] ${message}`, "error"), }), }; // Function to manually mark initialization as complete export function markInitializationComplete(): void { isInitializing = false; } // Function to check initialization status export function isInitializationPhase(): boolean { return isInitializing; } // Add CommonJS export for Electron if (typeof module !== "undefined" && module.exports) { module.exports = { logger, markInitializationComplete, isInitializationPhase, }; } // Add default export for ESM export default { logger, markInitializationComplete, isInitializationPhase };