Overview
@kubiks/otel-mongodb provides comprehensive OpenTelemetry instrumentation for the MongoDB Node.js driver . Capture spans for all database operations with detailed metadata about collections, queries, and execution metrics.
Visualize your MongoDB operations with detailed span information including collection names, operation types, and execution metrics.
Installation
npm install @kubiks/otel-mongodb
Peer Dependencies: @opentelemetry/api >= 1.9.0, mongodb >= 5.0.0
Quick Start
import { MongoClient } from "mongodb" ;
import { instrumentMongoClient } from "@kubiks/otel-mongodb" ;
const client = new MongoClient ( process . env . MONGODB_URI ! );
await client . connect ();
instrumentMongoClient ( client , {
captureFilters: true ,
peerName: "mongodb.example.com" ,
peerPort: 27017 ,
});
const db = client . db ( "myapp" );
const users = db . collection ( "users" );
const user = await users . findOne ({ email: "user@example.com" });
instrumentMongoClient wraps the client you already use—no configuration changes needed. Every database operation creates a client span with useful attributes.
What Gets Traced
This instrumentation automatically traces all major MongoDB operations including:
Find Operations find, findOne
Insert Operations insertOne, insertMany
Update Operations updateOne, updateMany, findOneAndUpdate
Delete Operations deleteOne, deleteMany, findOneAndDelete
Count Operations countDocuments
Configuration
With Filter Capture
instrumentMongoClient ( client , {
captureFilters: true , // Capture query filters (default: false)
peerName: "mongodb.example.com" ,
peerPort: 27017 ,
});
Filter capture is disabled by default to protect sensitive data. Only enable in secure, development environments or ensure filters don’t contain sensitive information.
Span Attributes
Each span includes rich metadata about the database operation following OpenTelemetry semantic conventions:
Attribute Description Example
db.systemConstant value mongodb mongodbdb.operationMongoDB operation type findOne, insertManydb.mongodb.collectionCollection name usersdb.nameDatabase name myappnet.peer.nameMongoDB server hostname mongodb.example.comnet.peer.portMongoDB server port 27017mongodb.filterQuery filter (when enabled) {"status":"active"}mongodb.result_countNumber of documents returned 42mongodb.inserted_countNumber of documents inserted 5mongodb.matched_countNumber of documents matched (updates) 10mongodb.modified_countNumber of documents modified 8mongodb.deleted_countNumber of documents deleted 15mongodb.execution_time_msQuery execution time (when enabled) 42.5mongodb.pipelineAggregation pipeline [{"$match":...}]
The instrumentation captures query metadata to help with debugging and monitoring, while optionally capturing filters based on your security requirements.
Usage Examples
Basic Find Operations
Find One
Find Many
With Projection
import { users } from "@/lib/mongodb" ;
const user = await users . findOne ({ email: "user@example.com" });
// Traced with:
// - db.operation: "findOne"
// - db.mongodb.collection: "users"
// - mongodb.result_count: 1
Insert Operations
const result = await users . insertOne ({
name: "John Doe" ,
email: "john@example.com" ,
status: "active" ,
});
// Traced with:
// - db.operation: "insertOne"
// - mongodb.inserted_count: 1
Update Operations
Update One
Update Many
Find and Update
const result = await users . updateOne (
{ email: "user@example.com" },
{ $set: { status: "inactive" } }
);
// Traced with:
// - db.operation: "updateOne"
// - mongodb.matched_count: 1
// - mongodb.modified_count: 1
Delete Operations
const result = await users . deleteOne ({ email: "user@example.com" });
// Traced with:
// - db.operation: "deleteOne"
// - mongodb.deleted_count: 1
Aggregation Pipeline
const pipeline = [
{ $match: { status: "active" } },
{ $group: { _id: "$country" , count: { $sum: 1 } } },
{ $sort: { count: - 1 } },
{ $limit: 10 },
];
const results = await users . aggregate ( pipeline ). toArray ();
// Traced with:
// - db.operation: "aggregate"
// - mongodb.pipeline: [{"$match":...},{"$group":...}]
// - mongodb.result_count: 10
Count Operations
Count Documents
Estimated Count
const count = await users . countDocuments ({ status: "active" });
// Traced with:
// - db.operation: "countDocuments"
// - mongodb.result_count: 42
Complete Integration Example
Here’s a complete example of MongoDB with OpenTelemetry in a Next.js application:
Setup
import { MongoClient } from "mongodb" ;
import { instrumentMongoClient } from "@kubiks/otel-mongodb" ;
if ( ! process . env . MONGODB_URI ) {
throw new Error ( "MONGODB_URI environment variable is not set" );
}
const client = new MongoClient ( process . env . MONGODB_URI );
let clientPromise : Promise < MongoClient >;
if ( process . env . NODE_ENV === "development" ) {
// In development, use a global variable to preserve the connection
let globalWithMongo = global as typeof globalThis & {
_mongoClientPromise ?: Promise < MongoClient >;
};
if ( ! globalWithMongo . _mongoClientPromise ) {
clientPromise = client . connect ();
instrumentMongoClient ( client , {
captureFilters: true ,
peerName: new URL ( process . env . MONGODB_URI ). hostname ,
peerPort: parseInt ( new URL ( process . env . MONGODB_URI ). port || "27017" ),
});
globalWithMongo . _mongoClientPromise = clientPromise ;
} else {
clientPromise = globalWithMongo . _mongoClientPromise ;
}
} else {
// In production, create a new connection
clientPromise = client . connect ();
instrumentMongoClient ( client , {
captureFilters: false , // Disable in production
peerName: new URL ( process . env . MONGODB_URI ). hostname ,
peerPort: parseInt ( new URL ( process . env . MONGODB_URI ). port || "27017" ),
});
}
export default clientPromise ;
export async function getDatabase () {
const client = await clientPromise ;
return client . db ( "myapp" );
}
export async function getCollection < T = any >( name : string ) {
const db = await getDatabase ();
return db . collection < T >( name );
}
Usage in Server Actions
"use server" ;
import { getCollection } from "@/lib/mongodb" ;
import { ObjectId } from "mongodb" ;
export async function getUser ( userId : string ) {
const users = await getCollection ( "users" );
return await users . findOne ({ _id: new ObjectId ( userId ) });
}
export async function createUser ( data : { name : string ; email : string }) {
const users = await getCollection ( "users" );
const result = await users . insertOne ({
... data ,
createdAt: new Date (),
status: "active" ,
});
return { id: result . insertedId . toString () };
}
export async function updateUser ( userId : string , data : Partial <{ name : string ; email : string }>) {
const users = await getCollection ( "users" );
const result = await users . updateOne (
{ _id: new ObjectId ( userId ) },
{ $set: { ... data , updatedAt: new Date () } }
);
return { success: result . modifiedCount > 0 };
}
export async function deleteUser ( userId : string ) {
const users = await getCollection ( "users" );
const result = await users . deleteOne ({ _id: new ObjectId ( userId ) });
return { success: result . deletedCount > 0 };
}
export async function getActiveUsers () {
const users = await getCollection ( "users" );
return await users . find ({ status: "active" }). toArray ();
}
Usage in API Routes
import { NextResponse } from "next/server" ;
import { getCollection } from "@/lib/mongodb" ;
export async function GET () {
const users = await getCollection ( "users" );
const stats = await users . aggregate ([
{
$group: {
_id: "$status" ,
count: { $sum: 1 },
},
},
{
$project: {
status: "$_id" ,
count: 1 ,
_id: 0 ,
},
},
]). toArray ();
return NextResponse . json ({ stats });
}
Best Practices
Always reuse the MongoDB client connection rather than creating new connections: // Good: Reuse client
const client = await clientPromise ;
// Bad: Create new client each time
const client = new MongoClient ( uri );
await client . connect ();
Limit Filter Capture in Production
Only enable filter capture in development or when filters don’t contain sensitive data: instrumentMongoClient ( client , {
captureFilters: process . env . NODE_ENV === "development" ,
});
Use Indexes for Performance
Always handle MongoDB errors in your application: try {
const user = await users . findOne ({ email });
if ( ! user ) {
throw new Error ( "User not found" );
}
return user ;
} catch ( error ) {
console . error ( "MongoDB error:" , error );
throw error ;
}
Troubleshooting
Ensure OpenTelemetry is initialized before connecting to MongoDB: import { NodeSDK } from "@opentelemetry/sdk-node" ;
const sdk = new NodeSDK ({
// ... configuration
});
sdk . start ();
// Then connect to MongoDB
const client = await clientPromise ;
Make sure your MongoDB URI is correct and the server is accessible: MONGODB_URI = mongodb://localhost:27017/myapp
# or for MongoDB Atlas:
MONGODB_URI = mongodb+srv://username:password@cluster.mongodb.net/myapp
Ensure you’ve configured peer information: instrumentMongoClient ( client , {
peerName: "mongodb.example.com" ,
peerPort: 27017 ,
});
Resources
License
MIT