/* eslint-disable prefer-rest-params */ import type { ErrorReporter } from '../ErrorReporter'; import { Collector } from '@tencent/merlin-core'; import { buildIssueFromEvent, buildIssueFromOnError } from '../utils'; import { ErrorReportItemData } from '../types'; /** Web 全局错误收集器 * @desc 用于兜底和发现未知错误的收集器,对于已发现的错误,尽量用 `ErrorReporter.reportError()` 来上报,而不是都依赖此兜底收集器 */ export class GeneralWebErrorCollector extends Collector { private onerrorHandler = this.privateOnerrorHandler.bind(this); private errorListener = this.privateErrorListener.bind(this); private unhandledrejectionListener = this.privateUnhandledrejectionListener.bind(this); private lastCaughtError: Error | null = null; init(reporter: ErrorReporter) { super.init(reporter); window.onerror = this.onerrorHandler; window.addEventListener('error', this.errorListener, true); window.addEventListener('unhandledrejection', this.unhandledrejectionListener); } destroy() { if (window.onerror === this.onerrorHandler) { window.onerror = null; } window.removeEventListener('error', this.errorListener, true); window.removeEventListener('unhandledrejection', this.unhandledrejectionListener); super.destroy(); } private privateOnerrorHandler( event: Event | string, source?: string, lineno?: number, colno?: number, error?: Error, ) { if (error === this.lastCaughtError) { return true; } this.lastCaughtError = error || null; if (this.reporter?.logError) { console.error(error || event); } this.reporter?.log('GeneralWebErrorCollector.onerrorHandler', arguments); this.reporter?.capture(buildIssueFromOnError(event, source, lineno, colno, error)); return true; // 防止继续向上抛出 } private privateErrorListener(event: ErrorEvent) { const { error } = event; if (error === this.lastCaughtError) { return; } this.lastCaughtError = error || null; if (this.reporter?.logError) { console.error(error || event); } this.reporter?.log('GeneralWebErrorCollector.errorListener', arguments); this.reporter?.capture(buildIssueFromEvent(event)); } private privateUnhandledrejectionListener(event: PromiseRejectionEvent) { event.preventDefault(); const error = event.reason || (event as unknown as CustomEvent).detail?.reason; if (error === this.lastCaughtError) { return; } this.lastCaughtError = error || null; if (this.reporter?.logError) { console.error(error || event); } this.reporter?.log('GeneralWebErrorCollector.unhandledrejectionListener', arguments); this.reporter?.capture(buildIssueFromEvent(event)); } }