Activepieces 代码审查指南:用 evlog 宽事件与结构化错误重构日志模式
发布时间:2026/9/13 4:41:31来源:尧图网络
Activepieces 代码审查指南用 evlog 宽事件与结构化错误重构日志模式【免费下载链接】activepiecesAI Agents MCPs AI Workflow Automation • (~400 MCP servers for AI agents) • AI Automation / AI Agent with MCPs • AI Workflows AI Agents • MCPs for AI Agents项目地址: https://gitcode.com/GitHub_Trending/ac/activepieces本篇指南基于 Activepieces 仓库内.agents/skills/review-logging-patterns技能附带的代码审查清单code-review.md整理而成面向需要在 TypeScript/JavaScript 代码库中审查并改进日志与错误处理模式的开发者。你将掌握一套可立即落地的审查流程扫描console.log泛滥、识别无上下文的泛化错误、为请求处理器补齐请求级日志并学会把散落的日志与错误转换为 evlog 的「宽事件wide event」与「结构化错误structured error」让每条日志都自带可排障的完整上下文。背景为什么要审查日志模式Activepieces 服务端本身已在生产代码中实践了这套日志体系。在 packages/server/api/src/app/helper/logger/index.ts 中可以看到API 服务通过evlogSetup.init()初始化全局日志门面支持LOG_LEVEL、LOG_PRETTY、LOG_SAMPLE_RATE_INFOinfo 级采样率、LOG_KEEP_SLOW_MS慢请求保留阈值等环境变量并可通过HYPERDX_TOKEN、AXIOM_TOKEN、AXIOM_DATASET、LOKI_URL、BETTERSTACK_TOKEN、OTEL_ENABLED、LOG_FILE等配置把事件投递到 HyperDX、Axiom、Loki、BetterStack、OTLP 或本地文件// packages/server/api/src/app/helper/logger/index.ts节选 return evlogSetup.init({ params: { serviceName: activepieces-api, version: apVersionUtil.getCurrentRelease(), logLevel, logPretty, sampleRateInfo, keepSlowMs, drainConfig: { hyperdxToken: environmentVariables.getEnvironment(AppSystemProp.HYPERDX_TOKEN), axiomToken: environmentVariables.getEnvironment(AppSystemProp.AXIOM_TOKEN), lokiUrl: environmentVariables.getEnvironment(AppSystemProp.LOKI_URL), betterstackToken: environmentVariables.getEnvironment(AppSystemProp.BETTERSTACK_TOKEN), otlpEnabled: environmentVariables.getEnvironment(AppSystemProp.OTEL_ENABLED) true, }, }, })同时Activepieces 使用 Fastify 的FastifyBaseLogger作为日志门面类型例如 action-run.service.ts 中import { FastifyBaseLogger } from fastify并通过log.child({ flowRunId, webhookId, flowId, flowVersionId })为每次流程运行、每个 Webhook 请求创建携带业务上下文的子日志器——这与本指南要讲的「在请求/任务作用域内累积上下文、最终一次性输出」的理念一脉相承。理解了这个背景下面进入完整的审查清单。一、快速扫描Quick Scan三分钟定位问题开始正式审查前先用下面三项快速检查确定改进机会。它们覆盖了最常见的三类日志问题散落的 console 语句、无上下文的错误、缺失日志的请求处理器。1. Console 语句审计在代码库中搜索以下模式// ❌ 需要查找并改造的模式 console.log(...) console.error(...) console.warn(...) console.info(...) console.debug(...)需要追问的问题同一个函数里是否存在多条 console 语句它们是否在记录请求/响应数据是否可以把它们合并成一个宽事件wide event关于宽事件的完整概念为什么一次请求只应输出一条包含全部上下文的日志参见仓库内的 wide-events.md。2. 错误模式审计搜索以下模式// ❌ 泛化错误 throw new Error(...) throw Error(...) // ❌ 无上下文的重新抛出 catch (error) { throw error } // ❌ 先记录再抛出重复记录 catch (error) { console.error(error) throw error }需要追问的问题错误消息是否解释了「发生了什么」是否有why字段说明根本原因是否有fix字段给出解决方案建议原始错误是否作为cause被保留下来3. 请求处理器审计对每个 API 路由/处理器检查// ❌ 缺少请求上下文 export default defineEventHandler(async (event) { // 完全没有日志或者散落着 console.log })需要追问的问题是否有请求作用域request-scoped的 logger上下文是否在整个请求过程中被持续累积是否在请求结束时只输出一次single emit二、详细审查Detailed Review三类转换的完整范式快速扫描发现的问题按下述三类范式逐一改造。Console.log 转换单个调试日志// ❌ 改造前 console.log(Processing user:, userId) // ✅ 改造后 - 如果是更大操作的一部分 log.set({ user: { id: userId } }) // ✅ 改造后 - 如果是独立的调试信息 log.debug(user, Processing user ${userId})多条相关日志合并// ❌ 改造前 console.log(Starting checkout) console.log(User:, user.id) console.log(Cart items:, cart.items.length) console.log(Total:, cart.total) // ✅ 改造后 - 合并为一条宽事件 log.info({ action: checkout, user: { id: user.id }, cart: { items: cart.items.length, total: cart.total }, })请求生命周期日志// server/api/process.post.ts // ❌ 改造前 export default defineEventHandler(async (event) { console.log(Request started) const user await getUser(event) console.log(User loaded) const result await processData(user) console.log(Processing complete) return result }) // ✅ 改造后Nuxt - 自动导入无需 import // Nitro v3: import { useLogger } from evlog/nitro/v3 // Nitro v2: import { useLogger } from evlog/nitro export default defineEventHandler(async (event) { const log useLogger(event) const user await getUser(event) log.set({ user: { id: user.id } }) const result await processData(user) log.set({ result: { id: result.id } }) return result // emit() 自动触发 })注意在 Nuxt/Nitro 下emit()由框架在请求结束时自动调用而在独立 TypeScript 场景脚本、Worker下必须手动调用emit()参见 SKILL 中「Standalone TypeScript」一节的 SKILL.md。错误转换泛化错误// ❌ 改造前 throw new Error(Failed to create user) // ✅ 改造后 throw createError({ message: Failed to create user, why: Email address already registered, fix: Use a different email or log in to existing account, link: https://your-app.com/docs/registration, })无上下文的包装错误// ❌ 改造前 try { await externalApi.call() } catch (error) { throw new Error(API call failed) } // ✅ 改造后 try { await externalApi.call() } catch (error) { throw createError({ message: External API call failed, why: API returned: ${error.message}, fix: Check API credentials and try again, link: https://api-docs.example.com/errors, cause: error, }) }「先记录再抛出」反模式// ❌ 改造前 try { await riskyOperation() } catch (error) { console.error(Operation failed:, error) throw error } // ✅ 改造后 - 记录宽事件同时抛出带上下文的错误 try { await riskyOperation() } catch (error) { log.error(error, { step: riskyOperation }) throw createError({ message: Operation failed, why: error.message, fix: Check input and retry, cause: error, }) }请求处理器转换完全无日志的处理器// server/api/orders.post.ts // ❌ 改造前 export default defineEventHandler(async (event) { const body await readBody(event) const result await processOrder(body) return result }) // ✅ 改造后Nuxt - 自动导入无需 import // Nitro v3: import { useLogger } from evlog/nitro/v3 // Nitro v2: import { useLogger } from evlog/nitro import { createError } from evlog export default defineEventHandler(async (event) { const log useLogger(event) const body await readBody(event) log.set({ order: { items: body.items?.length } }) try { const result await processOrder(body) log.set({ result: { orderId: result.id, status: result.status } }) return result } catch (error) { log.error(error, { step: processOrder }) throw createError({ message: Order processing failed, why: error.message, fix: Check the order data and try again, }) } // emit() 自动触发 })三、审查清单汇总Review Checklist Summary审查收尾时逐项核对以下清单。日志Logging生产代码中不存在裸console.log语句请求处理器使用useLogger(event)Nuxt/Nitro或createRequestLogger()独立场景上下文通过log.set()在整个请求过程中持续累积emit()在使用useLogger()时自动触发使用createRequestLogger()时手动触发宽事件包含用户信息、业务上下文、结果outcome错误Errors所有错误使用createError()而非new Error()从evlog导入每个错误都有清晰的message和恰当的status状态码复杂错误包含解释根因的why可修复的错误包含可操作步骤的fix有文档的错误包含link指向文档包装错误保留原始错误的cause仅面向运维或敏感的诊断信息放在internal而非message/why/fixinternal字段只出现在服务端日志与 drain 输出中不会进入 HTTP 错误响应体也不会出现在客户端parseError()的结果中它存放在非枚举 Symbol 上JSON.stringify(error)不会泄露其内容。更完整的字段规范与错误模板参见 structured-errors.md。前端错误处理Frontend Error HandlingAPI 错误被捕获并以完整上下文展示message、why、fixToast 或错误组件使用error.data.data中的结构化数据文档链接可点击操作Toast 中的按钮/链接上下文Context用户上下文包含id、套餐/订阅、相关业务数据请求上下文包含method、path、requestId业务上下文与领域相关且对排障有用日志中不包含敏感数据密码、令牌、完整卡号四、反模式汇总表反模式修复方案一个函数内多条console.log用useLogger(event).set()累积为单条宽事件throw new Error(...)throw createError({ message, status, why, fix })console.error(e); throw elog.error(e); throw createError(...)请求处理器完全无日志添加useLogger(event)Nuxt/Nitro或createRequestLogger()独立场景扁平化日志数据分组对象{ user: {...}, cart: {...} }缩写字段名使用描述性命名用userId而不是uid五、可直接复用的审查评论模板审查者在 PR 评论中使用以下措辞能帮助作者快速理解改法。发现 Console.logConsider using evlogs wide event pattern here. Instead of multiple console.log statements, useuseLogger(event)to accumulate context and emit a single comprehensive event.泛化错误This error would benefit from evlogs structured error pattern. Consider usingimport { createError } from evlogandcreateError({ message, status, why, fix })to provide more debugging context.缺少请求上下文This handler would benefit from request-scoped logging. AdduseLogger(event)at the start to capture context throughout the request lifecycle.正向反馈日志质量好Nice use of wide events here! The context is well-structured and will be very useful for debugging.六、进阶审查后的落地与深化宽事件应包含哪些字段每条宽事件都应包含三层上下文详见 wide-events.md请求上下文method、path、requestId、traceId用于分布式追踪用户上下文user.id、user.plan、user.accountAge等与业务相关的字段业务上下文按领域补充如电商的cart、payment、order上传场景的upload.filename、size、mimeType。结果outcome通过status字段记录duration由emit()自动计算无需手动计时。安全红线显式选择要记录的字段// ❌ 危险 - 会把 password 也打出来 log.set({ user: body }) // ✅ 安全 - 显式选择字段 log.set({ user: { id: body.id, email: maskEmail(body.email), // password: body.password ← 永远不要包含 }, })永远不要记录密码、API 密钥、令牌、密钥、完整卡号、CVV、SSN、PII、会话令牌、JWT。生产环境排水管道与采样生产环境建议用createDrainPipeline包裹 drain 适配器以获得批处理、指数退避重试与缓冲区溢出保护并务必在服务close钩子中调用drain.flush()否则进程退出时缓冲的事件会丢失配置细节与反模式见 drain-pipeline.md。结合 Activepieces 的实践采样率与慢请求阈值这类策略都可以通过环境变量下发例如LOG_SAMPLE_RATE_INFO控制 info 级事件的采样百分比LOG_KEEP_SLOW_MS控制超过多少毫秒的请求必须保留——这正对应 evlog 的 head samplingsampling.rates与 tail samplingsampling.keep能力。错误如何流向 HTTP 响应与前端后端只需throw createError(...)框架会自动把结构化字段转成 HTTP 响应前端用parseError()提取message、why、fix、link后既可展示在 Toast 中也可把link渲染为可点击的「了解更多」按钮——让用户看到的不是一句「出错了」而是发生了什么、为什么、怎么解决示例见 structured-errors.md。结语让审查有据可依把这份清单作为每次涉及日志与错误处理改动的 PR 审查基线先快速扫描三类高频问题再按「宽事件 结构化错误」两个范式逐例改造最后对照汇总清单逐项验收。配合仓库内的 wide-events.md、structured-errors.md 与 drain-pipeline.md 三份参考文档以及 SKILL.md 中各框架Nuxt、Next.js、Nitro、Express、Fastify、Hono、NestJS 等的接入方式你就能把「散落的 console 日志」逐步收敛为「一条宽事件讲清一次请求」把「一句 failed」升级为「message why fix cause 俱全」的可诊断错误。【免费下载链接】activepiecesAI Agents MCPs AI Workflow Automation • (~400 MCP servers for AI agents) • AI Automation / AI Agent with MCPs • AI Workflows AI Agents • MCPs for AI Agents项目地址: https://gitcode.com/GitHub_Trending/ac/activepieces创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
网站建设高端定制企业官网