框架
Nitro
Automatic wide events, structured errors, drain adapters, enrichers, and tail sampling in Nitro v2 and v3 applications.
evlog 提供了适用于 Nitro v3 和 Nitro v2(nitropack)的模块。该模块会钩入请求生命周期,创建一个请求范围内的日志记录器,可通过 useLogger(event) 访问,并在响应完成时发出一个广泛事件。
提示
在 Nitro 应用中设置 evlog。
- 安装 evlog:pnpm add evlog
- 在 nitro.config.ts 中导入 evlog 模块(v2 使用 evlog/nitro,v3 使用 evlog/nitro/v3)
- 配置 env.service 为你的应用名称
- 在路由处理程序中使用 useLogger(event) 构建广泛事件
- 使用 log.set() 在请求过程中累积上下文
- 使用 createError({ message, status, why, fix }) 抛出错误
- 请求完成时会自动发出广泛事件
文档:https://www.evlog.dev/frameworks/nitro
适配器:https://www.evlog.dev/adapters
快速开始
1. 安装
pnpm add evlog
npm install evlog
yarn add evlog
bun add evlog
2. 添加模块
import { defineConfig } from 'nitro'
import evlog from 'evlog/nitro/v3'
export default defineConfig({
modules: [
evlog({
env: { service: 'my-app' },
}),
],
})
import { defineNitroConfig } from 'nitropack/config'
import evlog from 'evlog/nitro'
export default defineNitroConfig({
modules: [
evlog({
env: { service: 'my-app' },
}),
],
})
广泛事件
使用 useLogger(event) 在请求过程中逐步构建上下文。evlog 会在请求完成时发出一个单一广泛事件。
import { defineHandler } from 'nitro/h3'
import { useLogger } from 'evlog/nitro/v3'
export default defineHandler(async (event) => {
const log = useLogger(event)
const body = await readBody(event)
log.set({ user: { id: body.userId } })
log.set({ cart: { items: body.items.length, total: body.total } })
return { success: true }
})
import { defineEventHandler, readBody } from 'h3'
import { useLogger } from 'evlog/nitro'
export default defineEventHandler(async (event) => {
const log = useLogger(event)
const body = await readBody(event)
log.set({ user: { id: body.userId } })
log.set({ cart: { items: body.items.length, total: body.total } })
return { success: true }
})
一个请求,一条日志行,包含所有上下文信息:
终端输出
10:23:45 INFO [my-app] POST /api/checkout 200 in 145ms
├─ user: id=usr_123
├─ cart: items=3 total=14999
└─ requestId: a1b2c3d4-...
错误处理
createError 会生成包含 why、fix 和 link 字段的结构化错误,帮助人类和 AI 代理理解问题所在。
import { defineHandler } from 'nitro/h3'
import { useLogger, createError } from 'evlog/nitro/v3'
export default defineHandler(async (event) => {
const log = useLogger(event)
throw createError({
status: 402,
message: 'Payment failed',
why: 'Card declined by issuer',
fix: 'Try a different payment method',
})
})
import { defineEventHandler } from 'h3'
import { useLogger } from 'evlog/nitro'
import { createError } from 'evlog'
export default defineEventHandler(async (event) => {
const log = useLogger(event)
throw createError({
status: 402,
message: 'Payment failed',
why: 'Card declined by issuer',
fix: 'Try a different payment method',
})
})
在 Nitro v3 中,请从
evlog/nitro/v3 导入 createError —— 它封装了 Nitro 的错误处理机制。在 Nitro v2 中,请直接从 evlog 导入 createError。配置
请参阅 配置参考 了解所有可用选项(enabled、pretty、silent、sampling 等)。
路由过滤
使用 include 和 exclude 控制哪些路由被记录,以及使用 routes 为不同的路由组分配不同的服务名称:
import { defineConfig } from 'nitro'
import evlog from 'evlog/nitro/v3'
export default defineConfig({
modules: [
evlog({
include: ['/api/**'],
exclude: ['/api/health'],
routes: {
'/api/auth/**': { service: 'auth-service' },
'/api/payment/**': { service: 'payment-service' },
},
})
],
})
import { defineNitroConfig } from 'nitropack/config'
import evlog from 'evlog/nitro'
export default defineNitroConfig({
modules: [
evlog({
include: ['/api/**'],
exclude: ['/api/health'],
routes: {
'/api/auth/**': { service: 'auth-service' },
'/api/payment/**': { service: 'payment-service' },
},
})
],
})
排除项优先级更高。 如果路径同时匹配
include 和 exclude,则会被排除。排水(Drain)与增强器(Enrichers)
使用 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)
})
对于独立 Nitro v3,请使用
definePlugin 替代 defineNitroPlugin。增强器插件
server/plugins/evlog-enrich.ts
import { createUserAgentEnricher, createGeoEnricher } from 'evlog/enrichers'
const enrichers = [createUserAgentEnricher(), createGeoEnricher()]
export default defineNitroPlugin((nitroApp) => {
nitroApp.hooks.hook('evlog:enrich', (ctx) => {
for (const enricher of enrichers) enricher(ctx)
})
})
采样
头部采样
按百分比随机保留各层级的日志。在请求完成前执行。
import { defineConfig } from 'nitro'
import evlog from 'evlog/nitro/v3'
export default defineConfig({
modules: [
evlog({
sampling: {
rates: { info: 10, warn: 50, debug: 5 },
keep: [
{ duration: 1000 },
{ status: 400 },
],
},
})
],
})
import { defineNitroConfig } from 'nitropack/config'
import evlog from 'evlog/nitro'
export default defineNitroConfig({
modules: [
evlog({
sampling: {
rates: { info: 10, warn: 50, debug: 5 },
keep: [
{ duration: 1000 },
{ status: 400 },
],
},
})
],
})
每个层级都是 0 到 100 的百分比。未配置的层级默认保留 100%(保留全部)。
自定义尾部采样
对于超出状态、时长和路径的条件,使用 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 才能丢弃它们。下一步
深入了解 Nitro 集成: