You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 07:37:20