学习
心智模型——三种日志模式、宽事件生命周期、采样、类型化字段和脱敏。如果你是新手,请按顺序阅读本节;如果你已经熟悉,可以直接选择你需要的部分。

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

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

这三种模式都共存于同一个日志记录器中。按调用场景选择即可——没有升级路径,没有高级模式,也没有需要切换的开关。底层使用相同的 drain、相同的脱敏机制、相同的类型。
不运行 HTTP 框架?请查看 独立 TypeScriptCloudflare Workers

三种日志模式

简单日志

一个功能完整的通用日志记录器。用 log.infolog.errorlog.warnlog.debug 替代 console.log、consola、pino 或 winston——具有相同的级别过滤、drain 管道、脱敏以及美化/JSON 输出。

大型事件

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

请求日志

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

快速对比

简单日志(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()响应结束时自动发射
生命周期手动管理框架管理
输出控制台 + 导出控制台 + 导出控制台 + 导出 + 增强

按上下文

ContextBest fitWhy
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
  • 采样(头部 + 尾部),用于控制生产环境中的日志量
  • 在任何秘密信息离开进程之前就将其清除的脱敏
  • 零依赖,约 6 kB gzip

本节其余内容

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

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

看完 Learn 之后,请前往 集成,将 evlog 接入你的技术栈。