---
title: "NuxtHub 存储"
description: "使用 NuxtHub 数据库存储实现自托管保留。无需外部依赖即可存储、查询和清理宽事件。"
canonical_url: "https://evlog.zhcndoc.com/integrate/adapters/self-hosted/nuxthub"
---
# NuxtHub 存储

> 使用 NuxtHub 数据库存储实现自托管保留。无需外部依赖即可存储、查询和清理宽事件。

`@evlog/nuxthub` 将你的 evlog 宽事件直接存储在 NuxtHub 数据库中。不需要外部日志服务。你的日志与数据存放在一起，并基于保留策略自动清理。

<prompt :actions="["copy","cursor","claude"]" description="将 evlog 宽事件存储在 NuxtHub 中" icon="i-simple-icons-nuxt">

将 evlog 宽事件存储到我的 NuxtHub 数据库中（自托管日志保留）。

- 同时安装这两个模块：pnpm add @nuxthub/core @evlog/nuxthub
- 将 @nuxthub/core 和 @evlog/nuxthub 添加到 nuxt.config.ts 的 modules 中（按这个顺序）
- 在 nuxt.config.ts 中启用 hub.database = true
- 配置 evlog.nuxthub: { retentionDays, batchSize, ... } 用于保留与批处理
- 运行数据库迁移，以创建 wide-events 表
- 在触发请求后，确认宽事件已写入我的 NuxtHub 数据库
- 在生产环境大规模使用时，结合外部 drain（Axiom / OTLP）进行长期存储

文档：[https://www.evlog.dev/integrate/adapters/self-hosted/nuxthub](https://www.evlog.dev/integrate/adapters/self-hosted/nuxthub)
NuxtHub：[https://hub.nuxt.com](https://hub.nuxt.com)

</prompt>

## 为什么要自托管日志？

外部日志服务（如 Axiom、Datadog 等）在大规模生产环境中表现很好。但有时你可能希望：

- **零外部依赖** - 日志存储在与应用相同的数据库中
- **完全的数据所有权** - 第三方无法访问你的日志数据
- **免费层级友好** - 没有按事件计费，仅使用你现有的数据库
- **开发与预发布环境** - 无需付费即可获得完整的日志可见性

`@evlog/nuxthub` 可以作为一个即插即用的“数据排出器”（drain）。你现有的 evlog 设置保持不变，只需要在上方添加一层数据库支持。

## 安装

<code-group>

```bash [pnpm]
pnpm add @nuxthub/core @evlog/nuxthub
```

```bash [bun]
bun add @nuxthub/core @evlog/nuxthub
```

```bash [yarn]
yarn add @nuxthub/core @evlog/nuxthub
```

```bash [npm]
npm install @nuxthub/core @evlog/nuxthub
```

</code-group>

或者使用 `nuxi`：

```bash [Terminal]
npx nuxi module add @nuxthub/core @evlog/nuxthub
```

## 配置

将模块添加到你的 `nuxt.config.ts`：

```typescript [nuxt.config.ts]
export default defineNuxtConfig({
  modules: ['@nuxthub/core', '@evlog/nuxthub'],

  evlog: {
    retention: '7d',
  },
})
```

即使 `@evlog/nuxthub` 可以自动注册缺失的模块，我们仍然建议显式安装 `@nuxthub/core` 并在 `modules` 中注册它，以获得更清晰、更可预测的配置。

仅此而已。该模块会自动完成以下操作：

1. 如果尚未注册，则安装 `evlog/nuxt` 和 `@nuxthub/core`
2. 使用 NuxtHub 注册 `evlog_events` 数据库架构
3. 挂钩到 `evlog:drain` 以将每个事件存储在数据库中
4. 根据你的保留策略安排清理任务

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

**前提条件：** 你的项目必须使用 [NuxtHub](https://hub.nuxt.com) 并配置了数据库。`@evlog/nuxthub` 使用 Drizzle ORM 与数据库交互。

</callout>

## 工作原理

```text
请求 → evlog 宽事件 → evlog:drain 钩子 → 插入到 evlog_events 表
                                                          ↓
                定时任务（自动） → 删除早于保留策略的事件
```

evlog 发出的每个宽事件都会作为一行存储在 `evlog_events` 表中。该 drain 插件同时处理单个事件和批量事件（当与 [pipeline](https://evlog.zhcndoc.com/extend/drain-pipeline) 一起使用时）。

### 数据库架构

`evlog_events` 表存储用于快速查询的索引列，以及包含所有其他字段的 `data` JSON 列：

<table>
<thead>
  <tr>
    <th>
      列名
    </th>
    
    <th>
      类型
    </th>
    
    <th>
      描述
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        id
      </code>
    </td>
    
    <td>
      <code>
        text
      </code>
    </td>
    
    <td>
      UUID 主键
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        timestamp
      </code>
    </td>
    
    <td>
      <code>
        text
      </code>
    </td>
    
    <td>
      事件时间戳
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        level
      </code>
    </td>
    
    <td>
      <code>
        text
      </code>
    </td>
    
    <td>
      日志级别（info、warn、error、debug）
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        service
      </code>
    </td>
    
    <td>
      <code>
        text
      </code>
    </td>
    
    <td>
      服务名称
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        environment
      </code>
    </td>
    
    <td>
      <code>
        text
      </code>
    </td>
    
    <td>
      环境（生产、预发布等）
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        method
      </code>
    </td>
    
    <td>
      <code>
        text
      </code>
    </td>
    
    <td>
      HTTP 方法
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        path
      </code>
    </td>
    
    <td>
      <code>
        text
      </code>
    </td>
    
    <td>
      请求路径
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        status
      </code>
    </td>
    
    <td>
      <code>
        integer
      </code>
    </td>
    
    <td>
      HTTP 状态码
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        duration_ms
      </code>
    </td>
    
    <td>
      <code>
        integer
      </code>
    </td>
    
    <td>
      请求持续时间（毫秒）
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        request_id
      </code>
    </td>
    
    <td>
      <code>
        text
      </code>
    </td>
    
    <td>
      请求关联 ID
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        source
      </code>
    </td>
    
    <td>
      <code>
        text
      </code>
    </td>
    
    <td>
      事件来源（服务器、客户端）
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        error
      </code>
    </td>
    
    <td>
      <code>
        text
      </code>
    </td>
    
    <td>
      错误详情（JSON 字符串）
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        data
      </code>
    </td>
    
    <td>
      <code>
        text
      </code>
    </td>
    
    <td>
      所有剩余事件字段（JSON）
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        created_at
      </code>
    </td>
    
    <td>
      <code>
        text
      </code>
    </td>
    
    <td>
      行插入时间戳
    </td>
  </tr>
</tbody>
</table>

索引列：`timestamp`、`level`、`service`、`status`、`request_id`、`created_at`。

### 方言支持

架构会根据你的 NuxtHub 数据库方言自动注册：

- **SQLite**（Cloudflare D1 的默认值）
- **MySQL**
- **PostgreSQL**

根据你的 NuxtHub 配置，会通过 `hub:db:schema:extend` 钩子自动选择正确的架构。

## 与外部适配器结合使用

`@evlog/nuxthub` 不会取代外部适配器，你可以同时使用两者。该模块注册了自己的 `evlog:drain` 钩子，因此你拥有的其他任何 drain 插件仍可正常工作：

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

export default defineNitroPlugin((nitroApp) => {
  // 这将与 @evlog/nuxthub 内置的排出器并行运行
  nitroApp.hooks.hook('evlog:drain', createAxiomDrain())
})
```

## 保留

`@evlog/nuxthub` 会根据你的保留策略自动删除旧事件。无需手动清理。

### 配置

在你的 `nuxt.config.ts` 中设置保留期：

```typescript [nuxt.config.ts]
export default defineNuxtConfig({
  modules: ['@nuxthub/core', '@evlog/nuxthub'],

  evlog: {
    retention: '7d', // 默认值
  },
})
```

保留值由一个数字后跟单位组成：

<table>
<thead>
  <tr>
    <th>
      单位
    </th>
    
    <th>
      描述
    </th>
    
    <th>
      示例
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        d
      </code>
    </td>
    
    <td>
      天
    </td>
    
    <td>
      <code>
        7d
      </code>
      
       = 7 天
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        h
      </code>
    </td>
    
    <td>
      小时
    </td>
    
    <td>
      <code>
        24h
      </code>
      
       = 24 小时
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        m
      </code>
    </td>
    
    <td>
      分钟
    </td>
    
    <td>
      <code>
        60m
      </code>
      
       = 60 分钟
    </td>
  </tr>
</tbody>
</table>

### 清理如何工作

该模块注册了一个 Nitro 定时任务（`evlog:cleanup`），它会按照基于你的保留值推导出的 cron 计划运行。cron 频率大约设为保留期的一半：

<table>
<thead>
  <tr>
    <th>
      保留期
    </th>
    
    <th>
      Cron 计划
    </th>
    
    <th>
      描述
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        60m
      </code>
    </td>
    
    <td>
      <code>
        */30 * * * *
      </code>
    </td>
    
    <td>
      每 30 分钟一次
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        24h
      </code>
    </td>
    
    <td>
      <code>
        0 */12 * * *
      </code>
    </td>
    
    <td>
      每 12 小时一次
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        7d
      </code>
    </td>
    
    <td>
      <code>
        0 3 * * *
      </code>
    </td>
    
    <td>
      每天凌晨 3:00
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        30d
      </code>
    </td>
    
    <td>
      <code>
        0 3 * * *
      </code>
    </td>
    
    <td>
      每天凌晨 3:00
    </td>
  </tr>
</tbody>
</table>

清理任务会删除 `evlog_events` 中所有 `created_at` 早于保留期的行。

### 手动清理

你可以通过 API 端点手动触发清理：

```bash [Terminal]
curl https://your-app.com/api/_cron/evlog-cleanup
```

如果设置了 `CRON_SECRET` 环境变量，该端点需要 Bearer token：

```bash [Terminal]
curl -H "Authorization: Bearer your-secret" \
  https://your-app.com/api/_cron/evlog-cleanup
```

建议在生产部署中这样做，以防止未经授权的清理触发。

### Vercel Cron

使用 `nuxi module add` 安装该模块时，会提示你创建一个包含合适 cron 计划的 `vercel.json`：

```json [vercel.json]
{
  "crons": [
    {
      "path": "/api/_cron/evlog-cleanup",
      "schedule": "0 3 * * *"
    }
  ]
}
```

在 Vercel 上，`CRON_SECRET` 环境变量会自动设置并验证。

### Cloudflare 与其他平台

在 Cloudflare Workers 和其他平台上，Nitro 定时任务会自动处理清理，无需额外的 cron 配置。该任务在 Nitro 配置中启用了 `experimental.tasks` 后注册。

## 下一步

- [适配器](https://evlog.zhcndoc.com/integrate/adapters/overview) - 将日志发送到外部服务，同时使用 NuxtHub 存储
- [管道](https://evlog.zhcndoc.com/extend/drain-pipeline) - 对事件进行批处理，以获得更好的数据库性能。

---

- [NuxtHub](https://hub.nuxt.com)
- [适配器](https://evlog.zhcndoc.com/integrate/adapters/overview)


## Sitemap

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