本指南介绍你最常与 evlog 一起使用的核心 API。
在 Nuxt 中,evlog 自动导入 所有函数(
useLogger、log、createError、parseError)。无需导入语句。log(简单日志)
使用 evlog 的最简单方式。随时随地发送无需关注的已结构化的日志:
import { log } from 'evlog'
log.info('auth', '用户已登录')
log.error({ action: 'payment', error: 'card_declined' })
log.warn('cache', '缓存未命中')
10:23:45.612 [auth] 用户已登录
10:23:45.613 ERROR [my-app] action=payment error=card_declined
10:23:45.614 [cache] 缓存未命中
两种调用风格:
- 带标签:
log.info('tag', '消息'),适合快速、可读的控制台输出 - 结构化:
log.info({ key: value }),适合在排水管道中传递的丰富事件
查看完整的简单日志指南,了解所有模式和与排水管道的集成。
createLogger(广泛事件)
当你需要在操作的多个步骤中累积上下文时,无论是在脚本、后台作业、队列工作者还是工作流中,请使用 createLogger:
import { initLogger, createLogger } from 'evlog'
initLogger({ env: { service: 'sync-worker' } })
const log = createLogger({ jobId: job.id, queue: 'emails' })
log.set({ batch: { size: 50 } })
log.set({ batch: { processed: 50 } })
log.emit()
10:23:45.612 INFO [sync-worker] 1204ms
├─ jobId: job_abc123
├─ queue: emails
└─ batch: size=50 processed=50
createLogger() 接受一个纯对象作为初始上下文。它会返回一个带有 set、error、info、warn、emit 和 getContext 方法的日志记录器。
对于 HTTP 请求上下文,请使用 createRequestLogger(),它会自动预填充 method、path 和 requestId:
src/worker.ts
import { createRequestLogger } from 'evlog'
const log = createRequestLogger({ method: 'POST', path: '/api/checkout' })
使用
createLogger 和 createRequestLogger 时,必须手动调用 log.emit()。在框架集成中,此操作会自动完成。useLogger(获取请求日志记录器)
在使用框架集成(Nuxt、Hono、Express 等)时,中间件会在请求开始时自动创建一个广泛事件日志记录器,并在响应结束时发出它。useLogger(event) 从请求上下文中获取该日志记录器:
import { useLogger } from 'evlog'
export default defineEventHandler(async (event) => {
const log = useLogger(event)
log.set({ user: { id: 1, plan: 'pro' } })
log.set({ cart: { items: 3, total: 9999 } })
const order = await processCheckout()
log.set({ orderId: order.id })
return { success: true, orderId: order.id }
})
10:23:45.612 INFO [my-app] POST /api/checkout 200 in 234ms
├─ user: id=1 plan=pro
├─ cart: items=3 total=9999
└─ orderId: ord_abc123
useLogger 不会创建日志记录器;框架中间件已经创建好了。它只是从事件上下文中检索出来,以便你可以通过 set() 添加数据。何时使用什么
使用 log | 使用 createLogger() / createRequestLogger() | 使用 useLogger(event) |
|---|---|---|
| 快速的一次性事件 | 脚本、作业、工作者、队列、无框架的 HTTP 操作 | 使用框架集成的 API 路由 |
| 无需累积上下文 | 在操作过程中累积上下文 | 获取自动管理的广泛事件 |
| 客户端日志记录 | 每个操作一个广泛事件 | 访问自动管理的广泛事件 |
服务识别
在多服务架构中,使用以下方式区分日志属于哪个服务:
基于路由的配置
在 nuxt.config.ts 中按路由模式配置服务名称:
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' },
},
},
})
匹配这些路由模式的日志会自动包含配置的服务名称:
输出
21:57:10.442 INFO [auth-service] POST /api/auth/login 200 in 1ms
├─ requestId: 88ced16a-bef2-4483-86cb-2b4fb677ea52
├─ user: id=user_123 email=demo@example.com
└─ action: login
显式服务参数
使用第二个参数覆盖特定路由的服务名称:
server/api/legacy/process.post.ts
import { useLogger } from 'evlog'
export default defineEventHandler((event) => {
const log = useLogger(event, 'legacy-service')
log.set({ action: 'process_legacy_request' })
return { success: true }
})
优先级顺序: 显式
useLogger 参数 > 路由配置 > env.service > 自动检测自环境createError(结构化错误)
使用 createError() 抛出带有可操作上下文的错误:
import { createError } from 'evlog'
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',
})
{
"statusCode": 402,
"message": "Payment failed",
"data": {
"why": "Card declined by issuer",
"fix": "Try a different payment method",
"link": "https://docs.example.com/payments/declined"
}
}
错误字段
| 字段 | 必填 | 描述 |
|---|---|---|
message | 是 | 发生了什么(面向用户) |
status | 否 | HTTP 状态码(默认:500) |
why | 否 | 技术原因(用于调试) |
fix | 否 | 可操作的解决方案 |
link | 否 | 更多信息文档 URL |
cause | 否 | 原始错误(如果包装) |
internal | 否 | 仅用于日志和广泛事件的后端字段 — 永远不会包含在 HTTP JSON 或 parseError() 中 |
前端集成
使用 parseError() 在客户端提取所有错误字段:
composables/useCheckout.ts
import { parseError } from 'evlog'
export async function checkout(cart: Cart) {
try {
await $fetch('/api/checkout', { method: 'POST', body: cart })
} catch (err) {
const error = parseError(err)
toast.add({
title: error.message,
description: error.why,
color: 'error',
actions: error.link
? [{ label: 'Learn more', onClick: () => window.open(error.link) }]
: undefined,
})
if (error.fix) {
console.info(`Fix: ${error.fix}`)
}
}
}
log(客户端)
相同的 log API 在客户端也适用,输出到浏览器控制台:
<script setup lang="ts">
async function handleCheckout() {
log.info('checkout', '用户已发起结算')
try {
await $fetch('/api/checkout', { method: 'POST' })
log.info({ action: 'checkout', status: 'success' })
} catch (err) {
log.error({ action: 'checkout', error: 'failed' })
}
}
</script>
export function useAnalytics() {
function trackEvent(event: string, data?: Record<string, unknown>) {
log.info('analytics', `事件: ${event}`)
if (data) {
log.debug({ event, ...data })
}
}
return { trackEvent }
}
查看客户端日志,了解传输配置、身份上下文和浏览器排水设置。