| name | autotel-mongoose |
| description | Use this skill when adding OpenTelemetry tracing to a Mongoose 8+ application — covers instrumentMongoose(), query text capture, automatic PII redaction, and Schema hook instrumentation.
|
autotel-mongoose
OpenTelemetry instrumentation for Mongoose 8+ with automatic db.query.text capture and built-in PII redaction. This package exists because the official @opentelemetry/instrumentation-mongodb has broken ESM+tsx support; use this instead when running Mongoose in an ESM environment.
Setup
npm install autotel-mongoose
import 'autotel/register';
import { init } from 'autotel';
import mongoose from 'mongoose';
import { instrumentMongoose } from 'autotel-mongoose';
init({ service: 'my-app' });
instrumentMongoose(mongoose, {
dbName: 'myapp',
peerName: 'mongo.example.com',
peerPort: 27017,
});
node --import ./src/instrumentation.mts dist/index.js
Core Patterns
Basic instrumentation
import mongoose from 'mongoose';
import { instrumentMongoose } from 'autotel-mongoose';
instrumentMongoose(mongoose);
instrumentMongoose(mongoose, {
dbName: 'myapp',
peerName: 'localhost',
peerPort: 27017,
});
Configuration reference (InstrumentMongooseConfig)
interface InstrumentMongooseConfig {
dbName?: string;
peerName?: string;
peerPort?: number;
tracerName?: string;
captureCollectionName?: boolean;
instrumentHooks?: boolean;
dbStatementSerializer?:
| ((operation: string, payload: SerializerPayload) => string | undefined)
| false;
statementRedactor?:
| AttributeRedactorPreset
| AttributeRedactorConfig
| false;
}
Span attributes
| Attribute | Condition |
|---|
db.system.name | Always (mongodb) |
db.operation.name | Always (e.g., find, insertMany) |
db.collection.name | When captureCollectionName: true (default) |
db.namespace | When dbName is set |
db.query.text | When statement serialization is enabled (default: JSON payload) |
server.address | When peerName is set |
server.port | When peerPort is set |
Span names follow <operation> <collectionName> (e.g., find users) or fall back to mongoose.<operation>.
What is instrumented
Query-returning Model methods (spans created; exec() wrapped for finalization):
find, findOne, findById, findOneAndUpdate, findOneAndDelete, findOneAndReplace, deleteOne, deleteMany, updateOne, updateMany, countDocuments, estimatedDocumentCount
Model static methods (spans created; promise finalization):
create, insertMany, aggregate, bulkWrite
Model instance methods (spans created; promise finalization):
save, deleteOne (on prototype)
Chainable Query methods (context propagation only, no span):
populate, select, lean, where, sort, limit, skip
Schema hooks (opt-in via instrumentHooks: true):
User-defined pre and post hooks are wrapped. Internal Mongoose hooks are skipped automatically.
Statement capture and redaction
By default, query payloads are serialized to JSON and set as db.query.text, with the 'default' redactor applied (strips emails, phone numbers, SSNs, credit cards).
instrumentMongoose(mongoose, {
dbStatementSerializer: (operation, payload) => {
if (operation === 'find') {
return JSON.stringify({ filter: payload.condition });
}
return undefined;
},
});
instrumentMongoose(mongoose, {
dbStatementSerializer: false,
});
instrumentMongoose(mongoose, {
statementRedactor: false,
});
SerializerPayload shape
interface SerializerPayload {
condition?: Record<string, unknown>;
updates?: Record<string, unknown>;
options?: Record<string, unknown>;
fields?: Record<string, unknown>;
aggregatePipeline?: unknown[];
document?: unknown;
documents?: unknown[];
operations?: unknown[];
}
Enabling Schema hook instrumentation
instrumentMongoose(mongoose, {
instrumentHooks: true,
});
const userSchema = new mongoose.Schema({ name: String });
userSchema.pre('save', async function () {
});
Hook spans use SpanKind.INTERNAL and carry hook.type, hook.operation, hook.model, db.system.name, and optionally db.collection.name.
Internal Mongoose hooks (those starting with _ or $, or containing known internal patterns like this.$__) are automatically skipped.
Idempotency
instrumentMongoose is idempotent. Multiple calls on the same mongoose instance are safe; the __autotelMongooseInstrumented flag prevents double-patching.
Common Mistakes
HIGH — Calling instrumentMongoose after defining schemas
const User = mongoose.model('User', new mongoose.Schema({ name: String }));
instrumentMongoose(mongoose);
instrumentMongoose(mongoose, { dbName: 'myapp' });
const User = mongoose.model('User', new mongoose.Schema({ name: String }));
Hook wrapping (instrumentHooks: true) only applies to pre/post calls made after instrumentMongoose runs. Model method patching affects mongoose.Model globally and works regardless, but hook instrumentation is order-sensitive.
HIGH — Disabling redaction when query text contains PII
instrumentMongoose(mongoose, {
statementRedactor: false,
});
instrumentMongoose(mongoose, {
dbStatementSerializer: (operation, payload) => {
return JSON.stringify({ op: operation });
},
});
The default 'default' redactor covers common PII patterns (email, phone, SSN, credit card) but is not exhaustive. Review your query payloads before disabling redaction.
MEDIUM — Using instrumentHooks: false (default) and expecting hook spans
instrumentMongoose(mongoose);
userSchema.pre('save', async function () {
});
instrumentMongoose(mongoose, { instrumentHooks: true });
Hook instrumentation is off by default to avoid unexpected performance overhead and to avoid wrapping Mongoose internals by accident.
MEDIUM — Using deprecated semconv attribute names
autotel-mongoose uses stable OTel semconv:
'db.statement';
'db.system';
'net.peer.name';
'net.peer.port';
The package exports stable constants from autotel-mongoose/constants if needed for custom attribute access.
MEDIUM — Expecting spans for .find() without calling .exec()
const query = User.find({ active: true });
const users = await query;
const users = await User.find({ active: true }).exec();
The instrumentation wraps exec() to finalize the span. Implicit execution (Mongoose auto-calling exec() when you await a Query) should also work, but explicit .exec() is more reliable and clearer.
Version
Targets autotel-mongoose v0.0.2 with mongoose >= 8.0.0 (peer dep) and autotel (peer dep). Uses stable OTel semconv only (db.query.text, db.operation.name, db.system.name, db.collection.name, db.namespace, server.address, server.port).