为什么从 evlog 开始
添加结构化日志最省成本的时机是在第一次请求之前。等你拥有 200 个路由、40 个后台任务,并且每个文件里都有一个 console.log 时,你就要开始为一个从未真正做出过的决定支付利息了。evlog 专为第 0 天的选择而设计:只需选择一次,系统的其余部分就会继承结构化日志、结构化错误、类型化目录、AI SDK 遥测、审计轨迹,以及无需日后自行构建的 drain 管道。
console.log 或 pino 发布了吗?evlog 依然更胜一筹,但理由有所不同。参见 evlog vs pino、winston、consola。从第一天起你就能得到什么
evlog 不是一个供你包装的小型原语。它是一个单一依赖,默认提供结构化接口、集成生态和有明确设计取舍的 drain 管道。
日志基础原语
自动脱敏
结构化错误
宽事件
类型化目录
头采样 + 尾采样
Drain 管道
不止是 logger
这些原语只是基本要求,每个现代 logger 都具备其中某种形式。evlog 在第 1 天就能占据一席之地,靠的是围绕它们连接起来的所有能力,用来解决那些你尚未遇到的问题。
想象一下你给应用添加 AI。 迟早你会把 Vercel AI SDK 接入某个路由。令牌成本让你感到意外,模型在流式传输中途卡住,工具返回垃圾结果。借助 AI SDK 集成,每次模型调用都会自动成为一个包含提示词、工具、令牌、延迟和成本的宽事件。而且如果你使用 Better Auth,evlog 会为你将操作者身份关联到这些事件,这样无需编写任何管道代码,你就能回答“刚才是哪个用户在一次对话中花掉了 14 美元?”
想象一下你的技术栈横跨多个框架。 一个 Nuxt 前端、一个 Hono 内部服务、一个 AWS Lambda webhook:大多数团队最终都会形成这样的组合。evlog 拥有 13+ 个框架集成,每个集成都提供相同的日志原语(useLogger、log.set、createError)。处理器本身仍然保持框架的形态,这部分由你负责,但每次跨越一个运行时,你无需重新学习一个 logger,它们背后的 drain 管道也始终保持一致。
想象一下你希望开发环境中的日志比生产环境更嘈杂。 在本地开发期间,你会添加一些 log.debug 调用(完整的请求体、每次重试、每个守卫),以便真正看清发生了什么。这些内容不应该被发布到线上。Vite 插件 会在构建时移除选定的日志级别,因此开发环境拥有你想要的详细上下文,而生产环境保持简洁。除此之外,每个保留下来的调用都会自动注入其源代码位置(file.ts:42),因此当事件进入你的仪表板时,你能准确知道是哪一行发出的。
想象一下用户从浏览器报告了一个 bug。 错误发生在用户的会话中,深藏在一个你无法复现的 fetch 内部。evlog 的浏览器 logger 会将客户端事件发送到你的服务器,在那里它们会与请求的其余部分合并成同一个宽事件:无论错误从哪里产生,最终都是一个类型化事件。将它与内置增强器结合使用,你还可以免费获得附加的 UA、GeoIP 和 W3C trace context。
想象一下你的技术栈更换了供应商。 你最初使用 stdout,后来签约 Axiom 进行查询,然后你的团队又希望使用 Sentry 处理错误、使用 PostHog 进行产品分析。借助 9+ 个 drain 适配器和内置扇出,这些事件会并行发送到所有地方,而应用代码无需改动。通过文件系统或 NuxtHub适配器,也可以直接切换为自行托管。
这些都不是“v2 功能”。从第 1 天起,它们就是同一个包、同一个 log API 的一部分。
目录会随着你一起成长
最小且有用的目录只需要两项:
import { defineErrorCatalog } from 'evlog'
export const errors = defineErrorCatalog('billing', {
PAYMENT_DECLINED: { status: 402, message: '支付被拒绝' },
INVOICE_NOT_FOUND: { status: 404, message: '未找到发票' },
})
六个月后,它会有三十项条目,类型增强会让你在任何地方对 createError({ code }) 拥有自动补全,而你会把它作为一个私有 npm 包发布到整个 monorepo。同一种模式,无需重写。
审计目录(defineAuditCatalog)也是如此:从一个动作开始,逐步成长为你的合规地图。参见目录。
为 AI 编码代理时代而构建
越来越多的应用是使用 AI 编码代理构建的:Cursor、Codex、Claude Code、Copilot。它们擅长编写处理器,却不太擅长调试处理器。它们需要的是上下文。
- 带有
why/fix/link的结构化错误。 模糊的Error: failed让人无法理解;createError({ message, why, fix, link })则是代理可以读取、总结或呈现给用户的内容,而无需你连接一个翻译层。 - 作为每个请求单一事实来源的宽事件。 代理可以端到端推理一个类型化事件,而不是在日志行之间进行 grep。
- 作为类似枚举接口的类型化目录。 代理不会自行发明错误代码,而是从
errors.PAYMENT_DECLINED、audit.INVOICE_REFUND等选项中选择,并通过目录获得自动补全。 - 每次 LLM 调用都具备 AI SDK 遥测。 当代理自己的模型调用失败、产生幻觉或消耗预算时,宽事件会告诉你使用了哪个提示词、哪些工具、多少令牌以及花费了多少成本。
- 内置代理技能。 evlog 提供了代理技能,因此 Cursor / Claude 已经知道如何接入,无需手动进行提示词工程。
审计与合规:现在便宜,以后昂贵
每个产品最终都会遇到以下之一:GDPR 数据导出请求、SOC 2 准备、医疗健康领域的 HIPAA、支付领域的 PCI,或者仅仅是一场事故复盘,有人会问 “是谁删了那个?”。获取一条审计轨迹有两种方式:
- 第 1000 天的方式。 搭建一个并行系统。确定一个架构。尽可能回填数据。从请求头中反向推断操作者身份。在截止期限的压力下发布。希望没有遗漏任何内容。
- 第 0 天的方式。 添加
auditEnricher(),并在任何会修改状态的处理器中调用log.audit({ action, actor, target })。在你原本就会发出的宽事件之上,evlog 提供哈希链完整性、保留策略,以及超越采样机制的强制保留。
evlog 的审计层不是一个并行系统。它就是你已经在使用的同一个 log,只是带有一个保留的 audit 字段。参见审计日志。
从第一天起就与 drain 无关
你的应用代码从不依赖某个供应商。它只向 drain 管道发出事件。第 0 天,这在开发环境中是 stdout,在 CI 中则是文件系统 drain。当你决定查询日志的那一天:
import { createAxiomDrain } from 'evlog/axiom'
import { createSentryDrain } from 'evlog/sentry'
export default defineNuxtConfig({
evlog: {
drain: {
adapters: [
createAxiomDrain({ token: '...', dataset: 'app' }),
createSentryDrain({ dsn: '...' }),
],
},
},
})
无需修改任何处理器。同样的事件会通过扇出进入 Axiom、Datadog、PostHog、Sentry、Better Stack、HyperDX或 OTLP,也可以进入它们全部。
“以后”到底要付出什么成本
当团队先用 console.log 发版,然后打算“等需要的时候”再补上正规日志时,账单就会找上门:
- 事后统一字段命名约定。 是
userId、user_id、uid,还是actor.id?一旦有三十个服务都用不同方式记录,你就只能在观测平台里写迁移脚本了。 - 事故后再补脱敏。 当还没有日志时,自动脱敏很简单;但如果六个月的日志里已经包含了 PII,那就是一次 P1 级审计事件。
- 在压力下选择日志落地端。 当你已经着火了,再去比较 Datadog、Axiom、Sentry,根本不是评估供应商的最佳时机。
- 事后补上可操作的错误上下文。 你写过的每一个
throw new Error('failed'),都少了一个why、少了一个fix、少了一个你的值班工程师(或者 AI 代理)可以使用的链接。
evlog 完全消除了“以后”:从第 1 天起,结构化接口、宽事件生命周期和 drain 管道就全部可用。
| 决策日 | 采用 evlog 的成本 |
|---|---|
| 第 0 天(新项目) | 添加框架模块。完成。 |
| 第 30 天(小型应用) | 切换日志接口——大约一天工作量。 |
| 第 365 天(生产应用) | 通读代码库替换日志器,统一字段命名约定,补入审计与脱敏。成本与在任意两个结构化日志器之间迁移相同。 |
这种不对称正是关键。一开始就用 evlog,因为成本为零。继续用 evlog,因为离开它的成本比自己把这些能力都搭出来还高。
第 0 天,实践中
从第一次提交开始就接入 evlog,开启一个新项目
下一步
- 安装:选择你的框架
- 快速开始:在 2 分钟内使用
useLogger、createLogger、createError - 目录:类型化错误和审计动作,从第 0 天扩展到 monorepo 规模
- 审计日志:第 0 天的合规状态
- AI SDK:令牌使用情况、工具调用、流式传输指标
- Better Auth:一行安装即可获得身份验证事件
- 最佳实践:不应记录的内容、脱敏、采样
- evlog vs pino、winston、consola:功能对等矩阵和迁移片段