框架

Nuxt

自动宽事件、结构化错误、排水适配器、增强器、尾部采样,以及 Nuxt 应用程序中的客户端传输。

evlog 为 Nuxt 提供一流的模块支持,包含自动导入的 useLoggercreateErrorparseError。将其添加到配置中即可零样板文件开始记录日志。

提示
在 Nuxt 应用中设置 evlog,开启宽事件和结构化错误。

- 安装 evlog:pnpm add evlog
- 在 nuxt.config.ts 的 modules 中添加 'evlog/nuxt'
- 设置 evlog.env.service 为你的应用名称
- useLogger、createError 和 parseError 已自动导入
- 创建一个服务器 API 路由,使用 useLogger(event) 和 log.set() 构建宽事件
- 使用 createError({ message, status, why, fix }) 抛出错误
- 宽事件会在每个请求完成时自动发出

文档:https://www.evlog.dev/frameworks/nuxt
适配器:https://www.evlog.dev/adapters

快速开始

1. 安装

pnpm add evlog

2. 添加模块

nuxt.config.ts
export default defineNuxtConfig({
  modules: ['evlog/nuxt'],
  evlog: {
    env: {
      service: 'my-app',
    },
  },
})

就这样。useLoggercreateErrorparseError 已经自动导入。

宽事件

使用 useLogger(event) 在请求过程中逐步构建上下文。evlog 在请求完成时发出单个宽事件。

server/api/checkout.post.ts
export default defineEventHandler(async (event) => {
  const log = useLogger(event)
  const body = await readBody(event)

  log.set({ user: { id: body.userId, plan: 'enterprise' } })

  const cart = await db.findCart(body.cartId)
  log.set({ cart: { items: cart.items.length, total: cart.total } })

  const payment = await processPayment(cart)
  log.set({ payment: { method: payment.method, cardLast4: payment.last4 } })

  return { success: true, orderId: payment.orderId }
})

一个请求,一条包含所有上下文的日志:

终端输出
10:23:45 INFO [my-app] POST /api/checkout 200 in 145ms
  ├─ user: id=usr_123 plan=enterprise
  ├─ cart: items=3 total=14999
  ├─ payment: method=card cardLast4=4242
  └─ requestId: a1b2c3d4-...

错误处理

createError 生成带有 whyfixlink 字段的结构化错误,帮助人类和 AI 代理理解问题所在。

server/api/payment/process.post.ts
export default defineEventHandler(async (event) => {
  const log = useLogger(event)
  const body = await readBody(event)

  log.set({ payment: { amount: body.amount } })

  if (body.amount <= 0) {
    throw createError({
      status: 400,
      message: 'Invalid payment amount',
      why: 'The amount must be a positive number',
      fix: 'Pass a positive integer in cents (e.g. 4999 for $49.99)',
      link: 'https://docs.example.com/api/payments#amount',
    })
  }

  return { success: true }
})
Nuxt 的错误处理器会自动捕获 EvlogError 并返回包含 whyfixlink 字段的结构化 JSON 响应。

配置

请参阅 配置参考 了解完整的共享选项列表(enabledprettysilentsampling、中间件选项等)。

所有选项均在 nuxt.config.ts 中的 evlog 键下设置:

选项类型默认值描述
enabledbooleantrue全局启用/禁用所有日志记录。当为 false 时,所有操作都变为无操作
consolebooleantrue启用/禁用浏览器控制台输出
env.servicestring'app'日志中显示的服务名称
env.environmentstring自动检测环境名称
includestring[]undefined要记录路由的模式。支持通配符(/api/**
excludestring[]undefined要排除的路由模式。排除项优先级更高
routesRecord<string, RouteConfig>undefined路由特定服务配置
prettyboolean开发环境下为 true使用树形格式美化输出
silentbooleanfalse抑制控制台输出。事件仍会被构建、采样和排出。适用于基于 stdout 的平台
sampling.ratesobjectundefined每个日志级别按 0-100% 的头部采样率
sampling.keeparrayundefined尾部采样条件,用于强制保留日志
transport.enabledbooleanfalse启用客户端到服务器的日志传输
transport.endpointstring'/api/_evlog/ingest'传输端点

路由过滤

使用 includeexclude 控制记录哪些路由:

nuxt.config.ts
export default defineNuxtConfig({
  modules: ['evlog/nuxt'],
  evlog: {
    include: ['/api/**', '/auth/**'],
    exclude: [
      '/api/_nuxt_icon/**',
      '/api/_content/**',
      '/api/health',
    ],
  },
})
排除项优先级更高。 如果路径同时匹配 includeexclude,则会被排除。

基于路由的服务名称

为不同路由组分配不同的服务名称:

nuxt.config.ts
export default defineNuxtConfig({
  modules: ['evlog/nuxt'],
  evlog: {
    env: { service: 'default-service' },
    routes: {
      '/api/auth/**': { service: 'auth-service' },
      '/api/payment/**': { service: 'payment-service' },
      '/api/booking/**': { service: 'booking-service' },
    },
  },
})

排水与增强器

使用 Nitro 插件钩子将日志发送到外部服务并使用附加上下文进行增强。

排水插件

server/plugins/evlog-drain.ts
import type { DrainContext } from 'evlog'
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 default defineNitroPlugin((nitroApp) => {
  nitroApp.hooks.hook('evlog:drain', drain)
})

增强器插件

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

const enrichers = [
  createUserAgentEnricher(),
  createGeoEnricher(),
  createRequestSizeEnricher(),
  createTraceContextEnricher(),
]

export default defineNitroPlugin((nitroApp) => {
  nitroApp.hooks.hook('evlog:enrich', (ctx) => {
    for (const enricher of enrichers) enricher(ctx)
  })
})
请参阅 适配器增强器 文档,了解可用的完整排水和增强器列表。

采样

头部采样

按百分比随机保留每个级别的日志。在请求完成前运行。

nuxt.config.ts
export default defineNuxtConfig({
  modules: ['evlog/nuxt'],
  evlog: {
    sampling: {
      rates: {
        info: 10,
        warn: 50,
        debug: 5,
        error: 100,
      },
    },
  },
})

每个级别都是一个 0 到 100 的百分比。未配置的级别默认保留 100%(保留全部)。错误级别默认为 100%,即使配置了其他级别。

尾部采样

在请求完成后进行评估,根据特定条件强制保留日志,无论头部采样如何。

nuxt.config.ts
export default defineNuxtConfig({
  modules: ['evlog/nuxt'],
  evlog: {
    sampling: {
      rates: { info: 10 },
      keep: [
        { duration: 1000 },
        { status: 400 },
        { path: '/api/critical/**' },
      ],
    },
  },
})

自定义尾部采样

对于超出状态、持续时间和路径的条件,使用 evlog:emit:keep 钩子:

server/plugins/evlog-sampling.ts
export default defineNitroPlugin((nitroApp) => {
  nitroApp.hooks.hook('evlog:emit:keep', (ctx) => {
    const user = ctx.context.user as { premium?: boolean } | undefined
    if (user?.premium) {
      ctx.shouldKeep = true
    }
  })
})
错误默认始终保留。你必须显式设置 error: 0 才能丢弃它们。

客户端传输

将浏览器日志发送到服务器进行处理和排出,与服务器端事件一起处理。

nuxt.config.ts
export default defineNuxtConfig({
  modules: ['evlog/nuxt'],
  evlog: {
    transport: {
      enabled: true,
      endpoint: '/api/_evlog/ingest',
    },
  },
})

工作原理

  1. 客户端调用 log.info({ action: 'click', button: 'submit' })
  2. 日志通过 POST 发送到 /api/_evlog/ingest
  3. 服务器使用环境上下文进行增强
  4. 触发 evlog:drain 钩子,source: 'client'
  5. 外部服务接收日志

客户端身份

使用 setIdentity 为每个客户端日志附加用户上下文:

Nuxt(自动导入)
// 登录后
setIdentity({ userId: 'usr_123', orgId: 'org_456' })

log.info({ action: 'checkout' })
// -> { userId: 'usr_123', orgId: 'org_456', action: 'checkout', ... }

// 登出后
clearIdentity()

与身份验证同步身份

使用路由中间件保持身份与认证状态同步:

middleware/identity.global.ts
export default defineNuxtRouteMiddleware(() => {
  const { user } = useAuth()

  if (user.value) {
    setIdentity({ userId: user.value.id, email: user.value.email })
  } else {
    clearIdentity()
  }
})

生产环境建议

使用 Nuxt 的 $production 覆盖,在开发环境中保持完整日志记录,同时在生产环境中进行采样并禁用控制台输出:

nuxt.config.ts
export default defineNuxtConfig({
  modules: ['evlog/nuxt'],
  evlog: {
    env: { service: 'my-app' },
  },
  $production: {
    evlog: {
      console: false,
      sampling: {
        rates: { info: 10, warn: 50, debug: 0 },
        keep: [{ duration: 1000 }, { status: 400 }],
      },
    },
  },
})

后续步骤

深入你的 Nuxt 集成:

  • 宽事件:设计带有上下文层级的综合事件
  • 适配器:将日志发送到 Axiom、Sentry、PostHog 等
  • 采样:通过头部和尾部采样控制日志量
  • 结构化错误:使用 whyfixlink 字段抛出错误