---
title: "学习 evlog"
description: "心智模型：三种日志模式、大型事件生命周期、采样、类型化字段和脱敏。如果你是新手，请按顺序阅读。"
canonical_url: "https://evlog.zhcndoc.com/learn/overview"
---
# 学习 evlog

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

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

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

<callout color="info" icon="i-lucide-info">

三种模式都共存于同一个 logger 中。每次调用时按需选择。不存在升级路径、进阶模式或需要切换的开关。底层使用相同的 drain、相同的脱敏机制和相同的类型。

</callout>

<callout color="neutral" icon="i-lucide-globe">

不运行 HTTP 框架？请查看 [独立 TypeScript](https://evlog.zhcndoc.com/integrate/frameworks/standalone) 和 [Cloudflare Workers](https://evlog.zhcndoc.com/integrate/frameworks/cloudflare-workers)。

</callout>

## 三种日志模式

<card-group>
<card color="neutral" icon="i-lucide-terminal" title="简单日志" to="https://evlog.zhcndoc.com/learn/simple-logging">

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

</card>

<card color="neutral" icon="i-lucide-layers" title="大型事件" to="https://evlog.zhcndoc.com/learn/wide-events">

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

</card>

<card color="neutral" icon="i-lucide-git-branch" title="请求日志" to="https://evlog.zhcndoc.com/integrate/frameworks/overview">

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

</card>
</card-group>

## 快速比较三种模式

### 简单日志（`log`）

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

```typescript [src/index.ts]
import { log } from 'evlog'

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

### 大型事件（`createLogger` / `createRequestLogger`）

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

<code-group>

```typescript [scripts/sync-job.ts]
import { createLogger } from 'evlog'

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

```typescript [src/worker.ts]
import { createRequestLogger } from 'evlog'

const log = createRequestLogger({ method: 'POST', path: '/api/checkout' })
log.set({ user: { id: 1, plan: 'pro' } })
log.emit()
```

</code-group>

`createRequestLogger` 是 `createLogger` 的轻量封装，预填充了 `method`、`path` 和 `requestId`。

### 请求日志（框架中间件）

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

<framework-tabs>

```typescript [Nuxt]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 }
  // 在响应结束时自动发出
})
```

```typescript [Nitro]routes/api/checkout.post.ts
import { defineHandler } from 'nitro/h3'
import { useLogger } from 'evlog/nitro/v3'

export default defineHandler(async (event) => {
  const log = useLogger(event)

  log.set({ user: { id: 1, plan: 'pro' } })
  return { success: true }
  // auto-emitted on response end
})
```

```typescript [Next.js]app/api/checkout/route.ts
import { withEvlog, useLogger } from '@/lib/evlog'

export const POST = withEvlog(async () => {
  const log = useLogger()

  log.set({ user: { id: 1, plan: 'pro' } })
  return Response.json({ success: true })
  // auto-emitted on response end
})
```

```typescript [Hono]src/index.ts
app.post('/api/checkout', async (c) => {
  const log = c.get('log')

  log.set({ user: { id: 1, plan: 'pro' } })
  return c.json({ success: true })
  // auto-emitted on response end
})
```

```typescript [Express]src/index.ts
app.post('/api/checkout', async (req, res) => {
  const log = req.log

  log.set({ user: { id: 1, plan: 'pro' } })
  return res.json({ success: true })
  // auto-emitted on response end
})
```

</framework-tabs>

<callout color="info" icon="i-lucide-info">

`useLogger(event)` 并不会创建日志记录器，而是获取框架中间件已附加到事件上的日志记录器。每个框架有各自的获取方式（`useLogger`、`req.log`、`c.get('log')` 等）。在 Nuxt 中，`useLogger` 是自动导入的。

</callout>

## 根据任务选择模式

<table>
<thead>
  <tr>
    <th>
      
    </th>
    
    <th>
      <code>
        log
      </code>
    </th>
    
    <th>
      <code>
        createLogger
      </code>
      
       / <code>
        createRequestLogger
      </code>
    </th>
    
    <th>
      框架中间件
    </th>
  </tr>
</thead>

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

### 按上下文

<table>
<thead>
  <tr>
    <th>
      上下文
    </th>
    
    <th>
      最适合
    </th>
    
    <th>
      原因
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <strong>
        Nuxt / Next / Hono / Express / … 中的 HTTP 路由
      </strong>
    </td>
    
    <td>
      通过 <a href="https://evlog.zhcndoc.com/integrate/frameworks/overview">
        框架集成
      </a>
      
       使用 <code>
        useLogger(event)
      </code>
    </td>
    
    <td>
      每个请求一个宽事件，在响应结束时自动发出
    </td>
  </tr>
  
  <tr>
    <td>
      <strong>
        没有框架的 HTTP 处理器
      </strong>
    </td>
    
    <td>
      <code>
        createRequestLogger({ method, path })
      </code>
    </td>
    
    <td>
      与框架中间件相同的形状，手动发出
    </td>
  </tr>
  
  <tr>
    <td>
      <strong>
        CLI 工具 / 一次性脚本
      </strong>
    </td>
    
    <td>
      步骤用 <code>
        log.*
      </code>
      
      ，运行汇总用 <code>
        createLogger
      </code>
      
       —— 见 <a href="https://evlog.zhcndoc.com/integrate/frameworks/standalone">
        独立模式
      </a>
    </td>
    
    <td>
      开发环境中友好美观，CI 中结构化输出，为整个运行生成一个汇总事件
    </td>
  </tr>
  
  <tr>
    <td>
      <strong>
        已发布的库
      </strong>
    </td>
    
    <td>
      只用 <code>
        createLogger
      </code>
      
       —— 不要使用 <code>
        initLogger
      </code>
      
       —— 见 <a href="https://evlog.zhcndoc.com/integrate/frameworks/standalone">
        独立模式
      </a>
    </td>
    
    <td>
      不污染宿主应用的全局配置，也不会强迫使用者接入 drain
    </td>
  </tr>
  
  <tr>
    <td>
      <strong>
        后台任务 / 队列 worker / 定时任务
      </strong>
    </td>
    
    <td>
      每次调用使用一个 <code>
        createLogger({ jobId, queue })
      </code>
      
       —— 见 <a href="https://evlog.zhcndoc.com/integrate/frameworks/standalone">
        独立模式
      </a>
    </td>
    
    <td>
      每次任务运行一个宽事件，非常适合重试分析
    </td>
  </tr>
  
  <tr>
    <td>
      <strong>
        Cloudflare Worker / 边缘函数
      </strong>
    </td>
    
    <td>
      <code>
        createWorkersLogger(req)
      </code>
      
       或 <code>
        createRequestLogger
      </code>
      
       —— 见 <a href="https://evlog.zhcndoc.com/integrate/frameworks/cloudflare-workers">
        Cloudflare Workers
      </a>
    </td>
    
    <td>
      每个请求一个事件，不需要 <code>
        process
      </code>
      
       全局变量
    </td>
  </tr>
  
  <tr>
    <td>
      <strong>
        AWS Lambda
      </strong>
    </td>
    
    <td>
      先 <code>
        initLogger
      </code>
      
       一次，再为每次调用使用 <code>
        createLogger
      </code>
      
       —— 见 <a href="https://evlog.zhcndoc.com/integrate/frameworks/aws-lambda">
        AWS Lambda
      </a>
    </td>
    
    <td>
      冷启动初始化、每个事件独立作用域、在处理器中刷新 drain
    </td>
  </tr>
  
  <tr>
    <td>
      <strong>
        批处理 / 流水线步骤
      </strong>
    </td>
    
    <td>
      每个阶段使用一个 <code>
        createLogger({ step })
      </code>
    </td>
    
    <td>
      每个阶段一个事件，输入和输出并排呈现
    </td>
  </tr>
  
  <tr>
    <td>
      <strong>
        AI 代理 / LLM 调用
      </strong>
    </td>
    
    <td>
      <code>
        createLogger
      </code>
      
       + <a href="https://evlog.zhcndoc.com/use-cases/ai-sdk/overview">
        <code>
          createAILogger
        </code>
      </a>
    </td>
    
    <td>
      同一个宽事件上记录 token 使用、工具调用、流式指标
    </td>
  </tr>
  
  <tr>
    <td>
      <strong>
        请求内被调用的库函数
      </strong>
    </td>
    
    <td>
      从调用方传入 <code>
        useLogger(event)
      </code>
      
      ，或接受一个 logger 作为参数
    </td>
    
    <td>
      继承父级请求上下文，贡献到同一个宽事件
    </td>
  </tr>
  
  <tr>
    <td>
      <strong>
        共享工作区包
      </strong>
    </td>
    
    <td>
      把它当作库来处理——见 <a href="https://evlog.zhcndoc.com/integrate/frameworks/standalone">
        独立模式
      </a>
    </td>
    
    <td>
      宿主应用负责 <code>
        initLogger
      </code>
      
       / drain；包使用 <code>
        createLogger
      </code>
      
       或接收一个 logger
    </td>
  </tr>
</tbody>
</table>

<callout color="info" icon="i-lucide-lightbulb">

这些模式之间没有任何一种是另一种的“升级版”。在合适的情况下，可以在同一个文件中同时使用 `log` 和 `createLogger`，因为它们共享全局 drain、脱敏机制和类型。

</callout>

## 三种模式的共同点

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

- 开发环境中使用**美化输出**，生产环境中使用 **JSON**（默认，无需配置）
- **Drain 管道**，用于将事件发送到 Axiom、Sentry、PostHog 等。请查看 [集成 / 适配器](https://evlog.zhcndoc.com/integrate/adapters/overview)
- **结构化错误**，包含 `why`、`fix` 和 `link`，以及可选的仅后端使用的日志 **internal**
- **采样**（头部 + 尾部），用于控制生产环境中的日志量
- **脱敏**，在机密离开进程之前将其清除
- **零依赖**，gzip 压缩后约 6 kB

## 接下来阅读什么

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

- [结构化错误](https://evlog.zhcndoc.com/learn/structured-errors)：`why`、`fix`、`link`、`internal`，以及 `createError` 与 `throw new Error` 的区别
- [目录](https://evlog.zhcndoc.com/learn/catalogs)：可在重构后继续保留的类型化错误 / 审计目录
- [生命周期](https://evlog.zhcndoc.com/learn/lifecycle)：从 `emit()` 到你的 drain 之间究竟发生了什么
- [采样](https://evlog.zhcndoc.com/learn/sampling)：保留所有错误和慢请求；丢弃健康噪声
- [类型化字段](https://evlog.zhcndoc.com/learn/typed-fields)：扩展 `RequestLogger`，让 `log.set` 支持自动补全
- [脱敏](https://evlog.zhcndoc.com/learn/redaction)：在 drain 之前移除 `authorization`、`password`、`token` 等字段的规则

完成 Learn 后，前往 [Integrate](https://evlog.zhcndoc.com/integrate/frameworks/overview) 将 evlog 接入你的技术栈，或者先运行 [`evlog map`](https://evlog.zhcndoc.com/cli/map)，查找受支持入口点中缺失的 evlog 模式。

---

- [简单日志](https://evlog.zhcndoc.com/learn/simple-logging)
- [大型事件](https://evlog.zhcndoc.com/learn/wide-events)
- [生命周期](https://evlog.zhcndoc.com/learn/lifecycle)


## Sitemap

See the full [sitemap](https://evlog.zhcndoc.com/sitemap.md) for all pages.
