Elysia 2 适配
Elogs 在 Elysia 2 open beta 上的适配说明,以及升级时的所有破坏性变更
Elysia 2 “DayDream” 是 Elysia 框架的一次完整重写。它重命名了所有生命周期钩子、改动了 Elysia 的类型泛型,并把 WebSocket 支持拆成 opt-in 插件。Elogs 无法在单一构建里同时兼容两个大版本,所以发布为两条独立分支。
兼容性矩阵
| Elysia | Elogs | npm tag | 安装 |
|---|---|---|---|
1.4.x |
6.x |
latest |
bun add @eastgold15/elogs |
>= 2.0.0-beta.2 |
7.x-next |
next |
bun add @eastgold15/elogs@next elysia@next |
next 分支紧跟 Elysia 2 beta,在 Elysia 2 稳定前还可能继续引入破坏性变更。
你应用里的变化
Elysia 2 移除了所有生命周期方法的 on 前缀,也没有留下别名。如果你在 Elogs 钩子周围注册了自定义钩子,需要重命名:
| Elysia 1.4 | Elysia 2 |
|---|---|
.onStart() |
.setup() |
.onStop() |
.cleanup() |
.onRequest() |
.request() |
.onTransform() |
.transform() |
.onBeforeHandle() |
.beforeHandle() |
.onAfterHandle() |
.afterHandle() |
.onAfterResponse() |
.afterResponse() |
.onMapResponse() |
.mapResponse() |
.onError() |
.error() |
.as('scoped') |
.as('plugin') |
resolve 被移除,derive 现在在 beforeHandle 阶段运行。Elogs 派生 ctx.log 到 beforeHandle,所以在 beforeHandle、route handler 和 afterHandle 都能拿到 —— 但 parse 和 transform 拿不到了。如果你需要更早记日志,用 store.logger(在 request 钩子里已经设好)。
WebSocket 路由需要 opt-in provider,createWsHandlerWrapper 才能包到东西:
import { Elysia } from 'elysia'
import { websocket } from 'elysia/websocket'
import { createElogs, createWsHandlerWrapper } from '@eastgold15/elogs'
const logging = createElogs()
const wrapWs = createWsHandlerWrapper()
const app = new Elysia()
.use(websocket())
.use(logging)
.ws('/ws', {
...wrapWs({
message(ws, message) {
ws.send(message)
}
})
})
trace 和自动 HEAD 路由也按同样方式搬到 elysia/trace 和 elysia/auto-head。
哪些没变
Elogs 自身的 API 没变:createElogs(options)、preset / config 形态、store.logger、ctx.log、useLogger()、createWsHandlerWrapper,以及 createElogs/otel 和 createElogs/ai 子路径 —— 都跟 6.x 行为一致。
单点 onError 设计
Elogs 2.x 重写了错误处理模型,从 1.x 时代的“多 handler 链”简化成一个钩子只做一件事。这背后的动机是响应格式和错误日志记录是两个正交的关注点,把它们绑在一起会让两边都难以替换。
设计要点
- 插件在
.error(logOnErrorHook)钩子里只记录日志 - 钩子不 return value —— 错误继续传播到用户的
.error(MyClass, fn)或 Elysia 默认的application/problem+json响应 - 钩子捕获所有进入错误管道的错误 —— 无论路由
throw还是return Response(status >= 400),都会触发(Elysia 2 语义) - 翻译器(
autoTranslate)只决定日志级别和内容;翻译后丢弃,原error继续以原形态传播
实现位置在 packages/elogs/src/plugin.ts:265-308,核心结构:
const logOnErrorHook: ErrorHandler = (ctx) => {
// 1) 可选:跑 autoTranslate 链得到 effectiveError(只用来决定 status)
// 2) 提取 status(从 error.status 或 HTTPError.status,默认 500)
// 3) 按 status 写一条 ERROR / WARNING / INFO 日志
// 4) 回显 X-Request-Id 到响应头
// 5) **不 return** —— 错误继续向下游传播
}
已删除的 API
下面是 1.x → 2.x 错误处理模型的破坏性变更清单。这些名字都不再存在,不能在新代码里使用:
| 旧 API | 替代方案 |
|---|---|
applyErrorLogging(error, request, store) |
已被 .error(logOnErrorHook) 钩子取代,无需手动调用 |
LogixlysiaErrorClass |
用 Elysia 2 原生 class extends HTTPError |
LogixlysiaErrorClasses |
用 Elysia 2 原生 class extends HTTPError |
createErrorHandler(options) |
改为用户用 Elysia 2 原生 .error(handler) |
userErrorHandlers |
用户在自己 app 上注册 .error(MyClass, fn) |
fallbackErrorHandler |
改为 Elysia 默认 problem 响应 + Elogs 日志钩子 |
errors 配置项 |
全部移除,改用 .error() 链 + autoTranslate 配置 |
旧的 extractErrorStatus 也已下放到内部,改名 extractStatus 后作为公开工具导出(供 translator 复用),不再是 Elogs 的对外约定。
自定义错误响应 Elysia 2 原生 .error()
想要自定义某个错误类的响应格式,直接用 Elysia 2 原生钩子。Elogs 的钩子会先跑,写完日志后把控制权交回,你的钩子再决定响应形态:
import { Elysia, problem } from 'elysia'
import { createElogs, httpError, errorMap } from '@eastgold15/elogs'
import { translateDrizzleError } from '@eastgold15/elogs/translator'
class NotFoundError extends Error {
status = 404
}
const app = new Elysia()
.use(
createElogs({
// 自动把 Drizzle 错误翻译成 4xx/5xx,只影响日志级别
autoTranslate: { db: 'drizzle' },
})
)
// 你的 .error() 在 Elogs 之后注册,日志已写好
.error(NotFoundError, ({ code }) => {
return problem(404, { detail: code ?? 'not found' })
})
.error('DrizzleError', (ctx) => {
const code = (ctx.error as { code?: string }).code ?? 'UNKNOWN'
return problem(409, { detail: `duplicate: ${code}` })
})
.get('/users/:id', ({ params }) => {
if (params.id === '404') {
throw httpError(404, 'user not found')
}
throw new Error('boom')
})
翻译器 vs 错误响应
翻译器和错误响应是两条独立的线,不要混:
autoTranslate—— 决定日志怎么记(级别 + 内容),原 error 不动.error(MyClass, fn)—— 决定响应怎么发,可能完全覆盖 Elogs 默认的 problem 响应
完整 Drizzle 翻译示例见 Database errors。httpError(status, msg) 和 errorMap() 仍是用户工具,只是不再被插件自动注册。
Elysia 2 框架行为对你的日志
两个框架行为变化会反映在日志内容里:
- 错误默认以 RFC 9457 problem document 形式报告。
- 当
NODE_ENV=production,未处理错误的message不再发回客户端,返回的Error序列化时也不会带cause。
这两个都不影响 Elogs 写入的内容 —— 完整 error 仍然服务端全量记录,具体范围由 logErrorPayload 和 autoRedact 决定。