如何在Mongoose插件中捕获实际更新的堆栈跟踪而非插件调用栈?
问题
我正在开发一个用于记录数据库更新(包括堆栈跟踪)的Mongoose插件。目前仅能捕获插件自身的堆栈跟踪,但我需要获取应用代码中触发实际更新查询的堆栈跟踪,而非插件内的调用栈。
以下是我的Mongoose插件简化版本:
schema.pre(['updateOne', 'findOneAndUpdate', 'updateMany'], async function (next) { const stackTrace = new Error().stack; // 这里获取的是插件自身的堆栈跟踪 // 我需要获取触发更新的应用代码处的堆栈跟踪 console.log("Stack Trace:", stackTrace); next(); });
我试过通过在查询选项中保存堆栈跟踪,再在插件中获取的方式实现需求,示例如下:
// 应用代码 await dbnative.orders.updateOne({'order_id': order_id, 'status': 2}, {'pending': true}, { stack_trace: captureStackTrace() }); // 插件代码 schema.pre(['updateOne', 'findOneAndUpdate', 'updateMany'], async function (next) { const stackTrace = this.options && this.options.stack_trace console.log("Stack Trace:", stackTrace); next(); });
但这种方式需要重构所有更新查询,是否存在更优的实现方案?
更优实现方案:重写Mongoose查询方法自动捕获堆栈
不需要修改所有业务代码的核心思路是重写Mongoose模型上的updateOne、findOneAndUpdate、updateMany方法,在这些方法被调用时先捕获堆栈跟踪,再把它附加到查询选项中,最后调用原始方法。这样插件就能直接从this.options里拿到应用层的堆栈。
具体实现
function attachStackTracePlugin(schema) { // 需要拦截的更新方法列表 const updateMethods = ['updateOne', 'findOneAndUpdate', 'updateMany']; updateMethods.forEach(methodName => { // 保存原始方法引用 const originalMethod = schema.statics[methodName]; schema.statics[methodName] = function(...args) { // 捕获当前调用栈(应用层触发更新的位置) let stackTrace = new Error().stack; // 可选:过滤掉Mongoose内部调用栈,只保留应用层代码 stackTrace = stackTrace.split('\n').filter(line => line.includes('/src/')).join('\n'); // 把堆栈跟踪追加到查询选项中 if (args.length >= 3) { args[2] = {...args[2], stack_trace: stackTrace}; } else if (args.length === 2) { args.push({stack_trace: stackTrace}); } // 调用原始方法并返回结果 return originalMethod.apply(this, args); }; }); // 原有的pre中间件逻辑,现在直接从options取应用层堆栈 schema.pre(['updateOne', 'findOneAndUpdate', 'updateMany'], async function (next) { const stackTrace = this.options?.stack_trace; console.log("应用层触发更新的堆栈:", stackTrace); next(); }); }
应用插件到模型
const mongoose = require('mongoose'); const OrderSchema = new mongoose.Schema({ order_id: Number, status: Number, pending: Boolean }); // 挂载自定义插件 OrderSchema.plugin(attachStackTracePlugin); const Order = mongoose.model('Order', OrderSchema);
优势说明
- 无需修改任何业务层的更新代码,原有调用方式完全兼容
- 自动捕获应用层触发更新的堆栈,无需手动传递参数
- 可通过字符串过滤,精简堆栈内容,只保留有用的应用代码调用信息
内容的提问来源于stack exchange,提问作者Avag Sargsyan
相关产品推荐
相关产品推荐

