feat(logging): 引入 LogTape 替换 console.* 为结构化日志
为什么选 LogTape(2026 实测):
- pino 在 bun build --compile 编译产物里因 worker_threads + 动态 require 在
/\$bunfs/ 虚拟文件系统中崩溃,与单二进制部署核心目标冲突;
- LogTape 零依赖(5.3KB)、零 worker、纯 ESM、原生 Bun 导出条件,runtime
agnostic,配合 configureSync 完美兼容 --bytecode 模式(无裸 top-level await);
- 一等公民集成:@logtape/drizzle-orm(SQL 查询日志)、@logtape/otel(后续
OpenTelemetry sink 留扩展点)。
变更:
- src/server/logger.ts: configureSync 引导 + getLogger 重导出。format 默认
process.stdout.isTTY ? pretty : json,可经 LOG_FORMAT 显式覆盖(绕开 Bun
bundler 把 process.env.NODE_ENV 在 --minify 时 inline 成字面量的特殊处理)。
- src/server/api/interceptors.ts: logError 改用 getLogger(['api']).error(...) +
结构化 properties,弃 logger.error 顶层 API。
- src/cli/migrate.ts: 所有 console.log 改走 getLogger(['cli','migrate']),logger
在 run() 内 lazy-import 以保持 citty subcommand 模块体 side-effect-free。
- src/server/db/index.ts: env.LOG_DB=true 时挂 DrizzleLogger 适配器,SQL 查询
按类别 ['db'] 在 debug 级输出(含 query/params/formattedQuery 三字段)。
新增 env 旋钮(t3-oss 校验):
- LOG_LEVEL: trace|debug|info|warning|error|fatal,默认 info
- LOG_FORMAT: pretty|json,默认 TTY 自动选
- LOG_DB: stringbool,默认 false
端到端验证(compose + Postgres 18-alpine):
- TTY 终端:pretty 输出含 ✨ 图标 + ANSI 彩色 + 类别·路径 ✓
- 管道/Docker:JSON Lines 一行一条,含 @timestamp/level/logger/properties ✓
- LOG_FORMAT=pretty 强制覆盖 ✓
- ./server migrate 应用 migration 并经 logger 输出 ✓
- ./server serve + RPC round-trip:interceptor logError 与 drizzle SQL 日志
在生产 JSON 模式下结构化输出 ✓
- fix / typecheck / test 3/3 / build / compile 117M 二进制全绿
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
import { ORPCError, ValidationError } from '@orpc/server'
|
||||
import { z } from 'zod'
|
||||
import { logger } from '@/server/logger'
|
||||
import { getLogger } from '@/server/logger'
|
||||
|
||||
const logger = getLogger(['api'])
|
||||
|
||||
export const logError = (error: unknown) => {
|
||||
logger.error(error)
|
||||
logger.error('Unhandled error in ORPC handler: {error}', { error })
|
||||
}
|
||||
|
||||
export const handleValidationError = (error: unknown) => {
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import { DrizzleLogger } from '@logtape/drizzle-orm'
|
||||
import { drizzle } from 'drizzle-orm/postgres-js'
|
||||
import { env } from '@/env'
|
||||
import * as schema from '@/server/db/schema'
|
||||
import { getLogger } from '@/server/logger'
|
||||
|
||||
export const db = drizzle({
|
||||
connection: env.DATABASE_URL,
|
||||
schema,
|
||||
logger: env.LOG_DB ? new DrizzleLogger(getLogger(['db'])) : false,
|
||||
})
|
||||
|
||||
+19
-5
@@ -1,5 +1,19 @@
|
||||
export const logger = {
|
||||
error: (error: unknown) => console.error(error),
|
||||
warn: (...args: unknown[]) => console.warn(...args),
|
||||
info: (...args: unknown[]) => console.info(...args),
|
||||
}
|
||||
import { configureSync, getConsoleSink, getJsonLinesFormatter, getLogger, type LogLevel } from '@logtape/logtape'
|
||||
import { prettyFormatter } from '@logtape/pretty'
|
||||
import { env } from '@/env'
|
||||
|
||||
const format = env.LOG_FORMAT ?? (process.stdout.isTTY ? 'pretty' : 'json')
|
||||
|
||||
configureSync({
|
||||
reset: true,
|
||||
sinks: {
|
||||
console: getConsoleSink({ formatter: format === 'pretty' ? prettyFormatter : getJsonLinesFormatter() }),
|
||||
},
|
||||
loggers: [
|
||||
{ category: [], lowestLevel: env.LOG_LEVEL, sinks: ['console'] },
|
||||
{ category: ['logtape', 'meta'], lowestLevel: 'warning', sinks: ['console'] },
|
||||
],
|
||||
})
|
||||
|
||||
export type { LogLevel }
|
||||
export { getLogger }
|
||||
|
||||
Reference in New Issue
Block a user