I’m working on a Mongoose plugin to log database updates, including the stack trace. Currently, I can only capture the stack trace of the plugin itself, but what I need is the stack trace where the actual update query is triggered in the application code, not in the plugin.
Here’s a simplified version of my Mongoose plugin:
schema.pre(['updateOne', 'findOneAndUpdate', 'updateMany'], async function (next) {
const stackTrace = new Error().stack; // This gives the plugin's stack trace
// I need the stack trace where the update was called
console.log("Stack Trace:", stackTrace);
next();
});
I can achieve this saving stack trace in the query options and then get it from there in the plugin. Something like this:
// Application code
await dbnative.orders.updateOne({'order_id': order_id, 'status': 2}, {'pending': true}, { stack_trace: captureStackTrace() });
// Plugin code
schema.pre(['updateOne', 'findOneAndUpdate', 'updateMany'], async function (next) {
const stackTrace = this.options && this.options.stack_trace
console.log("Stack Trace:", stackTrace);
next();
});
However, in this scenario I need to refactor the all update queries. Is there a better way to achieve this?
Found a solution. We can override the "update" methods and save the stack trace in the options:
function overwriteUpdateMethods() {
const queryProto = mongoose.Query.prototype;
const modelProto = mongoose.Model.prototype;
// Overwrite updateOne
const originalUpdateOne = queryProto.updateOne;
queryProto.updateOne = function (...args) {
// Add stack trace to options
const options = args[2] || {};
options.stack_trace = captureStackTrace();
args[2] = options; // Assign the modified options back to args
return originalUpdateOne.apply(this, args);
};
// Overwrite findOneAndUpdate
const originalFindOneAndUpdate = queryProto.findOneAndUpdate;
queryProto.findOneAndUpdate = function (...args) {
const options = args[2] || {};
options.stack_trace = captureStackTrace();
args[2] = options;
return originalFindOneAndUpdate.apply(this, args);
};
// Overwrite updateMany
const originalUpdateMany = queryProto.updateMany;
queryProto.updateMany = function (...args) {
const options = args[2] || {};
options.stack_trace = captureStackTrace();
args[2] = options;
return originalUpdateMany.apply(this, args);
};
// Overwrite save method (only for updates)
const originalSave = modelProto.save;
modelProto.save = async function (options = {}) {
const isNewDocument = this.isNew;
// adding stack trace to options only for updates
if (!isNewDocument) {
options.stack_trace = captureStackTrace();
console.log('stacktrace', options.stack_trace);
}
return originalSave.apply(this, [options]);
};
}
Hope this will help someone.