如何在TypeORM自定义Logger中获取请求Header等请求信息
TypeORM自定义Logger获取请求上下文解决方法
问题根本原因
queryRunner.data 是TypeORM预留的自定义数据存储字段,不会自动填充任何内容,你没有主动往该字段写入请求、用户相关数据的前提下,默认就是空值。
方案1:手动绑定数据到queryRunner(适合愿意调整业务代码的场景)
你需要在请求生命周期的入口,手动创建请求级的queryRunner,写入你需要的上下文数据,后续所有业务逻辑的数据库操作都使用该queryRunner执行,即可在Logger中读取到对应数据。
实现步骤:
- 新增全局请求中间件,创建请求级queryRunner并绑定上下文
// 以Express框架为例 import { dataSource } from './your-typeorm-config' import { parseJwt } from './your-jwt-util' app.use(async (req, res, next) => { // 解析JWT获取用户信息 const token = req.headers.authorization?.split(' ')[1] const user = token ? parseJwt(token) : null // 为当前请求创建独立的queryRunner const queryRunner = dataSource.createQueryRunner() // 写入自定义数据 queryRunner.data.request = req queryRunner.data.user = user // 挂载到req对象上供业务层使用 req.queryRunner = queryRunner // 请求结束后释放queryRunner res.on('finish', async () => { await queryRunner.release() }) next() })
- 业务层所有数据库操作,统一使用挂载在req上的queryRunner执行,示例:
// 错误写法:用全局manager/repository,不会携带你绑定的data // await getRepository(User).save(user) // 正确写法:用当前请求的queryRunner执行操作 await req.queryRunner.manager.getRepository(User).save(user)
方案2:用AsyncLocalStorage存储请求上下文(无需修改业务代码,更推荐)
如果你不想调整现有业务代码的数据库调用逻辑,可以用Node.js原生的AsyncLocalStorage存储请求上下文,直接在Logger中读取即可,完全不需要依赖queryRunner.data。
实现步骤:
- 新建全局上下文存储实例
import { AsyncLocalStorage } from 'async_hooks' export const requestContext = new AsyncLocalStorage<{ user?: { id: number; username: string } requestUrl?: string }>()
- 新增全局中间件,把上下文存入AsyncLocalStorage
import { parseJwt } from './your-jwt-util' app.use((req, res, next) => { // 解析JWT获取用户信息 const token = req.headers.authorization?.split(' ')[1] const user = token ? parseJwt(token) : null // 将当前请求的上下文存入AsyncLocalStorage requestContext.run( { user, requestUrl: req.url }, () => next() ) })
- 调整自定义Logger的逻辑,直接读取上下文
import { requestContext } from './your-context-file' public logQuery(query: string, parameters?: any[], queryRunner?: QueryRunner): any { const type = query.substring(0, 6); if ((type === 'INSERT' || type === 'UPDATE' || type === 'DELETE') && !query.includes('INSERT INTO "log"')) { const log = new Log(); // 直接从AsyncLocalStorage获取上下文 const context = requestContext.getStore() const requestUrl = context?.requestUrl ? `(${context.requestUrl}) ` : ''; console.log(`${requestUrl}executing query: `); log.query = query; log.parameters = parameters?.toString(); log.type = type; log.userId = context?.user?.id // 直接拿到发起操作的用户ID getRepository(Log).save(log).then(); } }
注意事项
AsyncLocalStorage需要Node.js版本 >= 12.17.0,目前绝大多数后端项目都满足该要求- 该方案不需要修改任何现有业务的数据库调用代码,侵入性极低
内容的提问来源于stack exchange,提问作者Wilson Silva
相关产品推荐
相关产品推荐

