
Effect 错误处理增强为Effect.ignore/Effect.ignoreCause定制失败忽略日志【免费下载链接】effectBuild production-ready applications in TypeScript项目地址: https://gitcode.com/GitHub_Trending/ef/effect本文基于当前仓库 .changeset/pre/lucky-buttons-jump.md 记录的变更展开effect包以 patch 版本为Effect.ignore与Effect.ignoreCause新增可选的message字段使开发者在忽略失败结果的同时能以自定义消息输出结构化的失败原因日志。读完本文你将掌握这两个 API 的完整选项语义、与源码实现对应的日志行为以及如何在尽力而为的业务场景中既不吞掉错误信号、又能留下可观测的日志证据。一、变更速览一个 patch 带来的可观测性提升本次变更记录位于 .changeset/pre/lucky-buttons-jump.md原文只有一条摘要Add an optionalmessagefield toEffect.ignoreandEffect.ignoreCausefor custom log output.它属于effect包的patch级变更意味着对现有 API 完全向后兼容——Effect.ignore与Effect.ignoreCause原有的调用方式不变只是新增了message选项用于在启用日志输出时给日志消息添加自定义前缀。这两个 API 的历史版本信息可从公共类型定义确认Effect.ignore 自2.0.0起存在Effect.ignoreCause 自4.0.0起存在而message字段是本次 patch 新增的能力。二、核心语义ignore 与 ignoreCause 有何不同在深入新选项之前先明确这两个 API 的根本区别这决定了你该在什么场景选谁。2.1 Effect.ignore只丢弃类型化失败Effect.ignore 的文档注释为 Discards both the success and failure values of an effect即同时丢弃成功值和类型化失败值typed failure即通过Effect.fail产生的错误把结果收敛为Effectvoid, never, Rimport { Effect } from effect // ┌─── Effectnumber, string, never // ▼ const task Effect.fail(Uh oh!).pipe(Effect.as(5)) // ┌─── Effectvoid, never, never // ▼ const program task.pipe(Effect.ignore) Effect.runSync(program) // undefined它适用于只想跑副作用、成败都不关心的场景但不会吞掉缺陷defect与中断interruption——这些非类型化失败仍然会继续传播避免掩盖程序级异常。2.2 Effect.ignoreCause忽略整个失败原因含缺陷与中断Effect.ignoreCause 的语义更彻底忽略 effect 的整个 Cause包括 defects 和 interruptions适合尽力而为、绝不能失败的任务import { Effect } from effect const task Effect.fail(boom) const program task.pipe(Effect.ignoreCause) Effect.runSync(program) // undefined从类型签名看两者的返回类型一致都是Effectvoid, never, R差异全部体现在对失败原因的处理粒度上。这一区别在源码实现与测试中均有对应证据详见下文第四、五节。三、新能力详解log 与 message 选项本次变更让两个 API 都支持一个可选参数对象其完整形状取自 Effect.ts 的类型定义{ readonly log?: boolean | Severity | undefined readonly message?: string | undefined }选项类型默认值行为说明logboolean \| Severityundefined不记录true时按当前日志级别输出传入Severity时按指定级别输出false或省略时不输出任何日志messagestringundefined在输出日志时前置的自定义消息需要配合log开启时才有意义其中Severity类型定义在 packages/effect/src/LogLevel.tsexport type Severity Fatal | Error | Warn | Info | Debug | Trace3.1 调用形态两种参数位置与 effect 中多数双参算子一致ignore/ignoreCause同时支持数据优先data-first与数据最后data-last两种调用方式import { Effect } from effect // 数据优先options 放在 effect 之前 const program1 Effect.ignore( { log: true, message: Ignoring failure }, Effect.fail(boom) ) // 数据最后通过 pipe 链式调用推荐 const program2 Effect.fail(boom).pipe( Effect.ignore({ log: true, message: Ignoring failure }) )3.2 组合示例忽略失败并留下可检索日志message的核心价值在于当log开启时你可以在海量日志中快速定位哪些失败被有意忽略以及发生在哪个业务环节import { Effect } from effect const cleanup Effect.fail(connection already closed).pipe( Effect.ignore({ log: true, // 按当前日志级别输出 message: [cleanup] ignoring failure, }) ) // 若希望固定为某个级别直接传入 Severity 字符串 const cleanupWarn Effect.fail(connection already closed).pipe( Effect.ignore({ log: Warn, message: [cleanup] ignoring failure, }) )在测试日志里message会作为日志消息数组的独立元素出现测试断言见第四节随后紧跟被忽略的 Cause 信息便于后续排查。四、源码级剖析message 如何参与日志输出要理解message的行为边界需要看内部实现。两个算子的实现都位于 packages/effect/src/internal/effect.ts。4.1 Effect.ignore 的实现export const ignore: ... dual( (args) isEffect(args[0]), (self, options?) { // 未开启 log直接用 matchEffect 忽略类型化失败 if (!options?.log) { return matchEffect(self, { onFailure: (_) void_, onSuccess: (_) void_ }) } const logEffect logWithLevel(options.log true ? undefined : options.log) return matchCauseEffect(self, { onFailure(cause) { const failure findFail(cause) return Result.isFailure(failure) ? failCause(failure.failure) // 无类型化失败纯缺陷/中断→ 继续传播 : options.message undefined ? logEffect(cause) // 仅记录 Cause : logEffect(options.message, cause) // message 前置 Cause }, onSuccess: (_) void_ }) } )几个关键点log未开启时走matchEffect分支只匹配类型化失败行为与旧版完全一致log开启时改用matchCauseEffect以便拿到完整 Cause并通过 findFail在 Cause 的reasons列表中查找Fail原因区分类型化失败与缺陷/中断如果 cause 中没有类型化失败Result.isFailure为真即纯缺陷或中断则调用failCause原样重新抛出保证缺陷仍然可见只有存在类型化失败时才记录日志并忽略options.log true ? undefined : options.log传给logWithLeveltrue表示使用当前日志级别CurrentLogLevel传入Severity则使用指定级别这正是 logWithLevel 的职责。4.2 Effect.ignoreCause 的实现export const ignoreCause: ... dual( (args) isEffect(args[0]), (self, options?) { if (!options?.log) { return matchCauseEffect(self, { onFailure: (_) void_, onSuccess: (_) void_ }) } const logEffect logWithLevel(options.log true ? undefined : options.log) return matchCauseEffect(self, { onFailure: (cause) options.message undefined ? logEffect(cause) : logEffect(options.message, cause), onSuccess: (_) void_ }) } )与ignore最大的差别在于不做findFail判定无论 Cause 中包含什么类型化失败、缺陷、中断只要开启log就完整记录并忽略。message的接入方式相同——作为logEffect的第一个参数前置到日志中。4.3 logWithLevel 如何组装日志logWithLevel 会扫描消息参数把其中的Cause实例从消息数组中提取出来单独作为cause字段传递其余参数含我们的message字符串构成message数组。因此最终 Logger 收到的选项形如{ logLevel: Warn, // 由 log 选项决定 cause: Cause..., // 被忽略的失败原因 message: [[cleanup] ignoring failure] }这也解释了为什么测试中断言logs[0].message深等于[Ignoring failure]——自定义消息是独立元素结构化的 Cause 另行存放。五、测试验证行为边界的完整覆盖packages/effect/test/Effect.test.ts 中为Effect.ignore与Effect.ignoreCause各建了一个describe块使用自定义测试 Logger 捕获{ logLevel, cause, message }三元组逐项验证新行为场景ignoreignoreCause断言要点省略log不输出日志不输出日志logs.length 0log: false不输出日志不输出日志logs.length 0log: true按当前级别输出按当前级别输出logLevel等于当前CurrentLogLevel如Warn且cause中能断言到boomlog: Error按指定级别输出按指定级别输出logLevel Error即使当前级别为Warnlog: truemessage前置自定义消息前置自定义消息message深等于[Ignoring failure]/[Ignoring cause]缺陷 / 中断—全部忽略Effect.die(boom)、Effect.interrupt经ignoreCause后Exit.void此外ignoreCause有两条ignore不具备的专属用例忽略缺陷Effect.die(boom)转为Exit.void与忽略中断Effect.interrupt转为Exit.void与文档注释 including defects and interruptions 完全一致也从侧面印证了二者语义差异。六、典型应用场景与注意事项6.1 资源清理中的尽力而为连接关闭、临时文件删除、发送心跳等清理类操作失败不应影响主流程但值得留痕import { Effect } from effect const release (conn: Connection) conn.close().pipe( Effect.ignore({ log: Debug, message: connection close failed }) )6.2 主动忽略可预期缺陷某些第三方库会以异常/缺陷形式暴露预期内的失败。若确认可安全忽略用ignoreCause连同缺陷一起吞掉同时保留日志const task Effect.die(new Error(expected transient failure)).pipe( Effect.ignoreCause({ log: Warn, message: transient failure ignored }) )6.3 注意事项message依赖log省略log或log: false时message不会产生任何输出实现中直接走matchEffect/matchCauseEffect的静默分支ignore不会吞缺陷开启log时若 Cause 中无类型化失败缺陷/中断会被重新抛出failCause这是刻意保留的错误信号需要连缺陷一起忽略请使用ignoreCause日志级别语义log: true使用当前日志级别可被CurrentLogLevel服务调整指定Severity字符串则强制使用该级别便于在信息流中统一过滤被忽略的失败向后兼容本次为patch级变更Effect.ignore与Effect.ignoreCause的既有用法与返回类型均未改变升级effect依赖后即可直接使用message选项。七、小结Effect.ignore与Effect.ignoreCause是 effect 错误处理工具箱中丢弃失败的一对算子本仓库 .changeset/pre/lucky-buttons-jump.md 记录的 patch 为它们补齐了最后一块可观测性拼图——message字段。结合 公共类型定义、内部实现 与 测试用例你现在可以精确控制哪些失败被忽略、以什么级别记录、带什么上下文消息让静默吞错变成有据可查的刻意忽略。【免费下载链接】effectBuild production-ready applications in TypeScript项目地址: https://gitcode.com/GitHub_Trending/ef/effect创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考