---
title: "增强器"
description: "为每个广泛事件添加派生上下文，从 user agent 和地理位置到你自己的计算字段。"
canonical_url: "https://evlog.zhcndoc.com/use-cases/enrichers"
---
# 增强器

> 为每个广泛事件添加派生上下文，从 user agent 和地理位置到你自己的计算字段。

增强器会在广泛事件发出后、打印之前以及到达你的 drain 适配器之前，为其添加派生上下文。可用它们自动从请求头中提取有用信息，而无需让应用代码变得杂乱。

所有内置增强器都从 `evlog/enrichers` 导出。每个增强器都是一个工厂函数，会返回一个 `(ctx: EnrichContext) => void` 回调。若要编写你自己的增强器，请参阅 [自定义增强器](https://evlog.zhcndoc.com/extend/custom-enrichers)。

<prompt :actions="["copy","cursor","claude"]" description="添加所有内置 evlog 增强器" icon="i-lucide-puzzle">

将所有内置增强器添加到我的 evlog 设置中。

1. 确定我正在使用的框架并遵循其 evlog 集成模式
2. 从 'evlog/enrichers' 导入 createUserAgentEnricher、createGeoEnricher、createRequestSizeEnricher 和 createTraceContextEnricher
3. 将增强器集成到框架的增强配置中
4. 增强器为广泛事件添加 userAgent、geo、requestSize 和 traceContext 字段
5. 所有增强器都接受 { overwrite?: boolean } - 默认为 false 以保留用户设置的数据

Enricher 文档：[https://www.evlog.dev/use-cases/enrichers](https://www.evlog.dev/use-cases/enrichers)
框架设置：[https://www.evlog.dev/integrate/frameworks/overview](https://www.evlog.dev/integrate/frameworks/overview)

</prompt>

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

## 所有内置增强器

使用 `createDefaultEnrichers()` 一次性组合用户代理、地理位置、请求大小和跟踪上下文：

<framework-tabs>

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

const enrich = createDefaultEnrichers()

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

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

const enrich = createDefaultEnrichers()

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

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

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

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

const enrich = createDefaultEnrichers()
export const { handle, handleError } = createEvlogHooks({ enrich: enrich })
```

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

const enrich = createDefaultEnrichers()

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

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

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

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

const enrich = createDefaultEnrichers()
app.use(evlog({ enrich: enrich }))
```

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

const enrich = createDefaultEnrichers()
app.use(evlog({ enrich: enrich }))
```

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

const enrich = createDefaultEnrichers()
await app.register(evlog, { enrich: enrich })
```

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

const enrich = createDefaultEnrichers()
app.use(evlog({ enrich: enrich }))
```

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

const enrich = createDefaultEnrichers()
EvlogModule.forRoot({ enrich: enrich })
```

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

const enrich = createDefaultEnrichers()
const handler = withEvlog(new RPCHandler(router), { enrich: enrich })
```

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

const enrich = createDefaultEnrichers()
export default withEvlog(handler, { enrich: enrich })
```

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

const enrich = createDefaultEnrichers()
initLogger({ enrich: enrich })
```

</framework-tabs>

每个增强器都接受 `{ overwrite?: boolean }`（默认值为 `false`），因此用户设置的字段会被保留。

## `userAgentEnricher()`

从 `User-Agent` 标头解析浏览器、操作系统和设备类型。

**设置：** `event.userAgent`

```typescript [user-agent-enricher.ts]
const enrich = createUserAgentEnricher()
```

**输出结构：**

```typescript [user-agent-types.ts]
interface UserAgentInfo {
  raw: string                                      // 原始 User-Agent 字符串
  browser?: { name: string; version?: string }     // Chrome、Firefox、Safari、Edge
  os?: { name: string; version?: string }          // Windows、macOS、iOS、Android、Linux
  device?: { type: 'mobile' | 'tablet' | 'desktop' | 'bot' | 'unknown' }
}
```

**示例输出：**

```json [示例宽事件：userAgent]
{
  "userAgent": {
    "raw": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 Chrome/120.0.0.0",
    "browser": { "name": "Chrome", "version": "120.0.0.0" },
    "os": { "name": "macOS", "version": "10.15.7" },
    "device": { "type": "desktop" }
  }
}
```

**检测到的浏览器：** Edge、Chrome、Firefox、Safari（按顺序检查，Edge 在 Chrome 之前以避免错误匹配）。

**检测到的设备：** 机器人（爬虫、蜘蛛）、平板（iPad）、手机（iPhone、Android 手机）、桌面（备用）。

## `geoEnricher()`

从平台注入的头部提取地理位置数据。

**设置：** `event.geo`

```typescript [geo-enricher.ts]
const enrich = createGeoEnricher()
```

**输出结构：**

```typescript [geo-types.ts]
interface GeoInfo {
  country?: string      // ISO 国家代码（例如："US"、"FR"）
  region?: string       // 区域/州名称
  regionCode?: string   // 区域代码
  city?: string         // 城市名称
  latitude?: number     // 十进制纬度
  longitude?: number    // 十进制经度
}
```

**支持的平台：**

<table>
<thead>
  <tr>
    <th>
      平台
    </th>
    
    <th>
      头部
    </th>
    
    <th>
      覆盖范围
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      Vercel
    </td>
    
    <td>
      <code>
        x-vercel-ip-country
      </code>
      
      , <code>
        x-vercel-ip-country-region
      </code>
      
      , <code>
        x-vercel-ip-city
      </code>
      
      , <code>
        x-vercel-ip-latitude
      </code>
      
      , <code>
        x-vercel-ip-longitude
      </code>
    </td>
    
    <td>
      完整
    </td>
  </tr>
  
  <tr>
    <td>
      Cloudflare
    </td>
    
    <td>
      <code>
        cf-ipcountry
      </code>
    </td>
    
    <td>
      仅国家
    </td>
  </tr>
</tbody>
</table>

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

**Cloudflare 注：** 只有 `cf-ipcountry` 是标准的 Cloudflare HTTP 标头。其他地理字段（`city`、`region`、`latitude` 等）是 `request.cf` 的属性，而不是作为标头暴露。若要获取完整的 Cloudflare 地理数据，请编写一个读取 `request.cf` 的 [自定义增强器](https://evlog.zhcndoc.com/extend/custom-enrichers)，或者使用 Workers 中间件将 `cf` 属性复制到自定义标头中。

</callout>

## `requestSizeEnricher()`

从 `Content-Length` 头部捕获请求和响应有效负载大小。

**设置：** `event.requestSize`

```typescript [request-size-enricher.ts]
const enrich = createRequestSizeEnricher()
```

**输出结构：**

```typescript [request-size-types.ts]
interface RequestSizeInfo {
  requestBytes?: number    // 请求 Content-Length
  responseBytes?: number   // 响应 Content-Length
}
```

**示例输出：**

```json [示例宽事件：requestSize]
{
  "requestSize": {
    "requestBytes": 1234,
    "responseBytes": 5678
  }
}
```

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

此增强器读取请求和响应的 `Content-Length` 头部。如果头部缺失（例如分块传输编码），对应字段将为 `undefined`。

</callout>

## `traceContextEnricher()`

从 `traceparent` 和 `tracestate` 头部提取 W3C 跟踪上下文。

**设置：** `event.traceContext`、`event.traceId`、`event.parentSpanId`

```typescript [trace-context-enricher.ts]
const enrich = createTraceContextEnricher()
```

**输出结构：**

```typescript [trace-context-types.ts]
interface TraceContextInfo {
  traceparent?: string   // Full traceparent header value
  tracestate?: string    // Full tracestate header value
  traceId?: string       // 32-char hex trace ID (parsed from traceparent)
  spanId?: string        // Span of this request, copied from event.spanId when already set
  parentSpanId?: string  // 16-char hex span ID of the caller (parsed from traceparent)
}
```

**示例输出：**

```json [示例宽事件：traceContext]
{
  "traceContext": {
    "traceparent": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
    "traceId": "4bf92f3577b34da6a3ce929d0e0e4736",
    "parentSpanId": "00f067aa0ba902b7"
  },
  "traceId": "4bf92f3577b34da6a3ce929d0e0e4736",
  "parentSpanId": "00f067aa0ba902b7"
}
```

`traceId` 和 `parentSpanId` 也会在事件顶层设置，便于查询和关联。

传入 `traceparent` 中的 span id 属于发起调用的服务，而不是当前处理的请求，因此丰富化器会将其记录为 `parentSpanId`，并保留 `event.spanId` 不变。请使用追踪器的活动 span 设置 `event.spanId`（使用 OpenTelemetry 时可写作 `trace.getActiveSpan()?.spanContext().spanId`），以便将事件关联到服务器 span。

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

traceparent 格式遵循 [W3C 跟踪上下文](https://www.w3.org/TR/trace-context/) 规范：`{version}-{traceId}-{spanId}-{flags}`。

</callout>

## 同时接入全部四个增强器

同时使用所有内置增强器。所有框架中的增强器列表都相同，只有接入方式不同。

<framework-tabs>

```typescript [Nuxt]server/plugins/evlog-enrich.ts
import {

  createUserAgentEnricher,
  createGeoEnricher,
  createRequestSizeEnricher,
  createTraceContextEnricher,
} from 'evlog/enrichers'
import { composeEnrichers } from 'evlog/toolkit'

export default defineNitroPlugin((nitroApp) => {
  nitroApp.hooks.hook('evlog:enrich', composeEnrichers([
    createUserAgentEnricher(),
    createGeoEnricher(),
    createRequestSizeEnricher(),
    createTraceContextEnricher(),
  ]))
})
```

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

  createUserAgentEnricher,
  createGeoEnricher,
  createRequestSizeEnricher,
  createTraceContextEnricher,
} from 'evlog/enrichers'
import { composeEnrichers } from 'evlog/toolkit'

export default definePlugin((nitroApp) => {
  nitroApp.hooks.hook('evlog:enrich', composeEnrichers([
    createUserAgentEnricher(),
    createGeoEnricher(),
    createRequestSizeEnricher(),
    createTraceContextEnricher(),
  ]))
})
```

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

  createUserAgentEnricher,
  createGeoEnricher,
  createRequestSizeEnricher,
  createTraceContextEnricher,
} from 'evlog/enrichers'
import { composeEnrichers } from 'evlog/toolkit'

export const { withEvlog, useLogger, log, createError } = createEvlog({
  service: 'my-app',
  enrich: composeEnrichers([
    createUserAgentEnricher(),
    createGeoEnricher(),
    createRequestSizeEnricher(),
    createTraceContextEnricher(),
  ]),
})
```

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

  createUserAgentEnricher,
  createGeoEnricher,
  createRequestSizeEnricher,
  createTraceContextEnricher,
} from 'evlog/enrichers'

const enrich = composeEnrichers([
  createUserAgentEnricher(),
  createGeoEnricher(),
  createRequestSizeEnricher(),
  createTraceContextEnricher(),
])

export const { handle, handleError } = createEvlogHooks({ enrich })
```

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

  createUserAgentEnricher,
  createGeoEnricher,
  createRequestSizeEnricher,
  createTraceContextEnricher,
} from 'evlog/enrichers'
import { composeEnrichers } from 'evlog/toolkit'

export default definePlugin((nitroApp) => {
  nitroApp.hooks.hook('evlog:enrich', composeEnrichers([
    createUserAgentEnricher(),
    createGeoEnricher(),
    createRequestSizeEnricher(),
    createTraceContextEnricher(),
  ]))
})
```

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

  createUserAgentEnricher,
  createGeoEnricher,
  createRequestSizeEnricher,
  createTraceContextEnricher,
} from 'evlog/enrichers'

const enrich = composeEnrichers([
  createUserAgentEnricher(),
  createGeoEnricher(),
  createRequestSizeEnricher(),
  createTraceContextEnricher(),
])

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

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

  createUserAgentEnricher,
  createGeoEnricher,
  createRequestSizeEnricher,
  createTraceContextEnricher,
} from 'evlog/enrichers'

const enrich = composeEnrichers([
  createUserAgentEnricher(),
  createGeoEnricher(),
  createRequestSizeEnricher(),
  createTraceContextEnricher(),
])

app.use(evlog({ enrich }))
```

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

  createUserAgentEnricher,
  createGeoEnricher,
  createRequestSizeEnricher,
  createTraceContextEnricher,
} from 'evlog/enrichers'

const enrich = composeEnrichers([
  createUserAgentEnricher(),
  createGeoEnricher(),
  createRequestSizeEnricher(),
  createTraceContextEnricher(),
])

app.use(evlog({ enrich }))
```

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

  createUserAgentEnricher,
  createGeoEnricher,
  createRequestSizeEnricher,
  createTraceContextEnricher,
} from 'evlog/enrichers'

const enrich = composeEnrichers([
  createUserAgentEnricher(),
  createGeoEnricher(),
  createRequestSizeEnricher(),
  createTraceContextEnricher(),
])

await app.register(evlog, { enrich })
```

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

  createUserAgentEnricher,
  createGeoEnricher,
  createRequestSizeEnricher,
  createTraceContextEnricher,
} from 'evlog/enrichers'

const enrich = composeEnrichers([
  createUserAgentEnricher(),
  createGeoEnricher(),
  createRequestSizeEnricher(),
  createTraceContextEnricher(),
])

app.use(evlog({ enrich }))
```

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

  createUserAgentEnricher,
  createGeoEnricher,
  createRequestSizeEnricher,
  createTraceContextEnricher,
} from 'evlog/enrichers'

const enrich = composeEnrichers([
  createUserAgentEnricher(),
  createGeoEnricher(),
  createRequestSizeEnricher(),
  createTraceContextEnricher(),
])

EvlogModule.forRoot({ enrich })
```

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

  createUserAgentEnricher,
  createGeoEnricher,
  createRequestSizeEnricher,
  createTraceContextEnricher,
} from 'evlog/enrichers'

const enrich = composeEnrichers([
  createUserAgentEnricher(),
  createGeoEnricher(),
  createRequestSizeEnricher(),
  createTraceContextEnricher(),
])

const handler = withEvlog(new RPCHandler(router), { enrich })
```

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

  createUserAgentEnricher,
  createGeoEnricher,
  createRequestSizeEnricher,
  createTraceContextEnricher,
} from 'evlog/enrichers'

const enrich = composeEnrichers([
  createUserAgentEnricher(),
  createGeoEnricher(),
  createRequestSizeEnricher(),
  createTraceContextEnricher(),
])

export default withEvlog(handler, { enrich })
```

```typescript [Standalone]index.ts
import { initLogger } from 'evlog'
import { composeEnrichers, enricherPlugin } from 'evlog/toolkit'
import {

  createUserAgentEnricher,
  createGeoEnricher,
  createRequestSizeEnricher,
  createTraceContextEnricher,
} from 'evlog/enrichers'

initLogger({
  plugins: [
    enricherPlugin('defaults', composeEnrichers([
      createUserAgentEnricher(),
      createGeoEnricher(),
      createRequestSizeEnricher(),
      createTraceContextEnricher(),
    ])),
  ],
})
```

</framework-tabs>

## 下一步

- [自定义增强器](https://evlog.zhcndoc.com/extend/custom-enrichers) - 编写你自己的增强器
- [适配器](https://evlog.zhcndoc.com/integrate/adapters/overview) - 将增强后的事件发送到外部服务。

---

- [自定义增强器](https://evlog.zhcndoc.com/extend/custom-enrichers)


## Sitemap

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