import { contextBridge, ipcRenderer } from "electron" import { LogLevel, LogLevelName } from "./common" import logger from "./preload" /** * 错误详情接口 */ interface ErrorDetail { message: string stack?: string componentInfo?: string additionalInfo?: Record timestamp: string type: string } /** * 错误处理配置 */ interface ErrorHandlerOptions { namespace?: string level?: LogLevel includeStack?: boolean includeComponentInfo?: boolean } /** * 渲染进程错误处理接口 */ interface IRendererErrorHandler { /** * 捕获错误 */ captureError(error: any, componentInfo?: string, additionalInfo?: Record): void /** * 设置错误处理选项 */ setOptions(options: Partial): void /** * 获取当前选项 */ getOptions(): ErrorHandlerOptions /** * 安装全局错误处理器 */ installGlobalHandlers(): void } /** * 默认错误处理配置 */ const DEFAULT_OPTIONS: ErrorHandlerOptions = { namespace: "error", level: LogLevel.ERROR, includeStack: true, includeComponentInfo: true, } /** * 格式化错误信息 */ const formatError = (error: any, options: ErrorHandlerOptions): ErrorDetail => { // 基本错误信息 const errorDetail: ErrorDetail = { message: "", timestamp: new Date().toISOString(), type: "Unknown", } // 处理不同类型的错误 if (error instanceof Error) { errorDetail.message = error.message errorDetail.type = error.name || error.constructor.name if (options.includeStack) { errorDetail.stack = error.stack } } else if (typeof error === "string") { errorDetail.message = error errorDetail.type = "String" } else if (error === null) { errorDetail.message = "Null error received" errorDetail.type = "Null" } else if (error === undefined) { errorDetail.message = "Undefined error received" errorDetail.type = "Undefined" } else if (typeof error === "object") { try { errorDetail.message = error.message || JSON.stringify(error) errorDetail.type = "Object" errorDetail.additionalInfo = { ...error } } catch (e) { errorDetail.message = "Unserializable error object" errorDetail.type = "Unserializable" } } else { try { errorDetail.message = String(error) errorDetail.type = typeof error } catch (e) { errorDetail.message = "Error converting to string" errorDetail.type = "Unknown" } } return errorDetail } /** * 创建渲染进程错误处理器 */ const createRendererErrorHandler = (): IRendererErrorHandler => { // 当前错误处理选项 let options: ErrorHandlerOptions = { ...DEFAULT_OPTIONS } /** * 处理并转发错误到主进程 */ const handleError = (error: any, componentInfo?: string, additionalInfo?: Record) => { // 如果已经是ErrorDetail格式,直接使用 let errorDetail: ErrorDetail if (error && typeof error === "object" && error.type && error.message && error.timestamp) { errorDetail = error as ErrorDetail } else { // 否则格式化错误 errorDetail = formatError(error, options) } // 添加组件信息 if (options.includeComponentInfo && componentInfo) { errorDetail.componentInfo = componentInfo } // 使用logger记录错误 const namespace = options.namespace || "error" const level = LogLevelName[options.level || LogLevel.ERROR].toLowerCase() // 添加额外信息 if (additionalInfo) { errorDetail.additionalInfo = { ...errorDetail.additionalInfo, ...additionalInfo, } } // 记录完整的错误信息 logger[level](namespace, JSON.stringify(errorDetail)) // 同时在控制台输出错误信息 logger[level](namespace, `${errorDetail.type}: ${errorDetail.message}`) if (errorDetail.stack) { logger[level](namespace, `Stack: ${errorDetail.stack}`) } // 如果有额外信息,单独记录 if (errorDetail.additionalInfo) { try { const additionalInfoStr = JSON.stringify(errorDetail.additionalInfo, null, 2) logger[level](namespace, `Additional Info: ${additionalInfoStr}`) } catch (e) { logger[level](namespace, "Additional Info: [Unserializable]") } } } /** * 空的安装全局错误处理器方法 * 实际的全局错误处理由renderer-error.ts负责 */ const installGlobalHandlers = () => { // 不再在preload层安装全局错误处理器 // 仅记录日志表明该方法被调用 logger.info("[ErrorHandler] Global error handlers should be installed in renderer process") } return { captureError: handleError, setOptions: (newOptions: Partial) => { options = { ...options, ...newOptions } // 同步选项到主进程 ipcRenderer.send("logger:errorOptions", options) }, getOptions: () => ({ ...options }), installGlobalHandlers, } } const errorHandler = createRendererErrorHandler() // 暴露错误处理器到渲染进程全局 contextBridge.exposeInMainWorld("preloadErrorHandler", errorHandler) // 导出类型定义,方便在渲染进程中使用 export type { IRendererErrorHandler, ErrorDetail, ErrorHandlerOptions }