入门
evlog 是一款为你交付的一切而打造的现代 TypeScript 日志记录器。它提供简单的结构化日志(可直接替代 console.log、pino 或 consola)、可在一次操作中累积上下文的广泛事件,以及解释错误发生原因的结构化错误——全部通过同一个 API,全部走同一套 drain 管线。可用于 CLI、库、后台任务、边缘工作器和 HTTP 处理器,无需切换日志记录器。
灵感来自 Logging Sucks(Boris Tane 著)。
哲学
传统日志记录已失效。你的日志分散在数十个文件中。每个请求生成超过 10 行日志。当出现问题时,你只能通过 grep 在噪音中寻找信号。
evlog 采用了不同的方法:
结构化日志
用类型化、结构化的事件替代
console.log,并通过 drain 管线流转。拥有与 pino 或 consola 相同的级别过滤、脱敏以及美化/JSON 输出。广泛事件
在任意工作单元(一次请求、脚本或任务)中累积上下文,然后一次性发出。两种模式并存——彼此都不是对方的升级版。
结构化错误
解释为什么发生以及如何修复的错误。
对开发者友好
开发阶段人类可读,生产环境为可解析的 JSON。
没有运行 HTTP 框架?请参阅用于脚本、工作器和 CLI 的 独立 TypeScript,以及用于边缘运行时的 Cloudflare Workers。
为什么选择 evlog,而不是 pino、winston 或 consola
evlog 是一个功能完整的通用日志记录器,恰好也支持广泛事件。具体来说:
- 零传递依赖,约 6 kB gzip——无需审计,也不会在下一个 Node LTS 中损坏。在 ~3 µs/request 的基准测试中,它在广泛事件模式下(1 个事件 vs 4 行日志)比 pino 快 7.7 倍,并且在其他所有路径上都具有竞争力。
- 所有上下文中都使用相同的 API——脚本、框架、边缘运行时、浏览器、库代码。没有
pino-http与pino的分裂,也不需要为每个环境分别维护consola报告器。 - 内置
why/fix/link的结构化错误——你的错误提示现在终于能告诉用户出了什么问题以及该怎么做,你的值班人员也不必再反向分析堆栈跟踪。 - 广泛事件是免费的升级路径——当你需要在一次操作中关联上下文时,同一个日志记录器会提供
log.set+log.emit,而不是之后再把多行日志拼接起来。
请查看完整的 功能对比(对等矩阵、诚实的差距和迁移片段),与 pino、winston 和 consola 逐项比较。
三种记录方式
evlog 为不同上下文提供三种 API。你可以在同一个项目中使用所有三种方式。
简单日志
即发即忘的结构化日志。替换 console.log、consola 或 pino:
src/index.ts
import { log } from 'evlog'
log.info('auth', '用户已登录')
log.error({ action: 'payment', error: 'card_declined', userId: 42 })
广泛事件
在任意操作过程中逐步累积上下文,然后发出一个全面的事件:
import { createLogger } from 'evlog'
const log = createLogger({ jobId: 'sync-001', queue: 'emails' })
log.set({ batch: { size: 50, processed: 50 } })
log.emit()
import { createRequestLogger } from 'evlog'
const log = createRequestLogger({ method: 'POST', path: '/api/checkout' })
log.set({ user: { id: 1, plan: 'pro' } })
log.emit()
import { useLogger } from 'evlog'
export default defineEventHandler(async (event) => {
const log = useLogger(event)
log.set({ user: { id: 1, plan: 'pro' } })
return { success: true }
// 在响应结束时自动发出
})
一个日志,包含所有上下文。理解发生的一切所需的一切都包含其中。
结构化错误
带有可操作上下文的错误:why 它发生,如何 fix,以及指向文档的 link:
import { createError } from 'evlog'
throw createError({
message: 'Payment failed',
status: 402,
why: 'Card declined by issuer (insufficient funds)',
fix: 'Try a different payment method or contact your bank',
link: 'https://docs.example.com/payments/declined',
})
{
"statusCode": 402,
"message": "Payment failed",
"data": {
"why": "Card declined by issuer (insufficient funds)",
"fix": "Try a different payment method or contact your bank",
"link": "https://docs.example.com/payments/declined"
}
}
为什么上下文很重要
我们正在进入一个 AI 代理构建、调试和维护应用程序的时代。这些代理需要 结构化上下文 才能有效工作:
why:根本原因,让代理理解哪里出了问题fix:可操作的解决方案,供代理建议或应用link:复杂问题的文档
传统的 console.log 和通用的 throw new Error() 无法提供可操作的上下文。evlog 的结构化输出旨在供人类和 AI 解析并采取行动。
下一步
- 为什么从 evlog 开始 - 在一开始就采用 evlog 的理由
- 安装 - 在你的项目中安装 evlog
- 快速开始 - 在几分钟内上手运行
- 日志概览 - 深入了解三种日志记录模式
- evlog vs pino、winston、consola - 功能对等、坦诚的差距和迁移片段
- 独立 TypeScript — 无需 Web 框架的脚本、工作器、库