import { _decorator, Component, log, sys } from 'cc'; import { uploadError } from '../main/comm'; export class ErrorManager { private static _instance: ErrorManager = null; // 用于存储已上报的错误指纹 private reportedErrors: Set = new Set(); public static get instance(): ErrorManager { if (!this._instance) { this._instance = new ErrorManager(); } return this._instance; } private constructor() { this.initialize(); } // 放在 ErrorManager.initialize() 里 private initialize(): void { if ((window as any).__rp_err_init__) return; (window as any).__rp_err_init__ = true; // 1) JS 运行时 & 资源加载错误 window.addEventListener('error', (event: any) => { // 资源错误(img/script/link)的 event 没有 error 对象 if (event && event.target && (event.target.src || event.target.href)) { const el = event.target; const url = el.src || el.href; this.reportError(`resource error: ${el.tagName} ${url}`); return; } const err: any = event?.error || {}; const msg = `error: ${event?.message}\nfrom: ${event?.filename}\npos: ${event?.lineno}:${event?.colno}\nstack: ${err.stack || 'no stack'}`; this.reportError(msg); }, true); // 2) Promise 未捕获 window.addEventListener('unhandledrejection', (event: any) => { const r: any = event?.reason || {}; const msg = `unhandledrejection: ${r.message || String(r)}\nstack: ${r.stack || 'no stack'}`; this.reportError(msg); }, true); // 3) 覆盖 console const rawErr = console.error.bind(console); const rawWarn = console.warn.bind(console); console.error = (...args: any[]) => { try { this.reportError(args.map(a => (a && a.stack) ? a.stack : String(a)).join(' ')); } catch { } rawErr(...args); }; console.warn = (...args: any[]) => { try { this.reportError(args.map(a => (a && a.stack) ? a.stack : String(a)).join(' ')); } catch { } rawWarn(...args); }; // 4) 覆盖 Cocos 日志(引擎内部 try/catch 只会调 cc.error/cc.warn) const ccAny: any = (window as any).cc; if (ccAny && !ccAny.__rp_hooked__) { ccAny.__rp_hooked__ = true; const e0 = ccAny.error?.bind(ccAny); const e1 = ccAny.errorID?.bind(ccAny); const w0 = ccAny.warn?.bind(ccAny); if (e0) ccAny.error = (...args: any[]) => { try { this.reportError(args.map(String).join(' ')); } catch { } e0(...args); }; if (e1) ccAny.errorID = (...args: any[]) => { try { this.reportError(args.map(String).join(' ')); } catch { } e1(...args); }; if (w0) ccAny.warn = (...args: any[]) => { try { this.reportError(args.map(String).join(' ')); } catch { } w0(...args); }; } } // 简单的指纹生成方法 private generateErrorFingerprint(error: string): string { // 只取错误消息的前100个字符作为指纹 return error.substring(0, 100); } // 手动上报错误 public reportError(errorMessage: string): void { try { let fingerprint = this.generateErrorFingerprint(errorMessage); log("start report error", fingerprint); // 检查是否已经上报过相同错误 if (this.reportedErrors.has(fingerprint)) { log("same error already reported, skip"); return; } // 记录已上报错误 this.reportedErrors.add(fingerprint); uploadError(errorMessage) } catch (err) { log("report error error", err); } } } // 初始化全局错误管理器 export function initErrorManager() { let errorManager = ErrorManager.instance; return errorManager; }