import AsyncStorage from '@/utils/kvStore'; interface LogEntry { id: string; timestamp: number; level: 'DEBUG' | 'INFO' | 'WARN' | 'ERROR'; message: string; data?: any; } /** * 改进的日志系统 * 主要改进: * 1. 内存队列和批量写入机制 - 避免频繁存储操作 * 2. 写入锁机制 - 防止并发写入导致数据丢失 * 3. 改进的错误处理和重试机制 * 4. 优化的 ID 生成 - 确保唯一性 * 5. 写入确认机制 - 返回 Promise 让调用者知道日志是否成功保存 */ class Logger { private static instance: Logger; private readonly maxLogs = 1000; // 最多保存1000条日志 private readonly storageKey = '@app_logs'; // 内存队列相关 private memoryQueue: LogEntry[] = []; private readonly queueMaxSize = 50; // 达到50条日志时触发批量写入 private readonly flushInterval = 5000; // 5秒自动刷新一次 private flushTimer: ReturnType | null = null; // 写入锁相关 private isWriting = false; private writePromise: Promise | null = null; // ID 生成相关 private idCounter = 0; private lastTimestamp = 0; // 重试相关 private readonly maxRetries = 3; private readonly retryDelay = 1000; // 1秒 static getInstance(): Logger { if (!Logger.instance) { Logger.instance = new Logger(); } return Logger.instance; } constructor() { // 启动定时刷新机制 this.startFlushTimer(); // 应用退出时确保刷新日志 if (typeof global !== 'undefined') { // 注册应用退出时的清理函数(如果可用) this.setupAppExitHandler(); } } /** * 启动定时刷新机制 */ private startFlushTimer(): void { if (this.flushTimer) { clearInterval(this.flushTimer); } this.flushTimer = setInterval(() => { if (this.memoryQueue.length > 0) { this.flushQueue().catch(error => { console.error('[Logger] Auto flush failed:', error); }); } }, this.flushInterval); } /** * 设置应用退出处理 */ private setupAppExitHandler(): void { // 这是一个最佳努力的清理,不是所有场景都能捕获 if (typeof process !== 'undefined' && process.on) { const cleanup = () => { if (this.memoryQueue.length > 0) { // 同步刷新(应用退出时) this.flushQueueSync(); } }; process.on('exit', cleanup); process.on('SIGINT', cleanup); process.on('SIGTERM', cleanup); } } /** * 生成唯一 ID */ private generateId(): string { const now = Date.now(); // 如果时间戳相同,增加计数器 if (now === this.lastTimestamp) { this.idCounter++; } else { this.lastTimestamp = now; this.idCounter = 0; } // 格式:timestamp-counter-random const random = Math.random().toString(36).substr(2, 5); return `${now}-${this.idCounter}-${random}`; } /** * 从存储中获取日志(带重试) */ private async getLogs(retries = 0): Promise { try { const logsJson = await AsyncStorage.getItem(this.storageKey); return logsJson ? JSON.parse(logsJson) : []; } catch (error) { if (retries < this.maxRetries) { console.warn(`[Logger] Failed to get logs, retrying (${retries + 1}/${this.maxRetries})...`); await this.delay(this.retryDelay); return this.getLogs(retries + 1); } console.error('[Logger] Failed to get logs after retries:', error); return []; } } /** * 保存日志到存储(带重试) */ private async saveLogs(logs: LogEntry[], retries = 0): Promise { try { // 只保留最新的maxLogs条日志 const trimmedLogs = logs.slice(-this.maxLogs); await AsyncStorage.setItem(this.storageKey, JSON.stringify(trimmedLogs)); } catch (error) { if (retries < this.maxRetries) { console.warn(`[Logger] Failed to save logs, retrying (${retries + 1}/${this.maxRetries})...`); await this.delay(this.retryDelay); return this.saveLogs(logs, retries + 1); } console.error('[Logger] Failed to save logs after retries:', error); throw error; } } /** * 延迟函数 */ private delay(ms: number): Promise { return new Promise(resolve => setTimeout(resolve, ms)); } /** * 安全地序列化数据 */ private serializeData(data: any): any { if (!data || typeof data !== 'object') { return data; } try { // 处理 Error 对象 if (data instanceof Error) { return { name: data.name, message: data.message, stack: data.stack }; } // 处理其他对象 - 使用 replacer 函数处理循环引用 const seen = new WeakSet(); return JSON.parse(JSON.stringify(data, (key, value) => { if (typeof value === 'object' && value !== null) { // 检测循环引用 if (seen.has(value)) { return '[Circular Reference]'; } seen.add(value); // 处理特殊对象类型 if (value.constructor === Object || Array.isArray(value)) { return value; } // 对于其他对象类型,转换为字符串表示 return value.toString ? value.toString() : '[Object]'; } return value; })); } catch (serializeError) { // 如果序列化失败,返回基本信息 return { error: 'Failed to serialize data', type: typeof data, toString: data.toString ? data.toString() : 'N/A' }; } } /** * 刷新队列到存储(异步,带锁) */ private async flushQueue(): Promise { // 如果正在写入,等待当前写入完成 if (this.isWriting && this.writePromise) { await this.writePromise; return; } // 如果队列为空,直接返回 if (this.memoryQueue.length === 0) { return; } // 设置写入锁 this.isWriting = true; // 保存要写入的日志(避免在写入过程中队列被修改) const logsToWrite = [...this.memoryQueue]; this.memoryQueue = []; this.writePromise = (async () => { try { // 获取现有日志 const existingLogs = await this.getLogs(); // 合并日志 const allLogs = [...existingLogs, ...logsToWrite]; // 保存到存储 await this.saveLogs(allLogs); console.log(`[Logger] Successfully flushed ${logsToWrite.length} logs to storage`); } catch (error) { console.error('[Logger] Failed to flush queue:', error); // 写入失败,将日志放回队列(保留在内存中) this.memoryQueue.unshift(...logsToWrite); // 限制队列大小,避免内存溢出 if (this.memoryQueue.length > this.maxLogs) { const overflow = this.memoryQueue.length - this.maxLogs; console.warn(`[Logger] Queue overflow, dropping ${overflow} oldest logs`); this.memoryQueue = this.memoryQueue.slice(-this.maxLogs); } } finally { // 释放写入锁 this.isWriting = false; this.writePromise = null; } })(); await this.writePromise; } /** * 同步刷新队列(应用退出时使用) */ private flushQueueSync(): void { if (this.memoryQueue.length === 0) { return; } try { // 注意:这是一个阻塞操作,仅在应用退出时使用 const logsToWrite = [...this.memoryQueue]; this.memoryQueue = []; // 这里我们无法使用异步操作,只能尝试 console.log(`[Logger] Attempting to flush ${logsToWrite.length} logs synchronously`); // 实际上在 React Native 中很难做到真正的同步保存 // 这里只是一个最佳努力的尝试 this.flushQueue().catch(error => { console.error('[Logger] Sync flush failed:', error); }); } catch (error) { console.error('[Logger] Failed to flush queue synchronously:', error); } } /** * 添加日志到队列 */ private async addLog(level: LogEntry['level'], message: string, data?: any): Promise { // 序列化数据 const safeData = this.serializeData(data); // 创建日志条目 const logEntry: LogEntry = { id: this.generateId(), timestamp: Date.now(), level, message, data: safeData }; // 输出到控制台 try { const logMethod = level === 'ERROR' ? console.error : level === 'WARN' ? console.warn : level === 'INFO' ? console.info : console.log; logMethod(`[${level}] ${message}`, safeData !== undefined ? safeData : ''); } catch (consoleError) { // 如果控制台输出失败,使用最基本的 console.log console.log(`[${level}] ${message}`); } // 添加到内存队列 this.memoryQueue.push(logEntry); // 检查是否需要刷新队列 if (this.memoryQueue.length >= this.queueMaxSize) { // 不等待刷新完成,避免阻塞调用者 this.flushQueue().catch(error => { console.error('[Logger] Failed to flush queue after size threshold:', error); }); } } /** * 公共日志方法 */ async debug(message: string, data?: any): Promise { await this.addLog('DEBUG', message, data); } async info(message: string, data?: any): Promise { await this.addLog('INFO', message, data); } async warn(message: string, data?: any): Promise { await this.addLog('WARN', message, data); } async error(message: string, data?: any): Promise { await this.addLog('ERROR', message, data); } /** * 获取所有日志(包括内存队列中的) */ async getAllLogs(): Promise { // 先刷新队列 await this.flushQueue(); // 然后获取存储中的日志 return await this.getLogs(); } /** * 清除所有日志 */ async clearLogs(): Promise { try { // 清空内存队列 this.memoryQueue = []; // 清除存储 await AsyncStorage.removeItem(this.storageKey); console.log('[Logger] All logs cleared successfully'); } catch (error) { console.error('[Logger] Failed to clear logs:', error); throw error; } } /** * 导出日志 */ async exportLogs(): Promise { // 先刷新队列 await this.flushQueue(); // 然后获取并导出日志 const logs = await this.getLogs(); return JSON.stringify(logs, null, 2); } /** * 手动刷新日志到存储 */ async flush(): Promise { await this.flushQueue(); } /** * 获取队列状态(用于调试) */ getQueueStatus(): { queueSize: number; isWriting: boolean } { return { queueSize: this.memoryQueue.length, isWriting: this.isWriting }; } /** * 清理资源 */ destroy(): void { if (this.flushTimer) { clearInterval(this.flushTimer); this.flushTimer = null; } // 最后刷新一次 if (this.memoryQueue.length > 0) { this.flushQueueSync(); } } } // 导出全局日志实例和便捷函数 export const logger = Logger.getInstance(); // 便捷的全局日志函数(返回 Promise 以便调用者可以等待) export const log = { debug: (message: string, data?: any) => logger.debug(message, data), info: (message: string, data?: any) => logger.info(message, data), warn: (message: string, data?: any) => logger.warn(message, data), error: (message: string, data?: any) => logger.error(message, data), // 额外的工具函数 flush: () => logger.flush(), getQueueStatus: () => logger.getQueueStatus(), }; export type { LogEntry };