Cherry Studio 统一日志服务 LoggerService 使用指南:主进程、渲染进程与 Worker 的完整实践

Cherry Studio 统一日志服务 LoggerService 使用指南:主进程、渲染进程与 Worker 的完整实践 人工智能大模型AI 应用交互助手本地部署【免费下载链接】cherry-studio Cherry Studio 是一款支持多个 LLM 提供商的桌面客户端项目地址https://gitcode.com/CherryHQ/cherry-studio点击查看免费下载本文是 Cherry Studio多 LLM 提供商桌面客户端开发者文档《How to use the LoggerService》docs/references/logging/README.md的完整技术指南。它讲解如何通过统一的LoggerService在主进程与渲染进程打印和记录日志涵盖日志级别、模块上下文CONTEXT、窗口来源window source、环境变量过滤以及 Worker 线程下的特殊用法。读完本文你将掌握 Cherry Studio 中禁止随意console.xxx、统一走loggerService的工程约定并能正确地在任意进程、任意窗口、任意日志级别下输出既利于终端排查又利于文件归档的日志。为什么要有统一的 LoggerServiceCherry Studio 是一个 Electron 桌面应用代码横跨主进程main、渲染进程renderer和Worker 线程三类执行环境。如果各模块各自为政地调用console.log会出现三个问题日志散落各处没有统一的级别过滤与模块标记难以在海量输出中定位目标模块渲染进程的日志无法自动汇聚到主进程统一落盘排查问题时缺少跨进程的完整时间线无法按模块、按级别做开发期过滤也无法在生产构建中按需开启详细日志。因此项目约定了统一的日志服务除非有特殊理由不要使用console.xxx打印日志。在代码中应始终通过loggerService记录核心实现位于 主进程 LoggerService 与 渲染进程 LoggerService二者共享同一套日志级别与上下文类型定义src/shared/types/logger.ts并通过logger别名在 electron.vite.config.ts 中分别指向主进程与渲染进程的实现主进程解析到src/main/core/logger/LoggerService渲染进程解析到src/renderer/services/LoggerService。主进程中的用法导入在主进程代码中导入统一导出的单例import { loggerService } from logger该单例在模块加载时即被创建LoggerService.ts 末尾loggerService是new LoggerService()的全局实例可直接使用。设置模块信息按约定必需导入语句之后请按如下方式建立带模块上下文的 loggerconst logger loggerService.withContext(moduleName)moduleName是当前文件的模块名可用文件名、主类名或主函数名命名原则是清晰易懂。moduleName会打印在终端中也会写入文件日志便于过滤。设置CONTEXT信息可选withContext还可以携带其他CONTEXT信息const logger loggerService.withContext(moduleName, CONTEXT)CONTEXT是形如{ key: value, ... }的对象。CONTEXT不会打印在终端中但会记录到文件日志里方便过滤。从源码看主进程实现withContext通过Object.create(this)派生新实例并浅拷贝合并上下文因此不会污染全局单例渲染进程的实现逻辑完全相同src/renderer/services/LoggerService.ts#L120-L128。记录日志在代码任意位置调用logger即可记录日志支持的级别有error、warn、info、verbose、debug、silly各级别含义见后文日志级别使用规范。日志方法的签名以logger.LEVEL为例支持以下几种调用形式logger.LEVEL(message) logger.LEVEL(message, CONTEXT) logger.LEVEL(message, error) logger.LEVEL(message, error, CONTEXT) logger.LEVEL(message, CONTEXT, MORE_CONTEXT)参数类型说明messagestring必填。日志的核心字段包含要记录的主要内容。CONTEXTobject可选。要记录到日志文件中的附加信息推荐使用{ key: value, ... }格式。errorError可选。错误堆栈也会被打印。注意catch(error)捕获到的error是unknown类型按照 TypeScript 最佳实践应先用instanceof做类型收窄若确定是Error类型也可使用as Error断言。在底层实现中主进程 LoggerService.ts主进程日志会走processMainLog继而进入processLog首个参数若为Error其stack会被展开进文件日志条目消息文本追加error.message后续对象参数会合并进条目的data字段。测试用例也验证了这一点LoggerService.test.tswithContext(ScanTest).error(boom, error, { requestId: r1 })写出的 JSON 行中module、process、level、stack以及requestId、error.code等调用方数据全部保留。记录非object类型的上下文信息对于非对象类型的附加信息直接在消息中拼接即可避免传入非对象参数const foo getFoo() logger.debug(foo ${foo})日志级别与默认行为开发环境所有级别的日志都会打印到终端并记录到文件日志中。生产环境默认日志级别为info日志只记录到文件不打印到终端。从源码看主进程的默认级别定义为DEV_LOGGING ? LEVEL.SILLY : LEVEL.INFOLoggerService.ts其中DEV_LOGGING isDev || DIAGNOSTICS_ENABLED——即开发环境或开启了CS_DIAGNOSTICS诊断模式时文件日志级别直接放宽到silly。动态修改日志级别可以通过以下方法修改、重置和查询日志级别logger.setLevel(newLevel) logger.resetLevel() logger.getLevel()setLevel(newLevel)把最小日志级别改为newLevel低于该级别的日志被丢弃。resetLevel()重置为默认级别。getLevel()获取当前日志级别。注意修改日志级别是全局生效的。除非你非常清楚自己在做什么否则不要在代码中随意修改。源码中setLevel直接改写底层 winston logger 的level属性LoggerService.ts而resetLevel恢复到DEFAULT_LEVELLoggerService.ts。渲染进程中的用法渲染进程中导入、设置模块信息、设置上下文信息的方式与主进程完全一致。下面的内容重点说明差异。窗口来源Window source渲染进程存在多个不同的window主窗口、截图窗口、子窗口、选区工具栏等因此每条日志都需要记录它来自哪个窗口。每个窗口在其index.html中以声明式方式声明来源meta namelogger-window-source contentmainWindow /LoggerService在构造时会读取这个 meta 标签。由于meta在任意模块脚本运行之前就被解析因此在entryPoint.tsx中不需要任何顺序约定import 期的日志也能拿到正确的来源。content值会记录到主进程终端和文件日志中不会打印在devTool的console中。若窗口没有 meta 标签也没有显式覆盖早期日志回退为UNKNOWN并打印一条console.error提示。渲染进程的 meta 解析由独立的resolveWindowSourceFromMeta函数完成src/renderer/services/LoggerService.ts#L33-L36对应测试覆盖了有 meta、无 meta、worker 无 document、空白 content四种情况渲染进程 LoggerService 测试。实际窗口 HTML 示例见 src/renderer/windows/main/index.html。initWindowSource显式覆盖对于没有 document 的上下文如 Worker以及任何特殊场景需要显式设置来源。显式设置会覆盖从 meta 推导出的来源loggerService.initWindowSource(Worker)只能设置一次后续调用无效会打印一条console.warn提示见 渲染进程实现。该方法返回LoggerService实例本身支持链式调用。在渲染进程的processLog中窗口来源的优先级是显式initWindowSource()meta推导 UNKNOWN兜底src/renderer/services/LoggerService.ts#L137-L142。测试用例lets an explicit initWindowSource override the derived source验证了显式覆盖的行为测试文件。日志级别开发环境默认所有级别都打印到devTool的console。生产环境默认日志级别为info日志打印到devTool的console。在开发和生产环境中warn与error级别的日志默认会传输到主进程并记录到文件日志。开发环境下主进程终端也会打印从渲染进程传来的日志。修改日志级别与主进程相同可使用setLevel(level)、resetLevel()、getLevel()管理日志级别同样地修改日志级别属于全局调整。修改传输到main的级别渲染进程的日志会被发送到主进程由主进程集中管理并落盘遵循主进程的文件日志级别。默认情况下只有warn与error级别的日志会传输到主进程源码常量MAIN_LOG_LEVEL LEVEL.WARN见 src/renderer/services/LoggerService.ts#L18。有两种方式可以修改传输到主进程的日志级别全局修改分别使用以下方法设置、重置、查询传输到主进程的日志级别logger.setLogToMainLevel(newLevel) logger.resetLogToMainLevel() logger.getLogToMainLevel()注意该方法全局生效请勿随意修改。单条日志强制传输在日志调用末尾追加{ logToMain: true }可以强制单条日志绕过全局传输级别限制、直接传输到主进程例如logger.info(message, { logToMain: true })从源码看渲染进程会检测data末尾是否为{ logToMain: true }命中则强制触发 IPC 转发并在转发前把该标记从data中剔除避免污染落盘内容src/renderer/services/LoggerService.ts#L184-L209。转发通过IpcChannel.App_LogToMain通道调用window.electron.ipcRenderer.invoke完成主进程侧在构造 LoggerService 时注册了对应 handler主进程实现把渲染进程的LogSourceWithContext、级别、消息与数据送入统一的processLog落盘。主进程测试用例preserves renderer window/module when the forwarded log carries data验证了这条链路主进程测试。关于worker线程主进程中的 Worker目前不支持日志主进程 LoggerService 构造时若处于非主线程会直接抛错见 LoggerService.ts。渲染进程中启动的 Worker支持日志但当前这些日志不会发送到主进程落盘源码中if (!IS_WORKER)分支才走 IPC 转发worker 分支留有待办TODO support worker to send log to main process见 渲染进程实现。如何在渲染进程的 Worker 中使用日志由于 Worker 线程相互独立在 Worker 中使用 LoggerService 等价于在新的渲染进程窗口中使用。因此必须先调用initWindowSourceloggerService.initWindowSource(Worker)如果 Worker 比较简单只有一个文件也可以直接使用方法链const logger loggerService.initWindowSource(Worker).withContext(LetsWork)用环境变量过滤日志在开发环境中可以定义环境变量按级别和模块过滤终端显示的日志帮助开发者聚焦自己的日志、提升开发效率。环境变量可以在终端中设置也可以定义在项目根目录的.env文件中。可用变量如下变量名说明CSLOGGER_MAIN_LEVEL主进程日志级别低于该级别的日志不会显示。CSLOGGER_MAIN_SHOW_MODULES过滤主进程的日志模块用逗号,分隔多个模块过滤区分大小写只显示列表内模块的日志。CSLOGGER_RENDERER_LEVEL渲染进程日志级别低于该级别的日志不会显示。CSLOGGER_RENDERER_SHOW_MODULES过滤渲染进程的日志模块用逗号,分隔多个模块过滤区分大小写只显示列表内模块的日志。示例CSLOGGER_MAIN_LEVELverbose CSLOGGER_MAIN_SHOW_MODULESMcpService,SelectionService注意默认情况下这些变量只在开发环境生效。若要在打包构建中启用请以CS_DIAGNOSTICS启动应用——此时 logger 的行为与开发环境完全一致文件级别放宽到 verbose、打开终端输出、上述覆盖变量全部生效详见 性能诊断文档。这些变量只影响终端或 DevTools 中显示的日志不影响文件日志也不影响logToMain的记录逻辑。从源码看主进程在构造时读取CSLOGGER_MAIN_LEVEL与CSLOGGER_MAIN_SHOW_MODULESLoggerService.ts渲染进程读取CSLOGGER_RENDERER_*渲染进程实现且仅在DEV_LOGGING开发或CS_DIAGNOSTICS模式下解析过滤逻辑发生在processLog的终端输出分支中先按级别、再按模块列表做短路跳过因此不会影响文件写入。日志级别使用规范Cherry Studio 中日志级别较多以下是各级别应当遵循的使用准则按从高到低排列日志级别核心定义与使用场景示例error导致程序崩溃或核心功能不可用的严重错误。最高优先级日志通常需要立即上报或通知用户。- 主进程或渲染进程崩溃。- 关键用户数据文件如数据库、配置文件读写失败导致应用无法运行。- 所有未捕获的异常。warn不影响程序核心功能的潜在问题或意外情况。程序可以恢复或使用降级方案。- 配置文件settings.json缺失使用默认设置启动。- 自动更新检查失败但不影响当前版本使用。- 非必需插件加载失败。info记录应用生命周期事件和关键用户操作。生产版本应记录到文件的默认级别用于追踪用户主要操作路径。- 应用启动、退出。- 用户成功打开/保存文件。- 主窗口创建/关闭。- 启动重要任务如开始视频导出。debug常规开发与调试时有用的诊断信息。默认低于生产的info阈值。- 选中的执行分支。- 安全标识符与计时细节。- 缓存命中/未命中。verbose比debug更详细的执行流程追踪。用于通常被隐藏的、嘈杂的逐步诊断。- 加载 UI 模块。- 某个 IPC 路由被调用。- 多步骤操作中的各个阶段。silly最详细、最低层的信息仅用于极端调试。常规开发中很少使用只用于解决非常棘手的难题。- 实时鼠标坐标(x: 150, y: 320)。- 读取文件时每个数据块的大小。- 每帧渲染耗时。底层实现中级别由数值映射定义src/shared/types/logger.ts#L24-L32error10、warn8、info6、debug4、verbose2、silly0、none-1。过滤判断即比较数值大小例如生产环境默认info级别意味着数值小于 6 的debug、verbose、silly全部被丢弃。文件日志的底层实现与归档策略理解文件日志的生成机制有助于排查问题。主进程 LoggerService 基于winston构建并注册两类按天轮转的DailyRotateFile传输LoggerService.ts通用日志写入logsDir/app.%DATE%.logdatePattern为YYYY-MM-DD单文件上限10m最多保留30d错误日志级别门槛warn写入logsDir/app-error.%DATE%.log单文件上限10m最多保留60d。日志目录来自核心早期常量模块LOGS_DIRsrc/main/core/paths/constants与BootConfigService、pathRegistry共享同一数据源保证日志目录、用户数据目录等路径在应用内处处一致。文件格式为带YYYY-MM-DD HH:mm:ss时间戳的 JSONwinston.format.json()errors({ stack: true })便于后续诊断收集器解析测试用例验证了时间戳可被Date.parse解析见 主进程测试。另外error与warn级别的文件日志会自动附带系统信息与版本号sys操作系统、硬件、CPU 型号、总内存与appver应用版本用于问题定位时快速核对运行环境LoggerService.ts。对应测试adds sys/appver on warn and error but not on info确认了info级别不会携带这些字段主进程测试。安全红线不要记录敏感信息无论使用哪个级别都不得记录密钥、授权头authorization headers、完整提示词或用户文件内容。因为低级别日志在开发环境以及开启诊断模式CS_DIAGNOSTICS的生产构建中仍然会写入文件敏感信息一旦落盘便可能随日志归档长期留存构成数据泄露风险。常见问题速查终端看不到自己的日志检查当前级别是否被setLevel或CSLOGGER_*环境变量过滤开发/诊断模式下可用CSLOGGER_MAIN_SHOW_MODULES/CSLOGGER_RENDERER_SHOW_MODULES精确指定模块。渲染进程的日志没有出现在主进程日志文件中默认只有warn/error会上传若要上传更细级别全局用setLogToMainLevel单条用{ logToMain: true }。Worker 中报window source not initializedWorker 没有 document必须先调用loggerService.initWindowSource(Worker)可直接链式withContext。生产构建如何拿到完整日志以CS_DIAGNOSTICS1启动打包后的可执行文件logger 即按开发模式行为运行verbose 文件级别 终端输出 环境变量过滤全部开启详见 docs/references/diagnostics/README.md。综上LoggerService是 Cherry Studio 中所有日志的唯一入口主进程侧统一落盘与终端着色输出渲染进程侧通过 meta 标签与 IPC 通道把关键日志回传主进程归档开发者再借助模块上下文与环境变量过滤实现精准排障。遵循本文的级别规范与安全红线即可在跨进程的复杂 Electron 架构中保持日志的整洁、可过滤与可追溯。赞分享人工智能大模型AI 应用交互助手本地部署【免费下载链接】cherry-studio Cherry Studio 是一款支持多个 LLM 提供商的桌面客户端项目地址https://gitcode.com/CherryHQ/cherry-studio点击查看免费下载相关推荐Cherry Studio架构深度解析主进程与渲染进程如何构建高效AI桌面应用Cherry Studio架构深度解析主进程与渲染进程如何构建高效AI桌面应用 Cherry Studio是一款支持多个LLM提供商的桌面客户端采用Elec人工智能大模型AI 应用交互助手本地部署Cherry Studio 统一日志体系解析LoggerService 的分级、上下文与跨进程转发机制Cherry Studio 统一日志体系解析LoggerService 的分级、上下文与跨进程转发机制 Cherry Studio一款面向 AI 场景的桌面AI 应用大模型桌面应用本地部署RAGElectron 进程模型完全指南主进程、渲染进程与工具进程的架构与实践Electron 进程模型完全指南主进程、渲染进程与工具进程的架构与实践 Electron 的多进程架构直接继承自 Chromium因此每个 Electro桌面应用跨平台前端上一篇Kubernetes The Hard Way 如何在 Jumpbox 上下载并整理 K8s 组件二进制包下一篇Helicone认证系统用户管理与权限控制创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考