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