如何在Sequelize中注入关联ID,实现上下文关联日志?
实现方案
完全可以实现你的需求,核心思路是利用**异步本地存储(AsyncLocalStorage)**维护请求上下文,将绑定了CID的Pino子日志注入上下文,再修改Sequelize的日志配置,让它自动从上下文获取带CID的日志实例输出查询日志。以下是具体实现步骤:
1. 初始化AsyncLocalStorage维护请求上下文
Node.js 12.17.0+内置的AsyncLocalStorage可以跨异步操作共享请求级别的上下文,正好用来存储每个请求的CID和绑定后的子日志:
const { AsyncLocalStorage } = require('async_hooks'); // 创建全局异步存储实例 const asyncLocalStorage = new AsyncLocalStorage();
2. 编写请求中间件注入CID和日志
在REST接口的入口中间件中生成CID,创建绑定CID的Pino子日志,并存入AsyncLocalStorage:
const { v4: uuidv4 } = require('uuid'); const pino = require('pino'); // 根日志实例 const rootLogger = pino(); // 请求上下文中间件 function requestContextMiddleware(req, res, next) { // 生成请求唯一CID const cid = uuidv4(); // 创建绑定CID的子日志 const requestLogger = rootLogger.child({ cid }); // 将上下文存入异步存储,包裹整个请求生命周期 asyncLocalStorage.run({ cid, logger: requestLogger }, () => { // 可选:将日志挂载到req对象,方便业务逻辑直接调用 req.logger = requestLogger; next(); }); } // 挂载到你的Express/Koa等框架 app.use(requestContextMiddleware);
3. 重写Sequelize的日志配置
修改Sequelize初始化参数,自定义日志函数,让它从AsyncLocalStorage中获取当前请求的日志实例输出查询日志:
const { Sequelize } = require('sequelize'); const sequelize = new Sequelize('database', 'username', 'password', { host: 'localhost', dialect: 'mysql', // 自定义日志处理逻辑 logging: (sql, timing) => { // 获取当前请求上下文 const context = asyncLocalStorage.getStore(); if (context?.logger) { // 输出带CID的SQL查询日志,可自定义格式 context.logger.info({ sql, executionTime: `${timing}ms` }, 'Sequelize query executed'); } else { // 无上下文时用根日志兜底 rootLogger.info({ sql, executionTime: `${timing}ms` }, 'Sequelize query executed (no request context)'); } } });
4. 业务逻辑中自动携带CID
完成上述配置后,无论是业务逻辑打日志,还是Sequelize执行查询,都会自动携带当前请求的CID,无需手动传入:
// REST接口处理器示例 app.get('/users/:id', async (req, res) => { // 业务日志自动带CID req.logger.info('Starting to fetch user', { userId: req.params.id }); // Sequelize查询自动输出带CID的日志,无需手动传参数 const user = await User.findByPk(req.params.id); req.logger.info('User fetched successfully', { user }); res.json(user); });
关键注意事项
- 版本兼容性:AsyncLocalStorage仅在Node.js 12.17.0+支持,若使用更低版本可替换为第三方库
cls-hooked,但建议升级到稳定版Node.js。 - 日志级别调整:若需要区分SQL日志级别(如debug),可在自定义日志函数中替换为
context.logger.debug()。 - 上下文泄漏防范:确保中间件正确包裹请求生命周期,AsyncLocalStorage会自动在异步操作结束后清理上下文,避免跨请求污染。
内容的提问来源于stack exchange,提问作者seawolf
相关产品推荐
相关产品推荐

