框架

TypeScript 独立使用

在不使用 Web 框架的 TypeScript 脚本、CLI 工具、队列工作者、定时任务等进程中,使用 evlog 的 `createLogger` 和 `createRequestLogger`。

对于不使用 Web 框架的脚本、CLI 工具、队列工作者、定时任务等进程,evlog 提供核心包中的 createLoggercreateRequestLogger

Prompt
在 TypeScript 项目中为脚本、工作者或 CLI 工具设置 evlog

- 安装 evlog:pnpm add evlog
- 从 'evlog' 导入 initLogger 和 createLogger(或 createRequestLogger)
- 在启动时调用一次 initLogger({ env: { service: 'my-script' } })
- 使用 createLogger({ jobId, source }) 为每个逻辑操作创建日志记录器
- 在操作进行中时使用 log.set() 累积上下文
- 在操作完成时手动调用 log.emit()

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

快速开始

1. 安装

Terminal
bun add evlog

2. 初始化并创建日志记录器

scripts/sync-job.ts
import type { DrainContext } from 'evlog'
import { initLogger, log, createLogger } from 'evlog'
import { createAxiomDrain } from 'evlog/axiom'
import { createDrainPipeline } from 'evlog/pipeline'

const pipeline = createDrainPipeline<DrainContext>({ batch: { size: 10 } })
const drain = pipeline(createAxiomDrain())

initLogger({
  env: { service: 'my-script', environment: 'production' },
  drain,
})

// 每条日志都会自动被排出
log.info({ action: 'sync_started' })

const syncLog = createLogger({ jobId: 'sync-001', source: 'postgres', target: 's3' })
syncLog.set({ recordsSynced: 150 })
syncLog.emit() // 自动排出

// 在退出前刷新剩余事件
await drain.flush()
在进程退出前始终调用 drain.flush(),以确保所有缓冲事件被发送。
使用 vite-node?evlog/vite 插件 会用编译时自动初始化替换 initLogger() 调用,从生产构建中剥离 log.debug(),并注入源代码位置。

createLogger 与 createRequestLogger 的区别

evlog 提供两种手动日志记录器构造函数:

createLogger(context) - 用于非 HTTP 上下文(脚本、CLI、队列):

scripts/job.ts
import { createLogger } from 'evlog'

const log = createLogger({ jobId: 'migrate-001', source: 'postgres' })
log.set({ recordsProcessed: 500 })
log.emit()

createRequestLogger(requestMeta) - 用于希望跟踪方法/路径/状态的类似 HTTP 上下文:

scripts/webhook-handler.ts
import { createRequestLogger } from 'evlog'

const log = createRequestLogger({
  method: 'POST',
  path: '/webhook/stripe',
})
log.set({ event: 'invoice.paid', customerId: 'cus_123' })
log.emit()

两者都需要手动调用 log.emit(),因为没有自动的生命周期可以挂接。

广泛事件

逐步构建上下文,然后发出:

scripts/migrate-users.ts
import { initLogger, createLogger } from 'evlog'

initLogger({
  env: { service: 'migrate' },
})

const log = createLogger({ task: 'user-migration' })

const users = await db.query('SELECT * FROM legacy_users')
log.set({ found: users.length })

let migrated = 0
for (const user of users) {
  await newDb.upsert({ id: user.id, email: user.email, plan: user.plan })
  migrated++
}

log.set({ migrated, status: 'complete' })
log.emit()
终端输出
14:58:15 INFO [migrate] user-migration
  ├─ migrated: 1250
  ├─ found: 1250
  ├─ status: complete
  └─ task: user-migration

错误处理

使用 createError 创建结构化错误:

scripts/sync-job.ts
import { createError, parseError } from 'evlog'

try {
  const result = await externalApi.sync()
  if (!result.ok) {
    throw createError({
      message: 'Sync failed',
      why: `API returned ${result.status}`,
      fix: 'Check the API status page and retry',
    })
  }
} catch (error) {
  log.error(error instanceof Error ? error : new Error(String(error)))
  log.emit()

  const { message, why, fix } = parseError(error)
  console.error(`${message}\nWhy: ${why}\nFix: ${fix}`)
  process.exit(1)
}

配置

请参阅 配置参考 了解所有可用选项(initLogger、中间件选项、采样、静默模式等)。

排水与增强器

initLogger 中配置排水:

scripts/init-logger.ts
import type { DrainContext } from 'evlog'
import { initLogger } 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())

initLogger({
  env: { service: 'my-script' },
  drain,
})
请参阅 适配器 文档,了解所有可用的排水适配器(Axiom、OTLP、PostHog、Sentry、Better Stack)。
请参阅完整的 bun-script 示例,获取一个完整的、可工作的脚本。

下一步

  • 广泛事件:设计具有上下文分层功能的全面事件
  • 适配器:将日志发送到 Axiom、Sentry、PostHog 等
  • 采样:使用头部和尾部采样控制日志量
  • 结构化错误:抛出带有 whyfixlink 字段的错误