---
title: "SvelteKit"
description: "一次性连接 handle 和 handleError 钩子，每个 SvelteKit 请求都会在响应结束前通过 `event.locals.log` 携带一个日志记录器"
canonical_url: "https://evlog.zhcndoc.com/integrate/frameworks/sveltekit"
---
# SvelteKit

> 一次性连接 handle 和 handleError 钩子，每个 SvelteKit 请求都会在响应结束前通过 `event.locals.log` 携带一个日志记录器

`evlog/sveltekit` 适配器提供 `handle` 和 `handleError` 钩子，这些钩子会自动创建请求作用域的日志记录器，可通过 `event.locals.log` 和 `useLogger()` 访问，并在请求完成时发送宽事件。

<prompt :actions="["copy","cursor","claude"]" description="在我的 SvelteKit 应用中设置 evlog" icon="i-simple-icons-svelte">

在我的 SvelteKit 应用中设置 evlog。

- 安装 evlog：pnpm add evlog
- 创建 evlog.config.ts，其中包含 export default defineEvlog({ service: 'my-api' })
- 在 hooks.server.ts 中调用 initLogger(toLoggerConfig(config))，并从 createEvlogHooks(toMiddlewareOptions(config)) 导出 handle 和 handleError
- 可选：将 evlog/vite 插件添加到 vite.config.ts，以移除 debug 并注入源代码位置（它不会读取 evlog.config.ts）
- 在路由和服务中通过 event.locals.log 或 useLogger() 访问日志记录器
- 使用 log.set() 累积上下文，使用 createError() 抛出结构化错误
- 请求完成时自动发送广泛事件

文档：[https://www.evlog.dev/integrate/frameworks/sveltekit](https://www.evlog.dev/integrate/frameworks/sveltekit)
适配器：[https://www.evlog.dev/integrate/adapters/overview](https://www.evlog.dev/integrate/adapters/overview)

:::

## 快速开始

### 1. 安装

<code-group>

```bash [pnpm]
pnpm add evlog
```

```bash [bun]
bun add evlog
```

```bash [yarn]
yarn add evlog
```

```bash [npm]
npm install evlog
```

</code-group>

### 2. 创建 evlog.config.ts

```typescript [evlog.config.ts]
import { defineEvlog } from 'evlog'

export default defineEvlog({
  service: 'my-api',
})
```

所有选项都位于此文件中：采样、drain、`enrich` 和路由过滤器。`npx evlog init` 会为你写入它。请参阅[配置](https://evlog.zhcndoc.com/reference/configuration)。

### 3. 创建钩子

```typescript [src/hooks.server.ts]
import { initLogger, toLoggerConfig, toMiddlewareOptions } from 'evlog'
import { createEvlogHooks } from 'evlog/sveltekit'
import config from '../evlog.config'

initLogger(toLoggerConfig(config))

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

`toLoggerConfig` 会传入文件中的日志记录器选项，`toMiddlewareOptions` 会传入请求选项，因此采样、drain、`enrich` 和路由过滤器都会生效。

<callout color="info" icon="i-custom-vite">

**可选：**[`evlog/vite` 插件](https://evlog.zhcndoc.com/reference/vite-plugin)会从生产构建中移除 `log.debug()`，并注入源代码位置。它不会读取 `evlog.config.ts`，因此无需传入 service；上面的 `initLogger` 调用会在插件自动初始化后运行，并替代它。

</callout>

### 4. 为 locals 添加类型

```typescript [src/app.d.ts]
import type { RequestLogger } from 'evlog'

declare global {
  namespace App {
    interface Locals {
      log: RequestLogger
    }
  }
}

export {}
```

## 宽事件

在处理程序中逐步构建上下文。一个请求对应一个宽事件：

```typescript [src/routes/api/users/[id]/+server.ts]
import { json } from '@sveltejs/kit'
import type { RequestHandler } from './$types'

export const GET: RequestHandler = async ({ locals, params }) => {
  locals.log.set({ user: { id: params.id } })

  const user = await db.findUser(params.id)
  locals.log.set({ user: { name: user.name, plan: user.plan } })

  const orders = await db.findOrders(params.id)
  locals.log.set({ orders: { count: orders.length, totalRevenue: sum(orders) } })

  return json({ user, orders })
}
```

所有字段会在请求完成时合并为一个宽事件并发送：

```bash [终端输出]
14:58:15 INFO [my-api] GET /api/users/usr_123 200 in 12ms
  ├─ orders: count=2 totalRevenue=6298
  ├─ user: id=usr_123 name=Alice plan=pro
  └─ requestId: 4a8ff3a8-...
```

## useLogger()

使用 `useLogger()` 可以从调用栈中的任何位置访问请求作用域的日志记录器，而无需将 locals 传递到服务层：

```typescript [src/lib/services/user.ts]
import { useLogger } from 'evlog/sveltekit'

export async function findUser(id: string) {
  const log = useLogger()
  log.set({ user: { id } })

  const user = await db.findUser(id)
  log.set({ user: { name: user.name, plan: user.plan } })

  return user
}
```

```typescript [src/routes/api/users/[id]/+server.ts]
import { json } from '@sveltejs/kit'
import { findUser } from '$lib/services/user'
import type { RequestHandler } from './$types'

export const GET: RequestHandler = async ({ params }) => {
  const user = await findUser(params.id)
  return json(user)
}
```

`event.locals.log` 和 `useLogger()` 返回相同的日志记录器实例。`useLogger()` 使用 `AsyncLocalStorage` 在异步边界之间传播日志记录器。

## 后台工作 (`log.fork`)

使用 `locals.log.fork(label, fn)` 创建子宽事件。请参阅宽事件中的[发送后](https://evlog.zhcndoc.com/learn/wide-events#after-emit-sealing-and-background-work)。

```typescript [src/routes/api/orders/+server.ts]
import { useLogger } from 'evlog/sveltekit'
import type { RequestHandler } from './$types'

export const POST: RequestHandler = async ({ locals }) => {
  locals.log.fork!('process', async () => {
    const log = useLogger()
    log.set({ step: '完成' })
  })
  return new Response(JSON.stringify({ ok: true }))
}
```

## 错误处理

使用 `createError` 创建包含 `why`、`fix` 和 `link` 字段的结构化错误。`handleError` 钩子会自动捕获抛出的错误：

```typescript [src/routes/api/checkout/+server.ts]
import { json } from '@sveltejs/kit'
import { createError } from 'evlog'
import type { RequestHandler } from './$types'

export const POST: RequestHandler = async ({ locals, request }) => {
  const { cartId } = await request.json()
  locals.log.set({ cart: { id: cartId } })

  throw createError({
    message: 'Payment failed',
    status: 402,
    why: 'Card declined by issuer',
    fix: 'Try a different payment method',
    link: 'https://docs.example.com/payments/declined',
  })
}
```

错误会被捕获并记录，同时包含自定义上下文和结构化错误字段：

```bash [终端输出]
14:58:20 ERROR [my-api] POST /api/checkout 402 in 3ms
  ├─ error: name=EvlogError message=Payment failed status=402
  ├─ cart: id=cart_456
  └─ requestId: 880a50ac-...
```

## 配置

所有选项都位于 `evlog.config.ts` 中。完整列表请参阅[配置参考](https://evlog.zhcndoc.com/reference/configuration)。

## 排水与丰富处理

在 `evlog.config.ts` 中设置 `drain` 和 `enrich`，它们会通过 `toMiddlewareOptions(config)` 到达应用。也可以直接传入钩子选项；内联键会替换文件中的同名键，而不是与其合并：

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

const userAgent = createUserAgentEnricher()

export const { handle, handleError } = createEvlogHooks({
  drain: createAxiomDrain(),
  enrich: (ctx) => {
    userAgent(ctx)
    ctx.event.region = process.env.FLY_REGION
  },
})
```

### 管道（批处理与重试）

在生产环境中，将适配器包装在 `createDrainPipeline` 中，以批处理事件并在失败时重试：

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

const pipeline = createDrainPipeline<DrainContext>({
  batch: { size: 50, intervalMs: 5000 },
  retry: { maxAttempts: 3 },
})
const drain = pipeline(createAxiomDrain())

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

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

在服务器关闭时调用 `drain.flush()`，以确保所有缓冲的事件都被发送。查看 [管道文档](https://evlog.zhcndoc.com/extend/drain-pipeline) 了解全部选项。
:::

## 尾采样

使用 `keep` 强制保留特定事件，无论头部采样如何：

```typescript [src/hooks.server.ts]
export const { handle, handleError } = createEvlogHooks({
  drain: createAxiomDrain(),
  keep: (ctx) => {
    if (ctx.duration && ctx.duration > 2000) ctx.shouldKeep = true
  },
})
```

## 路由过滤

使用 `include` 和 `exclude` 模式控制记录哪些路由：

```typescript [src/hooks.server.ts]
export const { handle, handleError } = createEvlogHooks({
  include: ['/api/**'],
  exclude: ['/_internal/**', '/health'],
  routes: {
    '/api/auth/**': { service: 'auth-service' },
    '/api/payment/**': { service: 'payment-service' },
  },
})
```

## 本地运行

```bash [Terminal]
git clone https://github.com/evloghq/evlog.git
cd evlog
pnpm install
pnpm example sveltekit
```

打开 [http://localhost:5173](http://localhost:5173) 探索交互式测试界面。

<card-group>
<card icon="i-simple-icons-github" title="源代码" to="https://github.com/evloghq/evlog/tree/main/examples/sveltekit">

在 GitHub 上浏览完整的 SvelteKit 示例源代码。

</card>

:::

## 下一步

深入学习 **SvelteKit** 集成：

- [宽事件](https://evlog.zhcndoc.com/learn/wide-events)：使用上下文分层设计完整事件
- [适配器](https://evlog.zhcndoc.com/integrate/adapters/overview)：将日志发送到 Axiom、Sentry、PostHog 等
- [采样](https://evlog.zhcndoc.com/learn/sampling)：使用头部采样和尾部采样控制日志量
- [结构化错误](https://evlog.zhcndoc.com/learn/structured-errors)：抛出包含 `why`、`fix` 和 `link` 字段的错误。

</card-group>
</callout>
</prompt>

---

- [源代码](https://github.com/evloghq/evlog/tree/main/examples/sveltekit)


## Sitemap

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