覆盖 CurrentLogAnnotations 为什么会丢注解?
我在外层用 Effect.annotateLogs 加了 b: 2,内层用 provideService 覆盖了 CurrentLogAnnotations 为 { a: 1 },最后那条日志只剩 { a: 1 },b: 2 不见了。
因为直接覆盖 Reference 是整体替换,而 Effect.annotateLogs 是在当前值上合并。想「加几个注解」用 annotateLogs,只有确实要清空或整体换掉时才直接覆盖。
CurrentLogAnnotations 是 effect 的 References 模块里的内置 Reference,也就是带默认值的服务键,默认是空对象,概念见 Effect v4 里 FiberRef 去哪了?。这个差别是用原始 Reference 时最容易犯的错:它给你的是最底层的赋值,不是合并。
三种写法分别会输出什么?
import { Effect, References } from "effect"
const program = Effect.gen(function*() {
console.log("-- CurrentLogAnnotations 直接覆盖")
yield* Effect.provideService(Effect.logInfo("hello"), References.CurrentLogAnnotations, { requestId: "r-1" })
console.log("-- annotateLogs 合并")
yield* Effect.annotateLogs(Effect.annotateLogs(Effect.logInfo("hello"), { a: 1 }), { b: 2 })
console.log("-- 内层覆盖整体替换")
yield* Effect.annotateLogs(
Effect.provideService(Effect.logInfo("hello"), References.CurrentLogAnnotations, { a: 1 }),
{ b: 2 }
)
})
Effect.runPromise(program)输出(时间戳略):
-- CurrentLogAnnotations 直接覆盖
INFO hello { requestId: 'r-1' }
-- annotateLogs 合并
INFO hello { b: 2, a: 1 }
-- 内层覆盖整体替换
INFO hello { a: 1 }为什么最后一段的 b: 2 没了?
第二段里两次 annotateLogs 的注解都在,说明它在当前值上合并。第三段里,provideService 离日志调用最近,它把值整体换成 { a: 1 },外层 annotateLogs 想加的 b: 2 就被盖掉了。离计算最近的覆盖生效,这条规则对所有 Reference 一样。
自己封装时该怎么避免这个坑?
值的类型尽量只读、不可变,包括默认值。覆盖是整体替换,不是合并;需要「在当前值上叠加」的,自己封装成 Effect.updateService 或像 annotateLogs 那样的辅助函数,别让调用方直接面对原始 Reference。
其他内置 Reference 的清单与用法,见 Effect 里怎么关掉局部追踪或调整日志级别?。
版本是 effect@4.0.5。上面三段输出都在本地跑过;updateService作为叠加手段的建议没有针对CurrentLogAnnotations单独运行验证。