From c142937af900cd40fc167677bbb1dc6cf9910c5e Mon Sep 17 00:00:00 2001 From: npmrun <1549469775@qq.com> Date: Fri, 28 Mar 2025 01:32:33 +0800 Subject: [PATCH] =?UTF-8?q?refactor(logger):=20=E9=87=8D=E6=9E=84=E9=94=99?= =?UTF-8?q?=E8=AF=AF=E5=A4=84=E7=90=86=E9=80=BB=E8=BE=91=EF=BC=8C=E5=88=86?= =?UTF-8?q?=E7=A6=BB=E6=B8=B2=E6=9F=93=E8=BF=9B=E7=A8=8B=E5=92=8Cpreload?= =?UTF-8?q?=E8=BF=9B=E7=A8=8B=E7=9A=84=E9=94=99=E8=AF=AF=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将全局错误处理逻辑从preload层移动到渲染进程层,提升代码的可维护性和清晰度。同时优化错误处理逻辑,增加对错误信息的序列化和额外信息的处理。 --- packages/logger/preload-error.ts | 80 +++++-------- packages/logger/renderer-error.ts | 243 ++++++++++++++++++++++++++++++++++++++ src/renderer/src/main.ts | 2 +- 3 files changed, 275 insertions(+), 50 deletions(-) create mode 100644 packages/logger/renderer-error.ts diff --git a/packages/logger/preload-error.ts b/packages/logger/preload-error.ts index e07e927..d30760f 100644 --- a/packages/logger/preload-error.ts +++ b/packages/logger/preload-error.ts @@ -5,7 +5,7 @@ import logger from "./preload" /** * 错误详情接口 */ -export interface ErrorDetail { +interface ErrorDetail { message: string stack?: string componentInfo?: string @@ -17,7 +17,7 @@ export interface ErrorDetail { /** * 错误处理配置 */ -export interface ErrorHandlerOptions { +interface ErrorHandlerOptions { namespace?: string level?: LogLevel includeStack?: boolean @@ -115,10 +115,17 @@ const createRendererErrorHandler = (): IRendererErrorHandler => { let options: ErrorHandlerOptions = { ...DEFAULT_OPTIONS } /** - * 发送错误到主进程 + * 处理并转发错误到主进程 */ - const sendError = (error: any, componentInfo?: string, additionalInfo?: Record) => { - const errorDetail = formatError(error, 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) { @@ -137,7 +144,7 @@ const createRendererErrorHandler = (): IRendererErrorHandler => { } } - // 通过logger直接记录错误,不再使用IPC通信 + // 记录完整的错误信息 logger[level](namespace, JSON.stringify(errorDetail)) // 同时在控制台输出错误信息 @@ -145,54 +152,30 @@ const createRendererErrorHandler = (): IRendererErrorHandler => { 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 = () => { - // 捕获未处理的异常 - window.addEventListener("error", event => { - event.preventDefault() - sendError(event.error || event.message, "window.onerror", { - filename: event.filename, - lineno: event.lineno, - colno: event.colno, - }) - return true - }) - - // 捕获未处理的Promise拒绝 - window.addEventListener("unhandledrejection", event => { - event.preventDefault() - sendError(event.reason, "unhandledrejection", { - promise: "[Promise]", // 不能直接序列化Promise对象 - }) - return true - }) - - // 捕获资源加载错误 - document.addEventListener( - "error", - event => { - // 只处理资源加载错误 - if (event.target && (event.target as HTMLElement).tagName) { - const target = event.target as HTMLElement - sendError(`Resource load failed: ${(target as any).src || (target as any).href}`, "resource.error", { - tagName: target.tagName, - src: (target as any).src, - href: (target as any).href, - }) - } - }, - true, - ) // 使用捕获阶段 - - logger.info("[ErrorHandler] Global error handlers installed") + // 不再在preload层安装全局错误处理器 + // 仅记录日志表明该方法被调用 + logger.info("[ErrorHandler] Global error handlers should be installed in renderer process") } return { - captureError: sendError, + captureError: handleError, setOptions: (newOptions: Partial) => { options = { ...options, ...newOptions } // 同步选项到主进程 @@ -204,10 +187,9 @@ const createRendererErrorHandler = (): IRendererErrorHandler => { } const errorHandler = createRendererErrorHandler() -errorHandler.installGlobalHandlers() // 暴露错误处理器到渲染进程全局 -contextBridge.exposeInMainWorld("errorHandler", errorHandler) +contextBridge.exposeInMainWorld("preloadErrorHandler", errorHandler) // 导出类型定义,方便在渲染进程中使用 -export type { IRendererErrorHandler } +export type { IRendererErrorHandler, ErrorDetail, ErrorHandlerOptions } diff --git a/packages/logger/renderer-error.ts b/packages/logger/renderer-error.ts new file mode 100644 index 0000000..6d0b6a8 --- /dev/null +++ b/packages/logger/renderer-error.ts @@ -0,0 +1,243 @@ +import { LogLevel, LogLevelName } from "./common" + +/** + * 错误详情接口 + */ +interface ErrorDetail { + message: string + stack?: string + componentInfo?: string + additionalInfo?: Record + timestamp: string + type: string +} + +/** + * 错误处理配置 + */ +interface ErrorHandlerOptions { + namespace?: string + level?: LogLevel + includeStack?: boolean + includeComponentInfo?: boolean +} + +/** + * 渲染进程错误处理接口 + */ +export 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", + } + console.log(error) + + // 处理不同类型的错误 + 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 +} + +// @ts-ignore +const preloadErrorHandler = window.preloadErrorHandler + +/** + * 创建渲染进程错误处理器 + */ +export const createRendererErrorHandler = (): IRendererErrorHandler => { + // 当前错误处理选项 + let options: ErrorHandlerOptions = { ...DEFAULT_OPTIONS } + + /** + * 处理错误并序列化 + */ + const processError = (error: any, componentInfo?: string, additionalInfo?: Record): ErrorDetail => { + const errorDetail = formatError(error, options) + + // 添加组件信息 + if (options.includeComponentInfo && componentInfo) { + errorDetail.componentInfo = componentInfo + } + + // 添加额外信息 + if (additionalInfo) { + errorDetail.additionalInfo = { + ...errorDetail.additionalInfo, + ...additionalInfo, + } + } + + return errorDetail + } + + /** + * 发送错误到preload层 + */ + const sendError = (error: any, componentInfo?: string, additionalInfo?: Record) => { + // 处理并序列化错误 + const errorDetail = processError(error, componentInfo, additionalInfo) + + // 调用window.errorHandler.captureError发送错误 + // 这里假设preload层已经暴露了errorHandler对象 + if (preloadErrorHandler && typeof preloadErrorHandler.captureError === "function") { + preloadErrorHandler.captureError(errorDetail) + } else { + // 如果errorHandler不可用,则降级到控制台输出 + console.error("[ErrorHandler]", errorDetail) + } + } + + /** + * 安装全局错误处理器 + */ + const installGlobalHandlers = () => { + // 捕获未处理的异常 + window.addEventListener("error", event => { + event.preventDefault() + sendError(event.error || event.message, "window.onerror", { + filename: event.filename, + lineno: event.lineno, + colno: event.colno, + }) + return true + }) + + // 捕获未处理的Promise拒绝 + window.addEventListener("unhandledrejection", event => { + event.preventDefault() + sendError(event.reason, "unhandledrejection", { + promise: "[Promise]", // 不能直接序列化Promise对象 + }) + return true + }) + + // 捕获资源加载错误 + document.addEventListener( + "error", + event => { + // 只处理资源加载错误 + if (event.target && (event.target as HTMLElement).tagName) { + const target = event.target as HTMLElement + sendError(`Resource load failed: ${(target as any).src || (target as any).href}`, "resource.error", { + tagName: target.tagName, + src: (target as any).src, + href: (target as any).href, + }) + } + }, + true, + ) // 使用捕获阶段 + + console.info("[ErrorHandler] Global error handlers installed") + } + + return { + captureError: sendError, + setOptions: (newOptions: Partial) => { + options = { ...options, ...newOptions } + // 同步选项到preload层 + if (preloadErrorHandler && typeof preloadErrorHandler.setOptions === "function") { + preloadErrorHandler.setOptions(options) + } + }, + getOptions: () => ({ ...options }), + installGlobalHandlers, + } +} + +// 导出类型定义,方便在渲染进程中使用 +export type { ErrorDetail, ErrorHandlerOptions } + +// 创建渲染进程错误处理器 +const errorHandler = createRendererErrorHandler() + +// 安装全局错误处理器 +errorHandler.installGlobalHandlers() + +window.errorHandler = errorHandler + +/** + * 使用示例: + * + * // 捕获特定错误 + * try { + * // 可能出错的代码 + * } catch (error) { + * errorHandler.captureError(error, 'ComponentName', { additionalInfo: 'value' }) + * } + * + * // 设置错误处理选项 + * errorHandler.setOptions({ + * namespace: 'custom-error', + * includeComponentInfo: true + * }) + */ diff --git a/src/renderer/src/main.ts b/src/renderer/src/main.ts index e5db733..422f456 100644 --- a/src/renderer/src/main.ts +++ b/src/renderer/src/main.ts @@ -1,4 +1,4 @@ - +import 'logger/renderer-error' import "simplebar-vue/dist/simplebar.min.css" import "@unocss/reset/normalize.css" import "@/assets/style/_common.scss"