---
title: "生命周期"
description: "evlog 事件从创建到传输，贯穿三种模式，以及采样、丰富化和传输过程"
canonical_url: "https://evlog.zhcndoc.com/learn/lifecycle"
---
# 生命周期

> evlog 事件从创建到传输，贯穿三种模式，以及采样、丰富化和传输过程

evlog 事件遵循从创建到传输的流水线。流水线会根据你使用的日志模式略有不同，但核心阶段（发送、采样、丰富化、传输）是共享的。

<lifecycle-flow>



</lifecycle-flow>

## 逐模式跟踪事件

<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>
      <code>
        createLogger({...})
      </code>
      
       或 <code>
        createRequestLogger({...})
      </code>
    </td>
    
    <td>
      请求开始时自动创建
    </td>
  </tr>
  
  <tr>
    <td>
      <strong>
        积累
      </strong>
    </td>
    
    <td>
      不适用（单次调用）
    </td>
    
    <td>
      多次调用 <code>
        log.set()
      </code>
    </td>
    
    <td>
      通过 <code>
        useLogger(event)
      </code>
      
       调用 <code>
        log.set()
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <strong>
        发送
      </strong>
    </td>
    
    <td>
      立即发送
    </td>
    
    <td>
      手动调用 <code>
        log.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>
  
  <tr>
    <td>
      <strong>
        传输
      </strong>
    </td>
    
    <td>
      通过全局传输钩子
    </td>
    
    <td>
      通过全局传输钩子
    </td>
    
    <td>
      通过钩子或回调
    </td>
  </tr>
</tbody>
</table>

在 **emit** 之后（包括采样返回无输出时），请求日志记录器会被**封闭**：后续的 `set` / `error` / `info` / `warn` 调用会被忽略，并在控制台显示警告。对于需要创建独立事件的后台工作，请在你的集成支持时使用 **log.fork()**。请参阅宽事件中的[发送后](https://evlog.zhcndoc.com/learn/wide-events#after-emit-sealing-and-background-work)。

## 逐步跟踪请求

对于由框架管理的请求日志，每个请求都会走完上面的流水线。下面详细说明每个阶段。

### 1. 路由过滤

当请求到达时，evlog 会检查路径是否匹配配置的 `include` / `exclude` 模式。如果路由被排除，不会创建日志记录器，请求将继续执行且不产生任何日志开销。

默认情况下记录所有路由。使用 `include` 限制仅记录特定模式：

<framework-tabs>

```typescript [Nuxt]nuxt.config.ts
export default defineNuxtConfig({
  modules: ['evlog/nuxt'],
  evlog: {
    include: ['/api/**'],
  },
})
```

```typescript [Nitro]nitro.config.ts
import { defineConfig } from 'nitro'
import evlog from 'evlog/nitro/v3'

export default defineConfig({
  modules: [evlog({ include: ['/api/**'] })],
})
```

```typescript [Next.js]lib/evlog.ts
import { createEvlog } from 'evlog/next'

export const { withEvlog, useLogger, log, createError } = createEvlog({
  service: 'my-app',
  include: ['/api/**'],
})
```

```typescript [SvelteKit]src/hooks.server.ts
import { createEvlogHooks } from 'evlog/sveltekit'

export const { handle, handleError } = createEvlogHooks({ include: ['/api/**'] })
```

```typescript [TanStack Start]nitro.config.ts
import { defineConfig } from 'nitro'
import evlog from 'evlog/nitro/v3'

export default defineConfig({
  modules: [evlog({ include: ['/api/**'] })],
})
```

```typescript [React Router]app/root.tsx
import { evlog } from 'evlog/react-router'

export const middleware: Route.MiddlewareFunction[] = [
  evlog({ include: ['/api/**'] }),
]
```

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

app.use(evlog({ include: ['/api/**'] }))
```

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

app.use(evlog({ include: ['/api/**'] }))
```

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

await app.register(evlog, { include: ['/api/**'] })
```

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

app.use(evlog({ include: ['/api/**'] }))
```

```typescript [NestJS]src/app.module.ts
import { EvlogModule } from 'evlog/nestjs'

EvlogModule.forRoot({ include: ['/api/**'] })
```

```typescript [oRPC]server/orpc.ts
import { withEvlog } from 'evlog/orpc'

const handler = withEvlog(new RPCHandler(router), { include: ['/api/**'] })
```

```typescript [Cloudflare Workers]src/worker.ts
import { withEvlog } from 'evlog/workers'

export default withEvlog(handler, { include: ['/api/**'] })
```

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

initLogger({ include: ['/api/**'] })
```

</framework-tabs>

### 2. 日志记录器创建

对于匹配的路由，evlog 会创建一个 `RequestLogger` 并将其附加到请求上下文。日志记录器预先填充了以下字段：

<table>
<thead>
  <tr>
    <th>
      字段
    </th>
    
    <th>
      来源
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        method
      </code>
    </td>
    
    <td>
      HTTP 方法（<code>
        GET
      </code>
      
      、<code>
        POST
      </code>
      
      、……）
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        path
      </code>
    </td>
    
    <td>
      请求路径
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        requestId
      </code>
    </td>
    
    <td>
      自动生成的 UUID（在 Cloudflare 上使用 <code>
        cf-ray
      </code>
      
      ）
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        startTime
      </code>
    </td>
    
    <td>
      用于计算持续时间的 <code>
        Date.now()
      </code>
    </td>
  </tr>
</tbody>
</table>

日志记录器存储在事件上下文上。`useLogger(event)` 是获取它的快捷方式，不会创建新的日志记录器。

### 3. 上下文积累

在处理器中，你调用 `log.set()` 来附加上下文。每次调用都会深度合并到现有上下文中，因此可以根据需要多次调用：

```typescript [server/api/checkout.post.ts]
import { useLogger } from 'evlog'

const log = useLogger(event)

const user = await getUser(event)
log.set({ user: { id: user.id, plan: user.plan } })

const cart = await getCart(user.id)
log.set({ cart: { items: cart.items.length, total: cart.total } })
```

如果抛出错误，evlog 的 `error` 钩子会自动捕获并记录错误及其状态码。

### 4. 请求结束

当响应发送（或抛出错误）时，evlog 会计算：

- **状态码**：来自响应（或错误的 `status` / `statusCode`）
- **持续时间**：从 `Date.now() - startTime`
- **级别**：如果记录了错误则为 `error`，如果状态码 >= 400 则为 `warn`，否则为 `info`

如果错误触发了发送请求，会标记该请求已发送，以防止在响应钩子中重复发送。

### 5. 尾部采样（`evlog:emit:keep`）

在事件被采样之前，evlog 会评估**尾部采样**规则。这些规则在请求完成后运行，因此可以检查结果：

```typescript [nuxt.config.ts]
evlog: {
  sampling: {
    keep: [
      { duration: 1000 },          // 慢请求
      { status: 400 },             // 客户端/服务器错误
      { path: '/api/critical/**' }, // 关键路径
    ],
  },
}
```

`evlog:emit:keep` 钩子也会触发，允许你基于自定义业务逻辑强制保留：

<framework-tabs>

```typescript [Nuxt]server/plugins/evlog.ts
export default defineNitroPlugin((nitroApp) => {
  nitroApp.hooks.hook('evlog:emit:keep', (ctx) => {
    if (ctx.context.user?.premium) {
      ctx.shouldKeep = true
    }
  })
})
```

```typescript [Nitro]server/plugins/evlog.ts
import { definePlugin } from 'nitro'
export default definePlugin((nitroApp) => {
  nitroApp.hooks.hook('evlog:emit:keep', (ctx) => {
    if (ctx.context.user?.premium) {
      ctx.shouldKeep = true
    }
  })
})
```

```typescript [Next.js]lib/evlog.ts
import { createEvlog } from 'evlog/next'

export const { withEvlog, useLogger, log, createError } = createEvlog({
  service: 'my-app',
  keep: (ctx) => {
      if (ctx.context.user?.premium) {
        ctx.shouldKeep = true
      }
    },
})
```

```typescript [SvelteKit]src/hooks.server.ts
import { createEvlogHooks } from 'evlog/sveltekit'

export const { handle, handleError } = createEvlogHooks({
  keep: (ctx) => {
      if (ctx.context.user?.premium) {
        ctx.shouldKeep = true
      }
    },
})
```

```typescript [TanStack Start]server/plugins/evlog.ts
import { definePlugin } from 'nitro'

export default definePlugin((nitroApp) => {
  nitroApp.hooks.hook('evlog:emit:keep', (ctx) => {
    if (ctx.context.user?.premium) {
      ctx.shouldKeep = true
    }
  })
})
```

```typescript [React Router]app/root.tsx
import { evlog } from 'evlog/react-router'

export const middleware: Route.MiddlewareFunction[] = [
  evlog({
      keep: (ctx) => {
          if (ctx.context.user?.premium) {
            ctx.shouldKeep = true
          }
        },
    }),
]
```

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

app.use(evlog({
  keep: (ctx) => {
      if (ctx.context.user?.premium) {
        ctx.shouldKeep = true
      }
    },
}))
```

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

app.use(evlog({
  keep: (ctx) => {
      if (ctx.context.user?.premium) {
        ctx.shouldKeep = true
      }
    },
}))
```

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

await app.register(evlog, {
  keep: (ctx) => {
      if (ctx.context.user?.premium) {
        ctx.shouldKeep = true
      }
    },
})
```

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

app.use(evlog({
  keep: (ctx) => {
      if (ctx.context.user?.premium) {
        ctx.shouldKeep = true
      }
    },
}))
```

```typescript [NestJS]src/app.module.ts
import { EvlogModule } from 'evlog/nestjs'

EvlogModule.forRoot({
  keep: (ctx) => {
      if (ctx.context.user?.premium) {
        ctx.shouldKeep = true
      }
    },
})
```

```typescript [oRPC]server/orpc.ts
import { withEvlog } from 'evlog/orpc'

const handler = withEvlog(new RPCHandler(router), {
  keep: (ctx) => {
      if (ctx.context.user?.premium) {
        ctx.shouldKeep = true
      }
    },
})
```

```typescript [Cloudflare Workers]src/worker.ts
import { withEvlog } from 'evlog/workers'

export default withEvlog(handler, {
  keep: (ctx) => {
      if (ctx.context.user?.premium) {
        ctx.shouldKeep = true
      }
    },
})
```

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

initLogger({
  keep: (ctx) => {
      if (ctx.context.user?.premium) {
        ctx.shouldKeep = true
      }
    },
})
```

</framework-tabs>

如果任何规则或钩子设置了 `shouldKeep = true`，该事件将**完全跳过头部采样**。

### 6. 头部采样

如果事件未被尾部采样强制保留，则应用头部采样。这是基于日志级别的随机投掷硬币。

默认情况下所有级别都以 100% 保留（无采样）。配置 `sampling.rates` 可在生产环境中减少流量：

```typescript [nuxt.config.ts]
evlog: {
  sampling: {
    rates: { info: 10, warn: 50, debug: 0 },
  },
}
```

- `info: 10` - 保留 10% 的 info 级别事件
- `warn: 50` - 保留 50% 的警告
- `error` 默认**始终保留**（即使设置了比率也不会被采样排除）

如果事件被采样排除，处理将完全停止：不会输出控制台内容，不会丰富化，也不会传输。

### 7. 发送

构建 `WideEvent` 对象，包含积累的上下文：

```json [WideEvent]
{
  "timestamp": "2026-01-15T10:30:00.000Z",
  "level": "info",
  "service": "my-app",
  "method": "POST",
  "path": "/api/checkout",
  "requestId": "abc-123",
  "duration": 234,
  "status": 200,
  "user": { "id": 1, "plan": "pro" },
  "cart": { "items": 3, "total": 9999 }
}
```

此时日志记录器已封存。还不会打印任何内容：控制台行会等待丰富化器完成，因此终端显示的事件与 drain 接收到的事件相同。

### 8. 丰富化（`evlog:enrich`）

发送后，丰富化器会向事件添加派生上下文。内置丰富化器从请求头中提取数据：

<table>
<thead>
  <tr>
    <th>
      丰富化器
    </th>
    
    <th>
      添加内容
    </th>
    
    <th>
      来源
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      用户代理
    </td>
    
    <td>
      <code>
        userAgent
      </code>
      
      （浏览器、操作系统、设备）
    </td>
    
    <td>
      <code>
        User-Agent
      </code>
      
       头
    </td>
  </tr>
  
  <tr>
    <td>
      地理位置
    </td>
    
    <td>
      <code>
        geo
      </code>
      
      （国家、地区、城市）
    </td>
    
    <td>
      平台头（Vercel、Cloudflare）
    </td>
  </tr>
  
  <tr>
    <td>
      请求大小
    </td>
    
    <td>
      <code>
        requestSize
      </code>
      
      （请求/响应字节数）
    </td>
    
    <td>
      <code>
        Content-Length
      </code>
      
       头
    </td>
  </tr>
  
  <tr>
    <td>
      追踪上下文
    </td>
    
    <td>
      <code>
        traceContext
      </code>
      
      （traceId、parentSpanId）
    </td>
    
    <td>
      <code>
        traceparent
      </code>
      
       头
    </td>
  </tr>
</tbody>
</table>

```typescript [server/plugins/evlog-enrich.ts]
import { createUserAgentEnricher, createGeoEnricher } from 'evlog/enrichers'

export default defineNitroPlugin((nitroApp) => {
  const enrichers = [createUserAgentEnricher(), createGeoEnricher()]

  nitroApp.hooks.hook('evlog:enrich', (ctx) => {
    for (const enricher of enrichers) enricher(ctx)
  })
})
```

丰富化器接收完整的 `EnrichContext`，其中包含可变事件、请求元数据、安全头和响应信息。

这些钩子返回后，事件会被打印到控制台，开发环境下使用美化格式，生产环境下输出为 JSON。无需配置。丰富化器添加的字段（`userAgent`、`geo`、signal verdicts）会出现在该行中，也会出现在任何读取 stdout 的平台上。

### 9. 传输（`evlog:drain`）

最后一步将丰富化后的事件发送到你的可观测性平台。`evlog:drain` 钩子接收包含完整事件的 `DrainContext`：

<framework-tabs>

```typescript [Nuxt]server/plugins/evlog.ts
import { createAxiomDrain } from 'evlog/axiom'

export default defineNitroPlugin((nitroApp) => {
  nitroApp.hooks.hook('evlog:drain', createAxiomDrain())
})
```

```typescript [Nitro]server/plugins/evlog.ts
import { definePlugin } from 'nitro'
import { createAxiomDrain } from 'evlog/axiom'

export default definePlugin((nitroApp) => {
  nitroApp.hooks.hook('evlog:drain', createAxiomDrain())
})
```

```typescript [Next.js]lib/evlog.ts
import { createEvlog } from 'evlog/next'
import { createAxiomDrain } from 'evlog/axiom'

export const { withEvlog, useLogger, log, createError } = createEvlog({
  service: 'my-app',
  drain: createAxiomDrain(),
})
```

```typescript [SvelteKit]src/hooks.server.ts
import { createEvlogHooks } from 'evlog/sveltekit'
import { createAxiomDrain } from 'evlog/axiom'

export const { handle, handleError } = createEvlogHooks({ drain: createAxiomDrain() })
```

```typescript [TanStack Start]server/plugins/evlog.ts
import { definePlugin } from 'nitro'
import { createAxiomDrain } from 'evlog/axiom'

export default definePlugin((nitroApp) => {
  nitroApp.hooks.hook('evlog:drain', createAxiomDrain())
})
```

```typescript [React Router]app/root.tsx
import { evlog } from 'evlog/react-router'
import { createAxiomDrain } from 'evlog/axiom'

export const middleware: Route.MiddlewareFunction[] = [
  evlog({ drain: createAxiomDrain() }),
]
```

```typescript [Hono]src/index.ts
import { evlog } from 'evlog/hono'
import { createAxiomDrain } from 'evlog/axiom'

app.use(evlog({ drain: createAxiomDrain() }))
```

```typescript [Express]src/index.ts
import { evlog } from 'evlog/express'
import { createAxiomDrain } from 'evlog/axiom'

app.use(evlog({ drain: createAxiomDrain() }))
```

```typescript [Fastify]src/index.ts
import { evlog } from 'evlog/fastify'
import { createAxiomDrain } from 'evlog/axiom'

await app.register(evlog, { drain: createAxiomDrain() })
```

```typescript [Elysia]src/index.ts
import { evlog } from 'evlog/elysia'
import { createAxiomDrain } from 'evlog/axiom'

app.use(evlog({ drain: createAxiomDrain() }))
```

```typescript [NestJS]src/app.module.ts
import { EvlogModule } from 'evlog/nestjs'
import { createAxiomDrain } from 'evlog/axiom'

EvlogModule.forRoot({ drain: createAxiomDrain() })
```

```typescript [oRPC]server/orpc.ts
import { withEvlog } from 'evlog/orpc'
import { createAxiomDrain } from 'evlog/axiom'

const handler = withEvlog(new RPCHandler(router), { drain: createAxiomDrain() })
```

```typescript [Cloudflare Workers]src/worker.ts
import { withEvlog } from 'evlog/workers'
import { createAxiomDrain } from 'evlog/axiom'

export default withEvlog(handler, { drain: createAxiomDrain() })
```

```typescript [Standalone]src/index.ts
import { initLogger } from 'evlog'
import { createAxiomDrain } from 'evlog/axiom'

initLogger({ drain: createAxiomDrain() })
```

</framework-tabs>

在支持 `waitUntil` 的平台（Cloudflare Workers、Vercel Edge）上，传输在响应发送后执行以避免增加延迟。在传统服务器上，传输会被等待以防止在服务器无服务器冷启动时丢失事件。

## 了解每个钩子的运行时机

<table>
<thead>
  <tr>
    <th>
      顺序
    </th>
    
    <th>
      钩子
    </th>
    
    <th>
      时机
    </th>
    
    <th>
      目的
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      1
    </td>
    
    <td>
      <code>
        evlog:emit:keep
      </code>
    </td>
    
    <td>
      请求结束后，采样前
    </td>
    
    <td>
      基于结果强制保留事件
    </td>
  </tr>
  
  <tr>
    <td>
      2
    </td>
    
    <td>
      <code>
        evlog:enrich
      </code>
    </td>
    
    <td>
      发送后、控制台输出和 drain 前
    </td>
    
    <td>
      向事件添加派生上下文
    </td>
  </tr>
  
  <tr>
    <td>
      3
    </td>
    
    <td>
      <code>
        evlog:drain
      </code>
    </td>
    
    <td>
      丰富化后
    </td>
    
    <td>
      将事件发送到外部服务
    </td>
  </tr>
</tbody>
</table>

## 查看错误路径在哪里分叉

两条路径在相同的发送／丰富化／传输流水线中汇聚。唯一区别在于**何时触发发送**：

<table>
<thead>
  <tr>
    <th>
      
    </th>
    
    <th>
      成功
    </th>
    
    <th>
      错误
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <strong>
        触发时机
      </strong>
    </td>
    
    <td>
      <code>
        afterResponse
      </code>
      
       / <code>
        response
      </code>
      
       钩子
    </td>
    
    <td>
      <code>
        error
      </code>
      
       钩子
    </td>
  </tr>
  
  <tr>
    <td>
      <strong>
        级别
      </strong>
    </td>
    
    <td>
      <code>
        info
      </code>
      
      （或状态码 >= 400 时为 <code>
        warn
      </code>
      
      ）
    </td>
    
    <td>
      <code>
        error
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <strong>
        状态码
      </strong>
    </td>
    
    <td>
      来自响应
    </td>
    
    <td>
      来自错误的 <code>
        status
      </code>
      
       字段（默认为 500）
    </td>
  </tr>
  
  <tr>
    <td>
      <strong>
        错误上下文
      </strong>
    </td>
    
    <td>
      无
    </td>
    
    <td>
      <code>
        error
      </code>
      
       字段，包含消息、堆栈、<code>
        why
      </code>
      
      、<code>
        fix
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <strong>
        重复发送保护
      </strong>
    </td>
    
    <td>
      检查 <code>
        _evlogEmitted
      </code>
      
       标志
    </td>
    
    <td>
      设置 <code>
        _evlogEmitted = true
      </code>
    </td>
  </tr>
</tbody>
</table>

## 跟踪简单的日志调用

使用 `log` 单例时，流水线更短：

1. **调用**：`log.info({ action: 'deploy' })` 或 `log.info('tag', 'message')`
2. **发送**：事件立即构建并输出
3. **传输**：如果通过 `initLogger()` 配置了全局 `drain`，事件会被发送到外部服务

标记日志（`log.info('tag', 'message')`）在纯文本模式下仅输出到控制台。对象形式日志（`log.info({ ... })`）始终通过传输流水线。

## 跟踪独立的宽事件

使用 `createLogger()` 在框架外使用时：

1. **创建**：`createLogger({ jobId: 'sync-001' })`
2. **积累**：`log.set()`、`log.info()`、`log.warn()`、`log.error()` 在整个操作过程中
3. **发送**：手动调用 `log.emit()`
4. **采样**：头部采样基于计算出的级别应用。尾部采样通过 `initLogger({ sampling: { keep: [...] } })` 配置
5. **传输**：如果配置了全局 `drain`，事件会被发送

```typescript [scripts/migrate.ts]
import { initLogger, createLogger } from 'evlog'
import { createAxiomDrain } from 'evlog/axiom'

initLogger({
  env: { service: 'worker' },
  drain: createAxiomDrain(),
  sampling: { rates: { info: 10 } },
})

const log = createLogger({ task: 'migrate' })
log.set({ records: 500, status: 'complete' })
log.emit()
```

## 下一步

- [宽事件](https://evlog.zhcndoc.com/learn/wide-events) - 设计有效的宽事件
- [采样](https://evlog.zhcndoc.com/learn/sampling) - 配置头部和尾部采样
- [适配器](https://evlog.zhcndoc.com/integrate/adapters/overview) - 将事件发送到外部平台
- [丰富器](https://evlog.zhcndoc.com/use-cases/enrichers) - 自动添加派生上下文。

---

- [宽事件](https://evlog.zhcndoc.com/learn/wide-events)
- [采样](https://evlog.zhcndoc.com/learn/sampling)


## Sitemap

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