学习
心智模型:三种日志模式、大型事件生命周期、采样、类型化字段和脱敏。如果你是新手,请按顺序阅读。

本节是 evlog 的心智模型。到最后,你会清楚地知道 evlog 的作用、每个 API 适合什么场景,以及一个事件如何从你的代码流向你的 drain。

如果你是新手,请按顺序阅读。如果你已经在项目中使用过 evlog,可以直接跳到与你问题相符的页面。

三种模式都共存于同一个 logger 中。每次调用时按需选择。不存在升级路径、进阶模式或需要切换的开关。底层使用相同的 drain、相同的脱敏机制和相同的类型。
不运行 HTTP 框架?请查看 独立 TypeScriptCloudflare Workers

三种日志模式

简单日志

功能完整的通用 logger。使用 log.infolog.errorlog.warnlog.debug 替代 console.log、consola、pino 或 winston,同时保留相同的级别过滤、drain 管道、脱敏机制以及美化/JSON 输出。

大型事件

在一个工作单元(脚本、任务、队列作业或请求)中累积上下文,最终发出一个完整的事件。

请求日志

自动管理、绑定 HTTP 请求的大型事件。框架中间件会自动为你创建 logger 并发出事件。

快速比较三种模式

简单日志(log

每次调用一个事件。不累积上下文,也不管理生命周期。

src/index.ts
import { log } from 'evlog'

log.info('auth', '用户已登录')
log.error({ action: 'payment', error: 'card_declined', userId: 42 })

大型事件(createLogger / createRequestLogger

每个工作单元一个事件。逐步累积上下文,完成时发出。

import { createLogger } from 'evlog'

const log = createLogger({ jobId: 'sync-001', queue: 'emails' })
log.set({ batch: { size: 50, processed: 50 } })
log.emit()

createRequestLoggercreateLogger 的轻量封装,预填充了 methodpathrequestId

请求日志(框架中间件)

框架集成会在每个请求上自动创建一个大型事件日志记录器。useLogger(event) 用于获取已附加到请求上下文的日志记录器:

server/api/checkout.post.ts
import { useLogger } from 'evlog'

export default defineEventHandler(async (event) => {
  const log = useLogger(event)
  log.set({ user: { id: 1, plan: 'pro' } })
  return { success: true }
  // 在响应结束时自动发出
})
useLogger(event) 并不会创建日志记录器,而是获取框架中间件已附加到事件上的日志记录器。每个框架有各自的获取方式(useLoggerreq.logc.get('log') 等)。在 Nuxt 中,useLogger 是自动导入的。

根据任务选择模式

logcreateLogger / createRequestLogger框架中间件
使用场景快速的一次性事件脚本、任务、工作者、队列、无框架的 HTTP 请求使用框架集成的 API 路由
上下文单次调用使用 set() 累积使用 set() 累积
发射立即发射手动调用 emit()响应结束时自动发射
生命周期手动管理框架管理
输出控制台 + 导出控制台 + 导出控制台 + 导出 + 增强

按上下文

上下文最适合原因
Nuxt / Next / Hono / Express / … 中的 HTTP 路由通过 框架集成 使用 useLogger(event)每个请求一个宽事件,在响应结束时自动发出
没有框架的 HTTP 处理器createRequestLogger({ method, path })与框架中间件相同的形状,手动发出
CLI 工具 / 一次性脚本步骤用 log.*,运行汇总用 createLogger —— 见 独立模式开发环境中友好美观,CI 中结构化输出,为整个运行生成一个汇总事件
已发布的库只用 createLogger —— 不要使用 initLogger —— 见 独立模式不污染宿主应用的全局配置,也不会强迫使用者接入 drain
后台任务 / 队列 worker / 定时任务每次调用使用一个 createLogger({ jobId, queue }) —— 见 独立模式每次任务运行一个宽事件,非常适合重试分析
Cloudflare Worker / 边缘函数createWorkersLogger(req)createRequestLogger —— 见 Cloudflare Workers每个请求一个事件,不需要 process 全局变量
AWS LambdainitLogger 一次,再为每次调用使用 createLogger —— 见 AWS Lambda冷启动初始化、每个事件独立作用域、在处理器中刷新 drain
批处理 / 流水线步骤每个阶段使用一个 createLogger({ step })每个阶段一个事件,输入和输出并排呈现
AI 代理 / LLM 调用createLogger + createAILogger同一个宽事件上记录 token 使用、工具调用、流式指标
请求内被调用的库函数从调用方传入 useLogger(event),或接受一个 logger 作为参数继承父级请求上下文,贡献到同一个宽事件
共享工作区包把它当作库来处理——见 独立模式宿主应用负责 initLogger / drain;包使用 createLogger 或接收一个 logger
这些模式之间没有任何一种是另一种的“升级版”。在合适的情况下,可以在同一个文件中同时使用 logcreateLogger,因为它们共享全局 drain、脱敏机制和类型。

三种模式的共同点

所有三种模式都基于同一底层能力:

  • 开发环境中使用美化输出,生产环境中使用 JSON(默认,无需配置)
  • Drain 管道,用于将事件发送到 Axiom、Sentry、PostHog 等。请查看 集成 / 适配器
  • 结构化错误,包含 whyfixlink,以及可选的仅后端使用的日志 internal
  • 采样(头部 + 尾部),用于控制生产环境中的日志量
  • 脱敏,在机密离开进程之前将其清除
  • 零依赖,gzip 压缩后约 6 kB

接下来阅读什么

在介绍完三种模式之后,Learn 的其余部分会涵盖所有模式都会出现的概念:

  • 结构化错误whyfixlinkinternal,以及 createErrorthrow new Error 的区别
  • 目录:可在重构后继续保留的类型化错误 / 审计目录
  • 生命周期:从 emit() 到你的 drain 之间究竟发生了什么
  • 采样:保留所有错误和慢请求;丢弃健康噪声
  • 类型化字段:扩展 RequestLogger,让 log.set 支持自动补全
  • 脱敏:在 drain 之前移除 authorizationpasswordtoken 等字段的规则

完成 Learn 后,前往 Integrate 将 evlog 接入你的技术栈,或者先运行 evlog map,查找受支持入口点中缺失的 evlog 模式。