WebSocket Logging
Log WebSocket open、message、close 事件
Elogs 把 WebSocket 的生命周期当成一类“请求”打日志 —— 走同一份 access log 管道,所以 access log 里能直接 grep WS 行的 open / message / close。
基本用法
createWsHandlerWrapper(options, logger, contextStore) 返回一个 (path, hooks) => wrappedHooks 的工厂,wrap 完的 hooks 跟普通 Elysia WebSocket handler 一样传给 .ws(path, hooks)。
import { Elysia } from 'elysia'
import {
createElogs,
createWsHandlerWrapper,
createRequestContextStore,
} from '@eastgold15/elogs'
const plugin = createElogs({
config: {
service: 'chat',
},
})
// 拿插件内部的 logger(挂在 Elysia store 上);contextStore 需要自己创建一份
// —— Elogs 内部维护一份,但它没有直接 export 给 store。
// 常见做法:在 app 启动时单独 createRequestContextStore,跟 logger 一起喂给 wrapper。
const contextStore = createRequestContextStore()
// 通过访问 plugin 暴露的 state 拿 logger
// 注:Elogs 把 logger 挂在 store.logger;Elysia 启动后可以从 ctx.store.logger 取
// 这里用一个 dummy request 来"探"到 logger 不直观,推荐方式见下节
上面这段有个真实的工程问题:
createWsHandlerWrapper的 3 个参数里,logger和contextStore是 Elogs 插件内部创建的对象,目前没从 plugin 实例上直接 expose 出来。
推荐用法 request handler 里拿
把 createWsHandlerWrapper 放在第一个 HTTP handler 里取一次,后面复用:
import { Elysia } from 'elysia'
import {
createElogs,
createWsHandlerWrapper,
createRequestContextStore,
} from '@eastgold15/elogs'
const contextStore = createRequestContextStore()
const plugin = createElogs({ config: { service: 'chat' } })
const app = new Elysia()
.use(plugin)
// 通过 .derive 拿一份 plugin 暴露的 logger(走 store)
.derive({ as: 'global' }, ({ store }) => ({
wrapWs: createWsHandlerWrapper({}, store.logger, contextStore),
}))
.ws(
'/chat',
({ wrapWs }) =>
wrapWs('/chat', {
open(ws) {
ws.send('connected')
},
message(ws, message) {
ws.send(message)
},
close() {
// 关闭时清理 contextStore
},
}),
)
每次生命周期事件产生一条 INFO 级别的 access log,HTTP method 显示为 WS,带路由路径、耗时,以及 wsId(从 ws.id 派生)。
Request context on WebSockets
用 WebSocket 实例本身作 key 累积 context,这样同一个 socket 的多行 message log 共享同一份 context bag:
.ws('/chat', ({ wrapWs }) =>
wrapWs('/chat', {
message(ws, payload) {
contextStore.mergeContext(ws, { lastPayloadType: typeof payload })
},
}),
)
close 时 Elogs 会自动 contextStore.clearContext(ws),不会泄漏。
关闭 WebSocket 日志
整段业务不想打 WS 生命周期日志:
import { createElogs } from '@eastgold15/elogs'
createElogs({
config: {
disableWebSocketLogging: true,
},
})
注意,你自己 wrap 后调的 log.info() 之类不受影响。
为什么是独立 export?
Elysia 2 用 #private brand 标记合法 plugin 实例,直接在 plugin 上挂额外字段(像 plugin.wrapWs)会破坏这个 brand,导致 .use() 报类型错误。createWsHandlerWrapper 作为独立函数导出,绕开这个限制,同时保证它跑在同一份 logger / context store 上。
相关 API
createWsHandlerWrapper— WebSocket 事件 wrap 工厂WebSocketLike— 最小 WebSocket 协议WsHandlerHooks—open/message/closehook 形状disableWebSocketLogging—ElogsConfig上的开关ContextKey— WebSocket 实例是合法的 context key