autotel-mongoose 8.1.0 → 10.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +801 -919
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +93 -90
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.ts +93 -90
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +800 -917
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
package/dist/index.cjs
CHANGED
|
@@ -1,970 +1,852 @@
|
|
|
1
|
-
'
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
let autotel = require("autotel");
|
|
3
|
+
let autotel_trace_helpers = require("autotel/trace-helpers");
|
|
2
4
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
+
//#region src/constants.ts
|
|
6
|
+
const ATTR_DB_QUERY_TEXT = "db.query.text";
|
|
7
|
+
const ATTR_DB_OPERATION_NAME = "db.operation.name";
|
|
8
|
+
const ATTR_DB_SYSTEM_NAME = "db.system.name";
|
|
9
|
+
const ATTR_DB_COLLECTION_NAME = "db.collection.name";
|
|
10
|
+
const ATTR_DB_NAMESPACE = "db.namespace";
|
|
11
|
+
const ATTR_SERVER_ADDRESS = "server.address";
|
|
12
|
+
const ATTR_SERVER_PORT = "server.port";
|
|
13
|
+
const ATTR_CODE_FUNCTION_NAME = "code.function.name";
|
|
14
|
+
const ATTR_MONGOOSE_METHOD_NAME = "mongoose.method.name";
|
|
15
|
+
/** One of: "static" | "instance" | "query". */
|
|
16
|
+
const ATTR_MONGOOSE_METHOD_TYPE = "mongoose.method.type";
|
|
17
|
+
const ATTR_MONGOOSE_METHOD_MODEL = "mongoose.method.model";
|
|
18
|
+
const ATTR_MONGOOSE_METHOD_PARAMETERS = "mongoose.method.parameters";
|
|
19
|
+
const ATTR_MONGOOSE_METHOD_PARAMETER_COUNT = "mongoose.method.parameter_count";
|
|
20
|
+
const DB_SYSTEM_NAME_VALUE_MONGODB = "mongodb";
|
|
5
21
|
|
|
6
|
-
|
|
22
|
+
//#endregion
|
|
23
|
+
//#region src/types.ts
|
|
24
|
+
const DEFAULT_TRACER_NAME = "autotel-mongoose";
|
|
7
25
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
var ATTR_DB_NAMESPACE = "db.namespace";
|
|
14
|
-
var ATTR_SERVER_ADDRESS = "server.address";
|
|
15
|
-
var ATTR_SERVER_PORT = "server.port";
|
|
16
|
-
var ATTR_CODE_FUNCTION_NAME = "code.function.name";
|
|
17
|
-
var ATTR_MONGOOSE_METHOD_NAME = "mongoose.method.name";
|
|
18
|
-
var ATTR_MONGOOSE_METHOD_TYPE = "mongoose.method.type";
|
|
19
|
-
var ATTR_MONGOOSE_METHOD_MODEL = "mongoose.method.model";
|
|
20
|
-
var ATTR_MONGOOSE_METHOD_PARAMETERS = "mongoose.method.parameters";
|
|
21
|
-
var ATTR_MONGOOSE_METHOD_PARAMETER_COUNT = "mongoose.method.parameter_count";
|
|
22
|
-
var DB_SYSTEM_NAME_VALUE_MONGODB = "mongodb";
|
|
23
|
-
|
|
24
|
-
// src/types.ts
|
|
25
|
-
var DEFAULT_TRACER_NAME = "autotel-mongoose";
|
|
26
|
+
//#endregion
|
|
27
|
+
//#region src/statement.ts
|
|
28
|
+
/**
|
|
29
|
+
* Default serializer — JSON.stringify of the payload.
|
|
30
|
+
*/
|
|
26
31
|
function defaultSerializer(_operation, payload) {
|
|
27
|
-
|
|
32
|
+
return JSON.stringify(payload);
|
|
28
33
|
}
|
|
34
|
+
/**
|
|
35
|
+
* Composes the serializer and redactor into a single capture function.
|
|
36
|
+
* Returns undefined if statement capture is disabled.
|
|
37
|
+
*/
|
|
29
38
|
function createStatementCapture(config) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
40
|
-
return (operation, payload) => {
|
|
41
|
-
const raw = serializer(operation, payload);
|
|
42
|
-
if (raw === void 0) {
|
|
43
|
-
return void 0;
|
|
44
|
-
}
|
|
45
|
-
return redact ? redact(raw) : raw;
|
|
46
|
-
};
|
|
39
|
+
if (config.dbStatementSerializer === false) return () => {};
|
|
40
|
+
const serializer = typeof config.dbStatementSerializer === "function" ? config.dbStatementSerializer : defaultSerializer;
|
|
41
|
+
let redact;
|
|
42
|
+
if (config.statementRedactor !== false && config.statementRedactor !== void 0) redact = (0, autotel.createStringRedactor)(config.statementRedactor);
|
|
43
|
+
return (operation, payload) => {
|
|
44
|
+
const raw = serializer(operation, payload);
|
|
45
|
+
if (raw === void 0) return;
|
|
46
|
+
return redact ? redact(raw) : raw;
|
|
47
|
+
};
|
|
47
48
|
}
|
|
48
|
-
|
|
49
|
+
const DEFAULT_PARAMETER_MAX_LENGTH = 2048;
|
|
50
|
+
/**
|
|
51
|
+
* Default serializer for custom-method arguments. Safely JSON-encodes the
|
|
52
|
+
* argument list, resolving Mongoose documents via `toObject()` and handling
|
|
53
|
+
* BigInt, functions, and circular references without throwing.
|
|
54
|
+
*/
|
|
49
55
|
function defaultParameterSerializer(args) {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
return arg;
|
|
78
|
-
});
|
|
79
|
-
try {
|
|
80
|
-
const json = JSON.stringify(normalized, replacer);
|
|
81
|
-
return json === void 0 ? void 0 : json;
|
|
82
|
-
} catch {
|
|
83
|
-
return void 0;
|
|
84
|
-
}
|
|
56
|
+
if (args.length === 0) return;
|
|
57
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
58
|
+
const replacer = (_key, value) => {
|
|
59
|
+
if (typeof value === "bigint") return value.toString();
|
|
60
|
+
if (typeof value === "function") return "[Function]";
|
|
61
|
+
if (typeof value === "object" && value !== null) {
|
|
62
|
+
if (seen.has(value)) return "[Circular]";
|
|
63
|
+
seen.add(value);
|
|
64
|
+
}
|
|
65
|
+
return value;
|
|
66
|
+
};
|
|
67
|
+
const normalized = args.map((arg) => {
|
|
68
|
+
if (arg !== null && typeof arg === "object" && typeof arg.toObject === "function") try {
|
|
69
|
+
return arg.toObject();
|
|
70
|
+
} catch {
|
|
71
|
+
return arg;
|
|
72
|
+
}
|
|
73
|
+
return arg;
|
|
74
|
+
});
|
|
75
|
+
try {
|
|
76
|
+
const json = JSON.stringify(normalized, replacer);
|
|
77
|
+
return json === void 0 ? void 0 : json;
|
|
78
|
+
} catch {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
85
81
|
}
|
|
82
|
+
/**
|
|
83
|
+
* Composes a parameter serializer + redactor + length cap into a single
|
|
84
|
+
* capture function for custom-method arguments. The redactor defaults to the
|
|
85
|
+
* instrumentation's `statementRedactor` so parameters get the same PII
|
|
86
|
+
* protection as `db.query.text`.
|
|
87
|
+
*/
|
|
86
88
|
function createParameterCapture(config) {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
}
|
|
100
|
-
const redacted = redact ? redact(raw) : raw;
|
|
101
|
-
return redacted.length > maxLength ? `${redacted.slice(0, maxLength)}\u2026[truncated]` : redacted;
|
|
102
|
-
};
|
|
89
|
+
const { parameterConfig } = config;
|
|
90
|
+
const maxLength = parameterConfig?.maxLength ?? DEFAULT_PARAMETER_MAX_LENGTH;
|
|
91
|
+
const serialize = parameterConfig?.serializer ?? defaultParameterSerializer;
|
|
92
|
+
const redactorSetting = parameterConfig?.redactor === void 0 ? config.statementRedactor : parameterConfig.redactor;
|
|
93
|
+
let redact;
|
|
94
|
+
if (redactorSetting !== false && redactorSetting !== void 0) redact = (0, autotel.createStringRedactor)(redactorSetting);
|
|
95
|
+
return (args, context) => {
|
|
96
|
+
const raw = serialize(args, context);
|
|
97
|
+
if (raw === void 0) return;
|
|
98
|
+
const redacted = redact ? redact(raw) : raw;
|
|
99
|
+
return redacted.length > maxLength ? `${redacted.slice(0, maxLength)}…[truncated]` : redacted;
|
|
100
|
+
};
|
|
103
101
|
}
|
|
104
102
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
103
|
+
//#endregion
|
|
104
|
+
//#region src/instrumentation.ts
|
|
105
|
+
const INSTRUMENTED_FLAG = "__autotelMongooseInstrumented";
|
|
106
|
+
const WRAPPED_HOOK_FLAG = "__autotelWrappedHook";
|
|
107
|
+
const WRAPPED_METHOD_FLAG = "__autotelWrappedMethod";
|
|
108
|
+
const MODEL_PATCHED_FLAG = "__autotelModelPatched";
|
|
109
|
+
/**
|
|
110
|
+
* Per-Mongoose-instance registry of the resolved tracer + config.
|
|
111
|
+
*
|
|
112
|
+
* Custom-function wrappers are installed once on the (potentially shared)
|
|
113
|
+
* schema object, so they must NOT close over a single instance's
|
|
114
|
+
* tracer/config — a schema reused across instances/connections would otherwise
|
|
115
|
+
* be permanently bound to whichever instrumented it first. Instead each wrapper
|
|
116
|
+
* resolves the owning Mongoose instance from its runtime `this` and looks up
|
|
117
|
+
* the config here at call time. An instance that was never instrumented (or has
|
|
118
|
+
* custom methods disabled) is absent, so its calls pass straight through.
|
|
119
|
+
*/
|
|
120
|
+
const INSTANCE_REGISTRY = /* @__PURE__ */ new WeakMap();
|
|
121
|
+
/** Resolves the owning Mongoose instance from a custom function's `this`. */
|
|
111
122
|
function resolveMongooseInstance(self, methodType) {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
}
|
|
120
|
-
case "query": {
|
|
121
|
-
return self?.model?.base;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
} catch {
|
|
125
|
-
}
|
|
126
|
-
return void 0;
|
|
123
|
+
try {
|
|
124
|
+
switch (methodType) {
|
|
125
|
+
case "static": return self?.base ?? self?.db?.base;
|
|
126
|
+
case "instance": return self?.constructor?.base ?? self?.db?.base;
|
|
127
|
+
case "query": return self?.model?.base;
|
|
128
|
+
}
|
|
129
|
+
} catch {}
|
|
127
130
|
}
|
|
131
|
+
/** Picks the selector for a given method category from a resolved config. */
|
|
128
132
|
function selectorFor(cm, methodType) {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
return cm.methods;
|
|
135
|
-
}
|
|
136
|
-
case "query": {
|
|
137
|
-
return cm.query;
|
|
138
|
-
}
|
|
139
|
-
}
|
|
133
|
+
switch (methodType) {
|
|
134
|
+
case "static": return cm.statics;
|
|
135
|
+
case "instance": return cm.methods;
|
|
136
|
+
case "query": return cm.query;
|
|
137
|
+
}
|
|
140
138
|
}
|
|
141
|
-
|
|
139
|
+
/**
|
|
140
|
+
* Symbol used to store the parent span on Query/Aggregate objects.
|
|
141
|
+
* This preserves context across chainable query methods.
|
|
142
|
+
*/
|
|
143
|
+
const _STORED_PARENT_SPAN = Symbol("stored-parent-span");
|
|
144
|
+
/**
|
|
145
|
+
* Creates a span for a Mongoose operation.
|
|
146
|
+
* Note: db.query.text is NOT set here — callers set it after payload extraction.
|
|
147
|
+
*/
|
|
142
148
|
function createSpan(tracer, operation, modelName, collectionName, config) {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
}
|
|
157
|
-
if (config.peerPort) {
|
|
158
|
-
attributes[ATTR_SERVER_PORT] = config.peerPort;
|
|
159
|
-
}
|
|
160
|
-
return tracer.startSpan(spanName, { kind: autotel.SpanKind.CLIENT, attributes });
|
|
149
|
+
const spanName = collectionName ? `${operation} ${collectionName}` : modelName ? `${operation} ${modelName}` : `mongoose.${operation}`;
|
|
150
|
+
const attributes = {
|
|
151
|
+
[ATTR_DB_SYSTEM_NAME]: DB_SYSTEM_NAME_VALUE_MONGODB,
|
|
152
|
+
[ATTR_DB_OPERATION_NAME]: operation
|
|
153
|
+
};
|
|
154
|
+
if (collectionName && config.captureCollectionName) attributes[ATTR_DB_COLLECTION_NAME] = collectionName;
|
|
155
|
+
if (config.dbName) attributes[ATTR_DB_NAMESPACE] = config.dbName;
|
|
156
|
+
if (config.peerName) attributes[ATTR_SERVER_ADDRESS] = config.peerName;
|
|
157
|
+
if (config.peerPort) attributes[ATTR_SERVER_PORT] = config.peerPort;
|
|
158
|
+
return tracer.startSpan(spanName, {
|
|
159
|
+
kind: autotel.SpanKind.CLIENT,
|
|
160
|
+
attributes
|
|
161
|
+
});
|
|
161
162
|
}
|
|
163
|
+
/**
|
|
164
|
+
* Wraps Model methods that return Query objects (find, findOne, findById,
|
|
165
|
+
* findOneAndUpdate, findOneAndDelete, findOneAndReplace, deleteOne, deleteMany,
|
|
166
|
+
* updateOne, updateMany, countDocuments, estimatedDocumentCount).
|
|
167
|
+
*
|
|
168
|
+
* Creates span FIRST, calls original, extracts payload from the returned Query,
|
|
169
|
+
* sets db.query.text AFTER extraction, then wraps exec() to finalize span.
|
|
170
|
+
*/
|
|
162
171
|
function wrapQueryReturningMethod(target, methodName, operation, getCollectionName, getModelName, tracer, config, captureStatement) {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
return value;
|
|
208
|
-
}).catch((error) => {
|
|
209
|
-
traceHelpers.finalizeSpan(
|
|
210
|
-
span,
|
|
211
|
-
error instanceof Error ? error : new Error(String(error))
|
|
212
|
-
);
|
|
213
|
-
throw error;
|
|
214
|
-
});
|
|
215
|
-
} catch (error) {
|
|
216
|
-
traceHelpers.finalizeSpan(
|
|
217
|
-
span,
|
|
218
|
-
error instanceof Error ? error : new Error(String(error))
|
|
219
|
-
);
|
|
220
|
-
throw error;
|
|
221
|
-
}
|
|
222
|
-
};
|
|
223
|
-
return result;
|
|
224
|
-
}
|
|
225
|
-
traceHelpers.finalizeSpan(span);
|
|
226
|
-
return result;
|
|
227
|
-
} catch (error) {
|
|
228
|
-
traceHelpers.finalizeSpan(
|
|
229
|
-
span,
|
|
230
|
-
error instanceof Error ? error : new Error(String(error))
|
|
231
|
-
);
|
|
232
|
-
throw error;
|
|
233
|
-
}
|
|
234
|
-
});
|
|
235
|
-
};
|
|
172
|
+
const original = target[methodName];
|
|
173
|
+
if (typeof original !== "function") return;
|
|
174
|
+
target[methodName] = function instrumented(...args) {
|
|
175
|
+
const collectionName = getCollectionName(this);
|
|
176
|
+
const span = createSpan(tracer, operation, getModelName(this), collectionName, config);
|
|
177
|
+
return (0, autotel_trace_helpers.runWithSpan)(span, () => {
|
|
178
|
+
try {
|
|
179
|
+
const result = original.apply(this, args);
|
|
180
|
+
if (result && typeof result.exec === "function") {
|
|
181
|
+
try {
|
|
182
|
+
const payload = {};
|
|
183
|
+
if (typeof result.getFilter === "function") payload.condition = result.getFilter();
|
|
184
|
+
if (result._update !== void 0) payload.updates = result._update;
|
|
185
|
+
if (typeof result.getOptions === "function") payload.options = result.getOptions();
|
|
186
|
+
if (result._fields !== void 0) payload.fields = result._fields;
|
|
187
|
+
const statementText = captureStatement(operation, payload);
|
|
188
|
+
if (statementText) span.setAttribute(ATTR_DB_QUERY_TEXT, statementText);
|
|
189
|
+
} catch {}
|
|
190
|
+
const originalExec = result.exec.bind(result);
|
|
191
|
+
result.exec = function wrappedExec() {
|
|
192
|
+
try {
|
|
193
|
+
const execPromise = originalExec();
|
|
194
|
+
return Promise.resolve(execPromise).then((value) => {
|
|
195
|
+
(0, autotel_trace_helpers.finalizeSpan)(span);
|
|
196
|
+
return value;
|
|
197
|
+
}).catch((error) => {
|
|
198
|
+
(0, autotel_trace_helpers.finalizeSpan)(span, error instanceof Error ? error : new Error(String(error)));
|
|
199
|
+
throw error;
|
|
200
|
+
});
|
|
201
|
+
} catch (error) {
|
|
202
|
+
(0, autotel_trace_helpers.finalizeSpan)(span, error instanceof Error ? error : new Error(String(error)));
|
|
203
|
+
throw error;
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
return result;
|
|
207
|
+
}
|
|
208
|
+
(0, autotel_trace_helpers.finalizeSpan)(span);
|
|
209
|
+
return result;
|
|
210
|
+
} catch (error) {
|
|
211
|
+
(0, autotel_trace_helpers.finalizeSpan)(span, error instanceof Error ? error : new Error(String(error)));
|
|
212
|
+
throw error;
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
};
|
|
236
216
|
}
|
|
217
|
+
/**
|
|
218
|
+
* Wraps Model static methods (create, insertMany, aggregate, bulkWrite).
|
|
219
|
+
*
|
|
220
|
+
* Builds payload from args BEFORE calling original (args are available
|
|
221
|
+
* immediately), creates span, sets db.query.text, calls original, then wraps
|
|
222
|
+
* exec() or promise for span finalization.
|
|
223
|
+
*/
|
|
237
224
|
function wrapStaticMethod(target, methodName, operation, getCollectionName, getModelName, tracer, config, captureStatement) {
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
traceHelpers.finalizeSpan(
|
|
304
|
-
span,
|
|
305
|
-
error instanceof Error ? error : new Error(String(error))
|
|
306
|
-
);
|
|
307
|
-
throw error;
|
|
308
|
-
}
|
|
309
|
-
};
|
|
310
|
-
return result;
|
|
311
|
-
}
|
|
312
|
-
if (result && typeof result.then === "function") {
|
|
313
|
-
return Promise.resolve(result).then((value) => {
|
|
314
|
-
traceHelpers.finalizeSpan(span);
|
|
315
|
-
return value;
|
|
316
|
-
}).catch((error) => {
|
|
317
|
-
traceHelpers.finalizeSpan(
|
|
318
|
-
span,
|
|
319
|
-
error instanceof Error ? error : new Error(String(error))
|
|
320
|
-
);
|
|
321
|
-
throw error;
|
|
322
|
-
});
|
|
323
|
-
}
|
|
324
|
-
traceHelpers.finalizeSpan(span);
|
|
325
|
-
return result;
|
|
326
|
-
} catch (error) {
|
|
327
|
-
traceHelpers.finalizeSpan(
|
|
328
|
-
span,
|
|
329
|
-
error instanceof Error ? error : new Error(String(error))
|
|
330
|
-
);
|
|
331
|
-
throw error;
|
|
332
|
-
}
|
|
333
|
-
});
|
|
334
|
-
};
|
|
225
|
+
const original = target[methodName];
|
|
226
|
+
if (typeof original !== "function") return;
|
|
227
|
+
target[methodName] = function instrumented(...args) {
|
|
228
|
+
const collectionName = getCollectionName(this);
|
|
229
|
+
const modelName = getModelName(this);
|
|
230
|
+
const payload = {};
|
|
231
|
+
try {
|
|
232
|
+
switch (operation) {
|
|
233
|
+
case "create":
|
|
234
|
+
payload.document = args[0];
|
|
235
|
+
break;
|
|
236
|
+
case "insertMany":
|
|
237
|
+
payload.documents = args[0];
|
|
238
|
+
break;
|
|
239
|
+
case "aggregate":
|
|
240
|
+
payload.aggregatePipeline = args[0];
|
|
241
|
+
break;
|
|
242
|
+
case "bulkWrite":
|
|
243
|
+
payload.operations = args[0];
|
|
244
|
+
break;
|
|
245
|
+
default: break;
|
|
246
|
+
}
|
|
247
|
+
} catch {}
|
|
248
|
+
const span = createSpan(tracer, operation, modelName, collectionName, config);
|
|
249
|
+
try {
|
|
250
|
+
const statementText = captureStatement(operation, payload);
|
|
251
|
+
if (statementText) span.setAttribute(ATTR_DB_QUERY_TEXT, statementText);
|
|
252
|
+
} catch {}
|
|
253
|
+
return (0, autotel_trace_helpers.runWithSpan)(span, () => {
|
|
254
|
+
try {
|
|
255
|
+
const result = original.apply(this, args);
|
|
256
|
+
if (result && typeof result.exec === "function") {
|
|
257
|
+
const originalExec = result.exec.bind(result);
|
|
258
|
+
result.exec = function wrappedExec() {
|
|
259
|
+
try {
|
|
260
|
+
const execPromise = originalExec();
|
|
261
|
+
return Promise.resolve(execPromise).then((value) => {
|
|
262
|
+
(0, autotel_trace_helpers.finalizeSpan)(span);
|
|
263
|
+
return value;
|
|
264
|
+
}).catch((error) => {
|
|
265
|
+
(0, autotel_trace_helpers.finalizeSpan)(span, error instanceof Error ? error : new Error(String(error)));
|
|
266
|
+
throw error;
|
|
267
|
+
});
|
|
268
|
+
} catch (error) {
|
|
269
|
+
(0, autotel_trace_helpers.finalizeSpan)(span, error instanceof Error ? error : new Error(String(error)));
|
|
270
|
+
throw error;
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
return result;
|
|
274
|
+
}
|
|
275
|
+
if (result && typeof result.then === "function") return Promise.resolve(result).then((value) => {
|
|
276
|
+
(0, autotel_trace_helpers.finalizeSpan)(span);
|
|
277
|
+
return value;
|
|
278
|
+
}).catch((error) => {
|
|
279
|
+
(0, autotel_trace_helpers.finalizeSpan)(span, error instanceof Error ? error : new Error(String(error)));
|
|
280
|
+
throw error;
|
|
281
|
+
});
|
|
282
|
+
(0, autotel_trace_helpers.finalizeSpan)(span);
|
|
283
|
+
return result;
|
|
284
|
+
} catch (error) {
|
|
285
|
+
(0, autotel_trace_helpers.finalizeSpan)(span, error instanceof Error ? error : new Error(String(error)));
|
|
286
|
+
throw error;
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
};
|
|
335
290
|
}
|
|
291
|
+
/**
|
|
292
|
+
* Wraps Model instance methods (save, deleteOne on prototype).
|
|
293
|
+
*
|
|
294
|
+
* Extracts document via `this.toObject()` BEFORE calling original,
|
|
295
|
+
* creates span, sets db.query.text, calls original, wraps promise
|
|
296
|
+
* for span finalization.
|
|
297
|
+
*/
|
|
336
298
|
function wrapInstanceMethod(target, methodName, operation, getCollectionName, getModelName, tracer, config, captureStatement) {
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
return Promise.resolve(result).then((value) => {
|
|
370
|
-
traceHelpers.finalizeSpan(span);
|
|
371
|
-
return value;
|
|
372
|
-
}).catch((error) => {
|
|
373
|
-
traceHelpers.finalizeSpan(
|
|
374
|
-
span,
|
|
375
|
-
error instanceof Error ? error : new Error(String(error))
|
|
376
|
-
);
|
|
377
|
-
throw error;
|
|
378
|
-
});
|
|
379
|
-
}
|
|
380
|
-
traceHelpers.finalizeSpan(span);
|
|
381
|
-
return result;
|
|
382
|
-
} catch (error) {
|
|
383
|
-
traceHelpers.finalizeSpan(
|
|
384
|
-
span,
|
|
385
|
-
error instanceof Error ? error : new Error(String(error))
|
|
386
|
-
);
|
|
387
|
-
throw error;
|
|
388
|
-
}
|
|
389
|
-
});
|
|
390
|
-
};
|
|
299
|
+
const original = target[methodName];
|
|
300
|
+
if (typeof original !== "function") return;
|
|
301
|
+
target[methodName] = function instrumented(...args) {
|
|
302
|
+
const collectionName = getCollectionName(this);
|
|
303
|
+
const modelName = getModelName(this);
|
|
304
|
+
const payload = {};
|
|
305
|
+
try {
|
|
306
|
+
if (typeof this.toObject === "function") payload.document = this.toObject();
|
|
307
|
+
} catch {}
|
|
308
|
+
const span = createSpan(tracer, operation, modelName, collectionName, config);
|
|
309
|
+
try {
|
|
310
|
+
const statementText = captureStatement(operation, payload);
|
|
311
|
+
if (statementText) span.setAttribute(ATTR_DB_QUERY_TEXT, statementText);
|
|
312
|
+
} catch {}
|
|
313
|
+
return (0, autotel_trace_helpers.runWithSpan)(span, () => {
|
|
314
|
+
try {
|
|
315
|
+
const result = original.apply(this, args);
|
|
316
|
+
if (result && typeof result.then === "function") return Promise.resolve(result).then((value) => {
|
|
317
|
+
(0, autotel_trace_helpers.finalizeSpan)(span);
|
|
318
|
+
return value;
|
|
319
|
+
}).catch((error) => {
|
|
320
|
+
(0, autotel_trace_helpers.finalizeSpan)(span, error instanceof Error ? error : new Error(String(error)));
|
|
321
|
+
throw error;
|
|
322
|
+
});
|
|
323
|
+
(0, autotel_trace_helpers.finalizeSpan)(span);
|
|
324
|
+
return result;
|
|
325
|
+
} catch (error) {
|
|
326
|
+
(0, autotel_trace_helpers.finalizeSpan)(span, error instanceof Error ? error : new Error(String(error)));
|
|
327
|
+
throw error;
|
|
328
|
+
}
|
|
329
|
+
});
|
|
330
|
+
};
|
|
391
331
|
}
|
|
332
|
+
/**
|
|
333
|
+
* Wraps chainable Query methods (populate, select, lean, etc.) to capture span context.
|
|
334
|
+
*/
|
|
392
335
|
function wrapChainableMethod(target, methodName) {
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
result[_STORED_PARENT_SPAN] = currentSpan;
|
|
402
|
-
}
|
|
403
|
-
return result;
|
|
404
|
-
};
|
|
336
|
+
const original = target[methodName];
|
|
337
|
+
if (typeof original !== "function") return;
|
|
338
|
+
target[methodName] = function captureContext(...args) {
|
|
339
|
+
const currentSpan = (0, autotel_trace_helpers.getActiveSpan)();
|
|
340
|
+
const result = original.apply(this, args);
|
|
341
|
+
if (result && typeof result.exec === "function") result[_STORED_PARENT_SPAN] = currentSpan;
|
|
342
|
+
return result;
|
|
343
|
+
};
|
|
405
344
|
}
|
|
345
|
+
/**
|
|
346
|
+
* Patches Mongoose Schema hooks (pre/post) to automatically trace them.
|
|
347
|
+
* Only wraps user-defined hooks, skipping Mongoose's internal hooks.
|
|
348
|
+
*/
|
|
406
349
|
function patchSchemaHooks(Schema, tracer, config) {
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
}
|
|
432
|
-
return Reflect.apply(originalPre, this, [hookName, ...args]);
|
|
433
|
-
};
|
|
434
|
-
}
|
|
435
|
-
const originalPost = Schema.prototype.post;
|
|
436
|
-
if (typeof originalPost === "function") {
|
|
437
|
-
Schema.prototype.post = function(hookName, ...args) {
|
|
438
|
-
const handler = typeof args[0] === "function" ? args[0] : typeof args[1] === "function" ? args[1] : null;
|
|
439
|
-
if (handler && !isMongooseInternalHook(handler)) {
|
|
440
|
-
const wrapped = wrapHookHandler(
|
|
441
|
-
handler,
|
|
442
|
-
hookName,
|
|
443
|
-
"post",
|
|
444
|
-
tracer,
|
|
445
|
-
config
|
|
446
|
-
);
|
|
447
|
-
if (typeof args[0] === "function") {
|
|
448
|
-
args[0] = wrapped;
|
|
449
|
-
} else if (typeof args[1] === "function") {
|
|
450
|
-
args[1] = wrapped;
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
return Reflect.apply(originalPost, this, [hookName, ...args]);
|
|
454
|
-
};
|
|
455
|
-
}
|
|
456
|
-
Schema.prototype[HOOK_FLAG] = true;
|
|
350
|
+
if (!Schema?.prototype) return;
|
|
351
|
+
const HOOK_FLAG = "__autotelHookInstrumented";
|
|
352
|
+
if (Schema.prototype[HOOK_FLAG]) return;
|
|
353
|
+
const originalPre = Schema.prototype.pre;
|
|
354
|
+
if (typeof originalPre === "function") Schema.prototype.pre = function(hookName, ...args) {
|
|
355
|
+
const handler = typeof args[0] === "function" ? args[0] : typeof args[1] === "function" ? args[1] : null;
|
|
356
|
+
if (handler && !isMongooseInternalHook(handler)) {
|
|
357
|
+
const wrapped = wrapHookHandler(handler, hookName, "pre", tracer, config);
|
|
358
|
+
if (typeof args[0] === "function") args[0] = wrapped;
|
|
359
|
+
else if (typeof args[1] === "function") args[1] = wrapped;
|
|
360
|
+
}
|
|
361
|
+
return Reflect.apply(originalPre, this, [hookName, ...args]);
|
|
362
|
+
};
|
|
363
|
+
const originalPost = Schema.prototype.post;
|
|
364
|
+
if (typeof originalPost === "function") Schema.prototype.post = function(hookName, ...args) {
|
|
365
|
+
const handler = typeof args[0] === "function" ? args[0] : typeof args[1] === "function" ? args[1] : null;
|
|
366
|
+
if (handler && !isMongooseInternalHook(handler)) {
|
|
367
|
+
const wrapped = wrapHookHandler(handler, hookName, "post", tracer, config);
|
|
368
|
+
if (typeof args[0] === "function") args[0] = wrapped;
|
|
369
|
+
else if (typeof args[1] === "function") args[1] = wrapped;
|
|
370
|
+
}
|
|
371
|
+
return Reflect.apply(originalPost, this, [hookName, ...args]);
|
|
372
|
+
};
|
|
373
|
+
Schema.prototype[HOOK_FLAG] = true;
|
|
457
374
|
}
|
|
375
|
+
/**
|
|
376
|
+
* Detects if a hook handler is from Mongoose's internal code.
|
|
377
|
+
* Skips private methods, known internal patterns, and functions with
|
|
378
|
+
* Mongoose-internal source code signatures.
|
|
379
|
+
*
|
|
380
|
+
* Note: We intentionally allow anonymous functions because user-defined
|
|
381
|
+
* hooks are often anonymous (e.g., `schema.pre('save', async function() {...})`).
|
|
382
|
+
*/
|
|
458
383
|
function isMongooseInternalHook(handler) {
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
"this.$isValid",
|
|
485
|
-
// Mongoose validation
|
|
486
|
-
"this.$locals",
|
|
487
|
-
// Mongoose local properties
|
|
488
|
-
"_this.$__",
|
|
489
|
-
// Mongoose internal with closure
|
|
490
|
-
"schema.s.hooks",
|
|
491
|
-
// Mongoose hooks system
|
|
492
|
-
"kareem"
|
|
493
|
-
// Mongoose's hooks library
|
|
494
|
-
];
|
|
495
|
-
if (mongooseInternalSourcePatterns.some((pattern) => source.includes(pattern))) {
|
|
496
|
-
return true;
|
|
497
|
-
}
|
|
498
|
-
} catch {
|
|
499
|
-
}
|
|
500
|
-
return false;
|
|
384
|
+
if (typeof handler !== "function") return false;
|
|
385
|
+
const funcName = handler.name || "";
|
|
386
|
+
if (funcName.startsWith("_") || funcName.startsWith("$")) return true;
|
|
387
|
+
if ([
|
|
388
|
+
"shardingPlugin",
|
|
389
|
+
"mongooseInternalHook",
|
|
390
|
+
"noop",
|
|
391
|
+
"wrapped",
|
|
392
|
+
"bound ",
|
|
393
|
+
"timestampsPreSave",
|
|
394
|
+
"timestampsPreUpdate",
|
|
395
|
+
"handleTimestampOption"
|
|
396
|
+
].some((pattern) => funcName.includes(pattern))) return true;
|
|
397
|
+
try {
|
|
398
|
+
const source = handler.toString();
|
|
399
|
+
if ([
|
|
400
|
+
"this.$__",
|
|
401
|
+
"this.$isValid",
|
|
402
|
+
"this.$locals",
|
|
403
|
+
"_this.$__",
|
|
404
|
+
"schema.s.hooks",
|
|
405
|
+
"kareem"
|
|
406
|
+
].some((pattern) => source.includes(pattern))) return true;
|
|
407
|
+
} catch {}
|
|
408
|
+
return false;
|
|
501
409
|
}
|
|
410
|
+
/**
|
|
411
|
+
* Wraps a hook handler to trace its execution.
|
|
412
|
+
* Handles both callback-style (with next) and promise-style hooks.
|
|
413
|
+
*/
|
|
502
414
|
function wrapHookHandler(handler, hookName, hookType, tracer, config) {
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
throw error;
|
|
564
|
-
}
|
|
565
|
-
});
|
|
566
|
-
const wrappedHook = expectsCallback ? function wrappedHookCallback(arg0, ...args) {
|
|
567
|
-
const span = startHookSpan(this);
|
|
568
|
-
const runtimeArgs = [arg0, ...args];
|
|
569
|
-
const callbackIndex = runtimeArgs.findIndex(
|
|
570
|
-
(arg) => typeof arg === "function"
|
|
571
|
-
);
|
|
572
|
-
const originalNext = callbackIndex === -1 ? void 0 : runtimeArgs[callbackIndex];
|
|
573
|
-
const callArgs = callbackIndex === -1 ? runtimeArgs : runtimeArgs.filter((_, index) => index !== callbackIndex);
|
|
574
|
-
const wrappedNext = function wrappedNext2(...nextArgs) {
|
|
575
|
-
const err = nextArgs[0];
|
|
576
|
-
if (err) {
|
|
577
|
-
traceHelpers.finalizeSpan(
|
|
578
|
-
span,
|
|
579
|
-
err instanceof Error ? err : new Error(String(err))
|
|
580
|
-
);
|
|
581
|
-
} else {
|
|
582
|
-
traceHelpers.finalizeSpan(span);
|
|
583
|
-
}
|
|
584
|
-
if (typeof originalNext === "function") {
|
|
585
|
-
return originalNext.apply(this, nextArgs);
|
|
586
|
-
}
|
|
587
|
-
return;
|
|
588
|
-
};
|
|
589
|
-
return invokeHook(this, span, [wrappedNext, ...callArgs], true);
|
|
590
|
-
} : function wrappedHookPromise(...args) {
|
|
591
|
-
const span = startHookSpan(this);
|
|
592
|
-
return invokeHook(this, span, args, false);
|
|
593
|
-
};
|
|
594
|
-
wrappedHook[WRAPPED_HOOK_FLAG] = true;
|
|
595
|
-
return wrappedHook;
|
|
415
|
+
if (typeof handler !== "function") return handler;
|
|
416
|
+
if (handler[WRAPPED_HOOK_FLAG]) return handler;
|
|
417
|
+
const expectsCallback = handler.length > 0;
|
|
418
|
+
const startHookSpan = (self) => {
|
|
419
|
+
let modelName;
|
|
420
|
+
let collectionName;
|
|
421
|
+
try {
|
|
422
|
+
if (self.constructor?.modelName) {
|
|
423
|
+
modelName = self.constructor.modelName;
|
|
424
|
+
collectionName = self.constructor.collection?.collectionName || modelName;
|
|
425
|
+
} else if (self.model?.modelName) {
|
|
426
|
+
modelName = self.model.modelName;
|
|
427
|
+
collectionName = self.model.collection?.collectionName || modelName;
|
|
428
|
+
}
|
|
429
|
+
} catch {}
|
|
430
|
+
const spanName = collectionName ? `mongoose.${collectionName}.${hookType}.${hookName}` : `mongoose.hook.${hookType}.${hookName}`;
|
|
431
|
+
const span = tracer.startSpan(spanName, { kind: autotel.SpanKind.INTERNAL });
|
|
432
|
+
span.setAttribute("hook.type", hookType);
|
|
433
|
+
span.setAttribute("hook.operation", hookName);
|
|
434
|
+
if (modelName) span.setAttribute("hook.model", modelName);
|
|
435
|
+
if (collectionName && config.captureCollectionName) span.setAttribute(ATTR_DB_COLLECTION_NAME, collectionName);
|
|
436
|
+
span.setAttribute(ATTR_DB_SYSTEM_NAME, DB_SYSTEM_NAME_VALUE_MONGODB);
|
|
437
|
+
if (config.dbName) span.setAttribute(ATTR_DB_NAMESPACE, config.dbName);
|
|
438
|
+
return span;
|
|
439
|
+
};
|
|
440
|
+
const invokeHook = (self, span, args, callbackStyle) => (0, autotel_trace_helpers.runWithSpan)(span, () => {
|
|
441
|
+
try {
|
|
442
|
+
const result = handler.apply(self, args);
|
|
443
|
+
if (result && typeof result.then === "function") return Promise.resolve(result).then((value) => {
|
|
444
|
+
(0, autotel_trace_helpers.finalizeSpan)(span);
|
|
445
|
+
return value;
|
|
446
|
+
}).catch((error) => {
|
|
447
|
+
(0, autotel_trace_helpers.finalizeSpan)(span, error instanceof Error ? error : new Error(String(error)));
|
|
448
|
+
throw error;
|
|
449
|
+
});
|
|
450
|
+
if (!callbackStyle) (0, autotel_trace_helpers.finalizeSpan)(span);
|
|
451
|
+
return result;
|
|
452
|
+
} catch (error) {
|
|
453
|
+
(0, autotel_trace_helpers.finalizeSpan)(span, error instanceof Error ? error : new Error(String(error)));
|
|
454
|
+
throw error;
|
|
455
|
+
}
|
|
456
|
+
});
|
|
457
|
+
const wrappedHook = expectsCallback ? function wrappedHookCallback(arg0, ...args) {
|
|
458
|
+
const span = startHookSpan(this);
|
|
459
|
+
const runtimeArgs = [arg0, ...args];
|
|
460
|
+
const callbackIndex = runtimeArgs.findIndex((arg) => typeof arg === "function");
|
|
461
|
+
const originalNext = callbackIndex === -1 ? void 0 : runtimeArgs[callbackIndex];
|
|
462
|
+
const callArgs = callbackIndex === -1 ? runtimeArgs : runtimeArgs.filter((_, index) => index !== callbackIndex);
|
|
463
|
+
return invokeHook(this, span, [function wrappedNext(...nextArgs) {
|
|
464
|
+
const err = nextArgs[0];
|
|
465
|
+
if (err) (0, autotel_trace_helpers.finalizeSpan)(span, err instanceof Error ? err : new Error(String(err)));
|
|
466
|
+
else (0, autotel_trace_helpers.finalizeSpan)(span);
|
|
467
|
+
if (typeof originalNext === "function") return originalNext.apply(this, nextArgs);
|
|
468
|
+
}, ...callArgs], true);
|
|
469
|
+
} : function wrappedHookPromise(...args) {
|
|
470
|
+
const span = startHookSpan(this);
|
|
471
|
+
return invokeHook(this, span, args, false);
|
|
472
|
+
};
|
|
473
|
+
wrappedHook[WRAPPED_HOOK_FLAG] = true;
|
|
474
|
+
return wrappedHook;
|
|
596
475
|
}
|
|
476
|
+
/**
|
|
477
|
+
* Resolves the `customMethods` config into a concrete, defaults-applied shape.
|
|
478
|
+
* Omitted/true → wrap everything and capture (redacted) parameters.
|
|
479
|
+
*/
|
|
597
480
|
function resolveCustomMethods(config) {
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
statics: obj.statics ?? true,
|
|
621
|
-
methods: obj.methods ?? true,
|
|
622
|
-
query: obj.query ?? true,
|
|
623
|
-
captureParameters
|
|
624
|
-
};
|
|
481
|
+
const setting = config?.customMethods;
|
|
482
|
+
if (setting === false) return {
|
|
483
|
+
enabled: false,
|
|
484
|
+
statics: false,
|
|
485
|
+
methods: false,
|
|
486
|
+
query: false,
|
|
487
|
+
captureParameters: false
|
|
488
|
+
};
|
|
489
|
+
const obj = setting === void 0 || setting === true ? {} : setting;
|
|
490
|
+
const cp = obj.captureParameters;
|
|
491
|
+
let captureParameters = false;
|
|
492
|
+
if (cp !== false) captureParameters = createParameterCapture({
|
|
493
|
+
parameterConfig: cp === void 0 || cp === true ? void 0 : cp,
|
|
494
|
+
statementRedactor: config?.statementRedactor ?? "default"
|
|
495
|
+
});
|
|
496
|
+
return {
|
|
497
|
+
enabled: true,
|
|
498
|
+
statics: obj.statics ?? true,
|
|
499
|
+
methods: obj.methods ?? true,
|
|
500
|
+
query: obj.query ?? true,
|
|
501
|
+
captureParameters
|
|
502
|
+
};
|
|
625
503
|
}
|
|
504
|
+
/**
|
|
505
|
+
* Evaluates whether a named function in a category should be instrumented.
|
|
506
|
+
* Supports boolean, include-list, and `{ include, exclude }` selectors.
|
|
507
|
+
*/
|
|
626
508
|
function selectorAllows(selector, name) {
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
if (Array.isArray(selector)) {
|
|
634
|
-
return selector.includes(name);
|
|
635
|
-
}
|
|
636
|
-
if (selector.include && !selector.include.includes(name)) {
|
|
637
|
-
return false;
|
|
638
|
-
}
|
|
639
|
-
if (selector.exclude && selector.exclude.includes(name)) {
|
|
640
|
-
return false;
|
|
641
|
-
}
|
|
642
|
-
return true;
|
|
509
|
+
if (selector === false) return false;
|
|
510
|
+
if (selector === true) return true;
|
|
511
|
+
if (Array.isArray(selector)) return selector.includes(name);
|
|
512
|
+
if (selector.include && !selector.include.includes(name)) return false;
|
|
513
|
+
if (selector.exclude && selector.exclude.includes(name)) return false;
|
|
514
|
+
return true;
|
|
643
515
|
}
|
|
516
|
+
/**
|
|
517
|
+
* Derives model + collection names from the runtime `this` of a custom
|
|
518
|
+
* function, which differs by category (Model / Document / Query).
|
|
519
|
+
*/
|
|
644
520
|
function resolveModelContext(self, methodType) {
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
} catch {
|
|
669
|
-
}
|
|
670
|
-
return {};
|
|
521
|
+
try {
|
|
522
|
+
switch (methodType) {
|
|
523
|
+
case "static": return {
|
|
524
|
+
modelName: self?.modelName,
|
|
525
|
+
collectionName: self?.collection?.collectionName || self?.modelName
|
|
526
|
+
};
|
|
527
|
+
case "instance": {
|
|
528
|
+
const ctor = self?.constructor;
|
|
529
|
+
return {
|
|
530
|
+
modelName: ctor?.modelName,
|
|
531
|
+
collectionName: ctor?.collection?.collectionName || ctor?.modelName
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
case "query": {
|
|
535
|
+
const model = self?.model;
|
|
536
|
+
return {
|
|
537
|
+
modelName: model?.modelName,
|
|
538
|
+
collectionName: model?.collection?.collectionName || model?.modelName
|
|
539
|
+
};
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
} catch {}
|
|
543
|
+
return {};
|
|
671
544
|
}
|
|
545
|
+
/**
|
|
546
|
+
* Wraps a single user-defined function so its invocation is traced. Purely
|
|
547
|
+
* observational: preserves `this`, the return value, and error propagation.
|
|
548
|
+
*
|
|
549
|
+
* The tracer, config, and selection are resolved per Mongoose instance at call
|
|
550
|
+
* time (see {@link INSTANCE_REGISTRY}), so a schema shared across instances or
|
|
551
|
+
* connections is never bound to whichever config instrumented it first. Calls
|
|
552
|
+
* from a non-instrumented, disabled, or de-selected instance pass straight
|
|
553
|
+
* through with no span.
|
|
554
|
+
*/
|
|
672
555
|
function wrapCustomFunction(original, methodName, methodType) {
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
}).catch((error) => {
|
|
749
|
-
traceHelpers.finalizeSpan(
|
|
750
|
-
span,
|
|
751
|
-
error instanceof Error ? error : new Error(String(error))
|
|
752
|
-
);
|
|
753
|
-
throw error;
|
|
754
|
-
});
|
|
755
|
-
}
|
|
756
|
-
traceHelpers.finalizeSpan(span);
|
|
757
|
-
return result;
|
|
758
|
-
} catch (error) {
|
|
759
|
-
traceHelpers.finalizeSpan(
|
|
760
|
-
span,
|
|
761
|
-
error instanceof Error ? error : new Error(String(error))
|
|
762
|
-
);
|
|
763
|
-
throw error;
|
|
764
|
-
}
|
|
765
|
-
});
|
|
766
|
-
};
|
|
767
|
-
try {
|
|
768
|
-
Object.defineProperty(wrapped, "name", {
|
|
769
|
-
value: original.name || methodName,
|
|
770
|
-
configurable: true
|
|
771
|
-
});
|
|
772
|
-
} catch {
|
|
773
|
-
}
|
|
774
|
-
wrapped[WRAPPED_METHOD_FLAG] = true;
|
|
775
|
-
return wrapped;
|
|
556
|
+
if (original[WRAPPED_METHOD_FLAG]) return original;
|
|
557
|
+
const wrapped = function instrumentedCustomFn(...args) {
|
|
558
|
+
const instance = resolveMongooseInstance(this, methodType);
|
|
559
|
+
const entry = instance ? INSTANCE_REGISTRY.get(instance) : void 0;
|
|
560
|
+
if (!entry || !entry.config.customMethods.enabled || !selectorAllows(selectorFor(entry.config.customMethods, methodType), methodName)) return original.apply(this, args);
|
|
561
|
+
const { tracer, config } = entry;
|
|
562
|
+
const captureParameters = config.customMethods.captureParameters;
|
|
563
|
+
const { modelName, collectionName } = resolveModelContext(this, methodType);
|
|
564
|
+
const spanName = modelName ? `mongoose.${modelName}.${methodName}` : `mongoose.${methodType}.${methodName}`;
|
|
565
|
+
const span = tracer.startSpan(spanName, { kind: autotel.SpanKind.INTERNAL });
|
|
566
|
+
span.setAttribute(ATTR_DB_SYSTEM_NAME, DB_SYSTEM_NAME_VALUE_MONGODB);
|
|
567
|
+
span.setAttribute(ATTR_CODE_FUNCTION_NAME, methodName);
|
|
568
|
+
span.setAttribute(ATTR_MONGOOSE_METHOD_NAME, methodName);
|
|
569
|
+
span.setAttribute(ATTR_MONGOOSE_METHOD_TYPE, methodType);
|
|
570
|
+
if (modelName) span.setAttribute(ATTR_MONGOOSE_METHOD_MODEL, modelName);
|
|
571
|
+
if (collectionName && config.captureCollectionName) span.setAttribute(ATTR_DB_COLLECTION_NAME, collectionName);
|
|
572
|
+
if (config.dbName) span.setAttribute(ATTR_DB_NAMESPACE, config.dbName);
|
|
573
|
+
if (captureParameters) {
|
|
574
|
+
span.setAttribute(ATTR_MONGOOSE_METHOD_PARAMETER_COUNT, args.length);
|
|
575
|
+
try {
|
|
576
|
+
const params = captureParameters(args, {
|
|
577
|
+
methodName,
|
|
578
|
+
methodType
|
|
579
|
+
});
|
|
580
|
+
if (params !== void 0) span.setAttribute(ATTR_MONGOOSE_METHOD_PARAMETERS, params);
|
|
581
|
+
} catch {}
|
|
582
|
+
}
|
|
583
|
+
return (0, autotel_trace_helpers.runWithSpan)(span, () => {
|
|
584
|
+
try {
|
|
585
|
+
const result = original.apply(this, args);
|
|
586
|
+
if (methodType === "query") {
|
|
587
|
+
(0, autotel_trace_helpers.finalizeSpan)(span);
|
|
588
|
+
return result;
|
|
589
|
+
}
|
|
590
|
+
if (result && typeof result.exec === "function") {
|
|
591
|
+
const originalExec = result.exec.bind(result);
|
|
592
|
+
result.exec = function wrappedExec() {
|
|
593
|
+
try {
|
|
594
|
+
return Promise.resolve(originalExec()).then((value) => {
|
|
595
|
+
(0, autotel_trace_helpers.finalizeSpan)(span);
|
|
596
|
+
return value;
|
|
597
|
+
}).catch((error) => {
|
|
598
|
+
(0, autotel_trace_helpers.finalizeSpan)(span, error instanceof Error ? error : new Error(String(error)));
|
|
599
|
+
throw error;
|
|
600
|
+
});
|
|
601
|
+
} catch (error) {
|
|
602
|
+
(0, autotel_trace_helpers.finalizeSpan)(span, error instanceof Error ? error : new Error(String(error)));
|
|
603
|
+
throw error;
|
|
604
|
+
}
|
|
605
|
+
};
|
|
606
|
+
return result;
|
|
607
|
+
}
|
|
608
|
+
if (result && typeof result.then === "function") return Promise.resolve(result).then((value) => {
|
|
609
|
+
(0, autotel_trace_helpers.finalizeSpan)(span);
|
|
610
|
+
return value;
|
|
611
|
+
}).catch((error) => {
|
|
612
|
+
(0, autotel_trace_helpers.finalizeSpan)(span, error instanceof Error ? error : new Error(String(error)));
|
|
613
|
+
throw error;
|
|
614
|
+
});
|
|
615
|
+
(0, autotel_trace_helpers.finalizeSpan)(span);
|
|
616
|
+
return result;
|
|
617
|
+
} catch (error) {
|
|
618
|
+
(0, autotel_trace_helpers.finalizeSpan)(span, error instanceof Error ? error : new Error(String(error)));
|
|
619
|
+
throw error;
|
|
620
|
+
}
|
|
621
|
+
});
|
|
622
|
+
};
|
|
623
|
+
try {
|
|
624
|
+
Object.defineProperty(wrapped, "name", {
|
|
625
|
+
value: original.name || methodName,
|
|
626
|
+
configurable: true
|
|
627
|
+
});
|
|
628
|
+
} catch {}
|
|
629
|
+
wrapped[WRAPPED_METHOD_FLAG] = true;
|
|
630
|
+
return wrapped;
|
|
776
631
|
}
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
632
|
+
/**
|
|
633
|
+
* Wraps every user-defined function on a schema in place (statics / methods /
|
|
634
|
+
* query) at model-compile time. Mutating the schema's collections before
|
|
635
|
+
* compilation means Mongoose copies the wrapped versions onto the Model, its
|
|
636
|
+
* prototype, and its query class.
|
|
637
|
+
*
|
|
638
|
+
* Wrapping is unconditional and idempotent: each wrapper decides per Mongoose
|
|
639
|
+
* instance at call time whether to actually trace (honoring that instance's
|
|
640
|
+
* `enabled` flag and include/exclude selectors). This keeps a schema shared
|
|
641
|
+
* across instances correct — a function excluded by one instance can still be
|
|
642
|
+
* traced by another, and vice versa — while a function is only ever wrapped
|
|
643
|
+
* once.
|
|
644
|
+
*/
|
|
645
|
+
/**
|
|
646
|
+
* Functions Mongoose itself injects into `schema.statics`/`methods`/`query`
|
|
647
|
+
* (e.g. the `timestamps: true` option adds an `initializeTimestamps` instance
|
|
648
|
+
* method). These are framework internals, not user code, so we skip them to
|
|
649
|
+
* avoid noisy spans. Names starting with `$` (Mongoose's internal prefix) are
|
|
650
|
+
* also skipped.
|
|
651
|
+
*/
|
|
652
|
+
const MONGOOSE_INTERNAL_FUNCTION_NAMES = new Set(["initializeTimestamps"]);
|
|
780
653
|
function isMongooseInternalFunctionName(name) {
|
|
781
|
-
|
|
654
|
+
return name.startsWith("$") || MONGOOSE_INTERNAL_FUNCTION_NAMES.has(name);
|
|
782
655
|
}
|
|
783
656
|
function instrumentSchemaCustomFunctions(schema) {
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
continue;
|
|
798
|
-
}
|
|
799
|
-
collection[name] = wrapCustomFunction(fn, name, methodType);
|
|
800
|
-
}
|
|
801
|
-
};
|
|
802
|
-
wrapCollection(schema.statics, "static");
|
|
803
|
-
wrapCollection(schema.methods, "instance");
|
|
804
|
-
wrapCollection(schema.query, "query");
|
|
657
|
+
if (!schema) return;
|
|
658
|
+
const wrapCollection = (collection, methodType) => {
|
|
659
|
+
if (!collection) return;
|
|
660
|
+
for (const name of Object.keys(collection)) {
|
|
661
|
+
if (isMongooseInternalFunctionName(name)) continue;
|
|
662
|
+
const fn = collection[name];
|
|
663
|
+
if (typeof fn !== "function" || fn[WRAPPED_METHOD_FLAG]) continue;
|
|
664
|
+
collection[name] = wrapCustomFunction(fn, name, methodType);
|
|
665
|
+
}
|
|
666
|
+
};
|
|
667
|
+
wrapCollection(schema.statics, "static");
|
|
668
|
+
wrapCollection(schema.methods, "instance");
|
|
669
|
+
wrapCollection(schema.query, "query");
|
|
805
670
|
}
|
|
671
|
+
/**
|
|
672
|
+
* Patches `mongoose.model()` (and `Connection.prototype.model()`) so custom
|
|
673
|
+
* functions are wrapped automatically as each model compiles. Idempotent per
|
|
674
|
+
* host and per function. Whether a wrapped function actually traces is decided
|
|
675
|
+
* per instance at call time, so it is safe for the patch to be global.
|
|
676
|
+
*/
|
|
806
677
|
function patchModelFactory(m, config) {
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
...rest
|
|
826
|
-
]);
|
|
827
|
-
};
|
|
828
|
-
host[MODEL_PATCHED_FLAG] = true;
|
|
829
|
-
};
|
|
830
|
-
patchHost(m);
|
|
831
|
-
if (m.Connection?.prototype) {
|
|
832
|
-
patchHost(m.Connection.prototype);
|
|
833
|
-
}
|
|
678
|
+
if (!config.customMethods.enabled) return;
|
|
679
|
+
const patchHost = (host) => {
|
|
680
|
+
if (!host || typeof host.model !== "function" || host[MODEL_PATCHED_FLAG]) return;
|
|
681
|
+
const originalModel = host.model;
|
|
682
|
+
host.model = function patchedModel(nameOrSchema, schema, ...rest) {
|
|
683
|
+
if (schema && typeof schema === "object") try {
|
|
684
|
+
instrumentSchemaCustomFunctions(schema);
|
|
685
|
+
} catch {}
|
|
686
|
+
return Reflect.apply(originalModel, this, [
|
|
687
|
+
nameOrSchema,
|
|
688
|
+
schema,
|
|
689
|
+
...rest
|
|
690
|
+
]);
|
|
691
|
+
};
|
|
692
|
+
host[MODEL_PATCHED_FLAG] = true;
|
|
693
|
+
};
|
|
694
|
+
patchHost(m);
|
|
695
|
+
if (m.Connection?.prototype) patchHost(m.Connection.prototype);
|
|
834
696
|
}
|
|
697
|
+
/**
|
|
698
|
+
* Instruments Mongoose with OpenTelemetry tracing.
|
|
699
|
+
*
|
|
700
|
+
* Supports Mongoose 8+ with promise-based API only.
|
|
701
|
+
* Patches Model methods, Query methods, and user-defined Schema hooks to create spans.
|
|
702
|
+
*
|
|
703
|
+
* **IMPORTANT:** Call `instrumentMongoose()` BEFORE defining schemas/models
|
|
704
|
+
* to ensure hooks are automatically instrumented.
|
|
705
|
+
*
|
|
706
|
+
* @example
|
|
707
|
+
* ```typescript
|
|
708
|
+
* import mongoose from 'mongoose';
|
|
709
|
+
* import { init } from 'autotel';
|
|
710
|
+
* import { instrumentMongoose } from 'autotel-mongoose';
|
|
711
|
+
*
|
|
712
|
+
* init({ service: 'my-app' });
|
|
713
|
+
*
|
|
714
|
+
* // Call BEFORE defining schemas
|
|
715
|
+
* instrumentMongoose(mongoose, { dbName: 'myapp' });
|
|
716
|
+
*
|
|
717
|
+
* const userSchema = new mongoose.Schema({ name: String });
|
|
718
|
+
* const User = mongoose.model('User', userSchema);
|
|
719
|
+
*
|
|
720
|
+
* // All operations are automatically traced
|
|
721
|
+
* await User.findOne({}).populate('posts').exec();
|
|
722
|
+
* ```
|
|
723
|
+
*/
|
|
835
724
|
function instrumentMongoose(mongoose, config) {
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
for (const method of chainableMethods) {
|
|
960
|
-
if (m.Query?.prototype?.[method]) {
|
|
961
|
-
wrapChainableMethod(m.Query.prototype, method);
|
|
962
|
-
}
|
|
963
|
-
}
|
|
964
|
-
m[INSTRUMENTED_FLAG] = true;
|
|
965
|
-
return mongoose;
|
|
725
|
+
if (!mongoose?.Model) return mongoose;
|
|
726
|
+
const m = mongoose;
|
|
727
|
+
if (m[INSTRUMENTED_FLAG]) return mongoose;
|
|
728
|
+
const resolvedSerializer = config?.dbStatementSerializer;
|
|
729
|
+
const resolvedRedactor = config?.statementRedactor ?? "default";
|
|
730
|
+
const finalConfig = {
|
|
731
|
+
dbName: config?.dbName || "",
|
|
732
|
+
peerName: config?.peerName || "",
|
|
733
|
+
peerPort: config?.peerPort || 27017,
|
|
734
|
+
tracerName: config?.tracerName || "autotel-mongoose",
|
|
735
|
+
captureCollectionName: config?.captureCollectionName ?? true,
|
|
736
|
+
instrumentHooks: config?.instrumentHooks ?? false,
|
|
737
|
+
dbStatementSerializer: resolvedSerializer === false ? false : resolvedSerializer ?? defaultSerializer,
|
|
738
|
+
statementRedactor: resolvedRedactor,
|
|
739
|
+
customMethods: resolveCustomMethods(config)
|
|
740
|
+
};
|
|
741
|
+
const tracer = autotel.otelTrace.getTracer(finalConfig.tracerName);
|
|
742
|
+
INSTANCE_REGISTRY.set(mongoose, {
|
|
743
|
+
tracer,
|
|
744
|
+
config: finalConfig
|
|
745
|
+
});
|
|
746
|
+
patchModelFactory(m, finalConfig);
|
|
747
|
+
const captureStatement = createStatementCapture({
|
|
748
|
+
dbStatementSerializer: resolvedSerializer,
|
|
749
|
+
statementRedactor: resolvedRedactor
|
|
750
|
+
});
|
|
751
|
+
if (m.Schema && finalConfig.instrumentHooks) patchSchemaHooks(m.Schema, tracer, finalConfig);
|
|
752
|
+
const getModelCollectionName = (model) => {
|
|
753
|
+
try {
|
|
754
|
+
return model.collection?.collectionName || model.modelName;
|
|
755
|
+
} catch {
|
|
756
|
+
return;
|
|
757
|
+
}
|
|
758
|
+
};
|
|
759
|
+
for (const { method, operation } of [
|
|
760
|
+
{
|
|
761
|
+
method: "find",
|
|
762
|
+
operation: "find"
|
|
763
|
+
},
|
|
764
|
+
{
|
|
765
|
+
method: "findOne",
|
|
766
|
+
operation: "findOne"
|
|
767
|
+
},
|
|
768
|
+
{
|
|
769
|
+
method: "findById",
|
|
770
|
+
operation: "findById"
|
|
771
|
+
},
|
|
772
|
+
{
|
|
773
|
+
method: "findOneAndUpdate",
|
|
774
|
+
operation: "findOneAndUpdate"
|
|
775
|
+
},
|
|
776
|
+
{
|
|
777
|
+
method: "findOneAndDelete",
|
|
778
|
+
operation: "findOneAndDelete"
|
|
779
|
+
},
|
|
780
|
+
{
|
|
781
|
+
method: "findOneAndReplace",
|
|
782
|
+
operation: "findOneAndReplace"
|
|
783
|
+
},
|
|
784
|
+
{
|
|
785
|
+
method: "deleteOne",
|
|
786
|
+
operation: "deleteOne"
|
|
787
|
+
},
|
|
788
|
+
{
|
|
789
|
+
method: "deleteMany",
|
|
790
|
+
operation: "deleteMany"
|
|
791
|
+
},
|
|
792
|
+
{
|
|
793
|
+
method: "updateOne",
|
|
794
|
+
operation: "updateOne"
|
|
795
|
+
},
|
|
796
|
+
{
|
|
797
|
+
method: "updateMany",
|
|
798
|
+
operation: "updateMany"
|
|
799
|
+
},
|
|
800
|
+
{
|
|
801
|
+
method: "countDocuments",
|
|
802
|
+
operation: "countDocuments"
|
|
803
|
+
},
|
|
804
|
+
{
|
|
805
|
+
method: "estimatedDocumentCount",
|
|
806
|
+
operation: "estimatedDocumentCount"
|
|
807
|
+
}
|
|
808
|
+
]) {
|
|
809
|
+
wrapQueryReturningMethod(m.Model, method, operation, getModelCollectionName, (model) => model.modelName, tracer, finalConfig, captureStatement);
|
|
810
|
+
if (m.Query?.prototype?.[method]) wrapChainableMethod(m.Query.prototype, method);
|
|
811
|
+
}
|
|
812
|
+
for (const method of ["save", "deleteOne"]) if (m.Model.prototype[method]) wrapInstanceMethod(m.Model.prototype, method, method, (doc) => {
|
|
813
|
+
try {
|
|
814
|
+
return doc.constructor?.collection?.collectionName || doc.constructor?.modelName;
|
|
815
|
+
} catch {
|
|
816
|
+
return;
|
|
817
|
+
}
|
|
818
|
+
}, (doc) => {
|
|
819
|
+
try {
|
|
820
|
+
return doc.constructor?.modelName;
|
|
821
|
+
} catch {
|
|
822
|
+
return;
|
|
823
|
+
}
|
|
824
|
+
}, tracer, finalConfig, captureStatement);
|
|
825
|
+
for (const method of [
|
|
826
|
+
"create",
|
|
827
|
+
"insertMany",
|
|
828
|
+
"aggregate",
|
|
829
|
+
"bulkWrite"
|
|
830
|
+
]) if (m.Model[method]) wrapStaticMethod(m.Model, method, method, (model) => {
|
|
831
|
+
try {
|
|
832
|
+
return model.collection?.collectionName;
|
|
833
|
+
} catch {
|
|
834
|
+
return;
|
|
835
|
+
}
|
|
836
|
+
}, (model) => model.modelName, tracer, finalConfig, captureStatement);
|
|
837
|
+
for (const method of [
|
|
838
|
+
"populate",
|
|
839
|
+
"select",
|
|
840
|
+
"lean",
|
|
841
|
+
"where",
|
|
842
|
+
"sort",
|
|
843
|
+
"limit",
|
|
844
|
+
"skip"
|
|
845
|
+
]) if (m.Query?.prototype?.[method]) wrapChainableMethod(m.Query.prototype, method);
|
|
846
|
+
m[INSTRUMENTED_FLAG] = true;
|
|
847
|
+
return mongoose;
|
|
966
848
|
}
|
|
967
849
|
|
|
850
|
+
//#endregion
|
|
968
851
|
exports.instrumentMongoose = instrumentMongoose;
|
|
969
|
-
//# sourceMappingURL=index.cjs.map
|
|
970
852
|
//# sourceMappingURL=index.cjs.map
|