---
title: "尾部采样"
description: "在了解事件状态、持续时间和错误后再决定是否保留事件。保留所有错误，丢弃健康流量中的噪声"
canonical_url: "https://evlog.zhcndoc.com/extend/tail-sampling"
---
# 尾部采样

> 在了解事件状态、持续时间和错误后再决定是否保留事件。保留所有错误，丢弃健康流量中的噪声

<tail-sample-decision>



</tail-sample-decision>

**尾部采样**是在请求运行结束后，充分了解其结果（状态、持续时间、错误和自定义标志）后做出的决策。它可以让你保留所有错误和慢请求，同时丢弃大部分健康流量；这与头部采样相反，后者会在事先、不知道结果的情况下做出决策。

完整的理论和配置参考，包括内置的 `keep` 规则、通过 `evlog:emit:keep` 自定义谓词，以及组合头部采样与尾部采样，详见[采样](https://evlog.zhcndoc.com/learn/sampling)。本页面介绍**扩展接口**：如何将你自己的保留逻辑接入管道。

<prompt :actions="["copy","cursor","claude"]" description="在 evlog 上配置尾部采样" icon="i-lucide-filter">

设置尾部采样，以便保留所有错误和慢请求，同时丢弃健康流量中的噪声。

- 从内置的声明式规则开始：`evlog.sampling.keep = { status: '>=400', duration: '>1000', path: ['/api/auth/*'] }`
- 对于多字段或派生条件，注册 `evlog:emit:keep` 钩子（Nitro：`nitroApp.hooks.hook('evlog:emit:keep', (ctx) => ...)`）；设置 `ctx.shouldKeep = true` 以进行保留
- 保持钩子运行快速。它会在丰富处理完成后对每个请求运行，因此不要执行 I/O 和异步操作
- 通过同时设置 `sample`（头部）和 `keep`（尾部），与头部采样组合使用（例如保留 10% 的健康流量）
- 始终保留错误事件（`level: 'error'`），不受采样影响；再次检查规则，确保不会意外丢弃这些事件

文档：[https://www.evlog.dev/extend/tail-sampling](https://www.evlog.dev/extend/tail-sampling)

</prompt>

## 当内置规则还不够用时

内置的声明式 `keep` 规则覆盖了典型场景（状态码阈值、持续时间阈值、路径匹配、级别匹配）。当你需要以下能力时，请切换到自定义钩子：

- **对多个字段进行条件逻辑判断**（例如：“如果 `status >= 500` 且 `user.plan === 'enterprise'`，则保留”）
- **基于派生值保留**（例如：“如果 `event.audit?.context.actor.role === 'admin'`，则保留”）
- **有状态的决策**（较少见；需要谨慎，因为采样运行在热路径上）

## 自定义保留钩子

无论框架如何，钩子签名都是相同的。具体接线方式取决于你的运行时。

<framework-tabs>

```ts [Nuxt]
nitroApp.hooks.hook('evlog:emit:keep', (ctx) => {
  if (ctx.context.user?.plan === 'enterprise' && ctx.status >= 500) {
    ctx.shouldKeep = true
  }
})
```

```ts [Nitro]
nitroApp.hooks.hook('evlog:emit:keep', (ctx) => {
  if (ctx.context.user?.plan === 'enterprise' && ctx.status >= 500) {
    ctx.shouldKeep = true
  }
})
```

```ts [插件（任意框架）]
import { definePlugin } from 'evlog/toolkit'

export const keepEnterpriseErrors = definePlugin({
  name: 'keep-enterprise-errors',
  keep(ctx) {
    if (ctx.context.user?.plan === 'enterprise' && ctx.status >= 500) {
      ctx.shouldKeep = true
    }
  },
})

// 然后：initLogger({ plugins: [keepEnterpriseErrors] })
//   或：app.use(evlog({ plugins: [keepEnterpriseErrors] }))
```

</framework-tabs>

对于复杂逻辑，推荐使用插件形式，因为它会与 evlog 配置的其他部分（排出器、丰富器）一起使用，并且可以跨框架复用。

## 组合多个保留谓词

使用 `evlog/toolkit` 中的 `composeKeep` 将多个谓词组合成一个钩子。每个谓词独立运行，如果其中任意一个将最终的 `shouldKeep` 设为 `true`，则结果为 `true`：

```ts
import { composeKeep } from 'evlog/toolkit'

const keep = composeKeep([
  ({ duration, shouldKeep }) => duration && duration > 2000 ? true : shouldKeep,
  ({ event }) => event.level === 'error',
  ({ context, status }) => context.user?.plan === 'enterprise' && status >= 500,
])
```

单个谓词中的错误是隔离的（会以 `[evlog/keep]` 前缀记录），因此有缺陷的谓词不会在不知不觉中丢弃合法事件。

## 你将收到什么

保留钩子会接收一个 `TailSamplingContext`：

```ts
interface TailSamplingContext {
  /** 事件级别（debug | info | warn | error） */
  level: string
  /** HTTP 响应状态（如果已知） */
  status?: number
  /** 请求持续时间（毫秒），如果已测量 */
  duration?: number
  /** 完整的累计上下文（所有 log.set 的内容） */
  context: Record<string, unknown>
  /** 完全丰富后的、准备排出的事件 */
  event: WideEvent
  /** 可变：设为 true 以强制保留此事件 */
  shouldKeep: boolean
}
```

将 `shouldKeep = true` 会强制让事件通过。将 `shouldKeep = false` 则无效（其他谓词仍可能保留它；默认由头部采样器决定）。

## 下一步

- [采样](https://evlog.zhcndoc.com/learn/sampling)：头部采样、尾部采样、内置的声明式 `keep` 规则
- [插件](https://evlog.zhcndoc.com/extend/plugins)：何时应将保留逻辑放入多钩子插件中
- [最佳实践](https://evlog.zhcndoc.com/reference/best-practices)：保留所有错误，再次检查规则

---

- [采样参考](https://evlog.zhcndoc.com/learn/sampling)
- [插件](https://evlog.zhcndoc.com/extend/plugins)


## Sitemap

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