Tokuro.OpenTelemetry.Instrumentation.MongoDB 0.1.0

dotnet add package Tokuro.OpenTelemetry.Instrumentation.MongoDB --version 0.1.0
                    
NuGet\Install-Package Tokuro.OpenTelemetry.Instrumentation.MongoDB -Version 0.1.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Tokuro.OpenTelemetry.Instrumentation.MongoDB" Version="0.1.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Tokuro.OpenTelemetry.Instrumentation.MongoDB" Version="0.1.0" />
                    
Directory.Packages.props
<PackageReference Include="Tokuro.OpenTelemetry.Instrumentation.MongoDB" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Tokuro.OpenTelemetry.Instrumentation.MongoDB --version 0.1.0
                    
#r "nuget: Tokuro.OpenTelemetry.Instrumentation.MongoDB, 0.1.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Tokuro.OpenTelemetry.Instrumentation.MongoDB@0.1.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Tokuro.OpenTelemetry.Instrumentation.MongoDB&version=0.1.0
                    
Install as a Cake Addin
#tool nuget:?package=Tokuro.OpenTelemetry.Instrumentation.MongoDB&version=0.1.0
                    
Install as a Cake Tool

Tokuro.OpenTelemetry.Instrumentation.MongoDB

OpenTelemetry instrumentation for the official MongoDB.Driver (3.x). PII-safe by default, dual-writes legacy and stable OTel DB semantic conventions, and bounds its in-flight activity map so a leaky cursor cannot exhaust process memory.

NuGet Downloads Build License: Apache-2.0 .NET

Quick example

Two calls are required: one on the OpenTelemetry tracer provider, one on the MongoClientSettings that backs each MongoClient. The first tells OpenTelemetry to listen; the second tells MongoDB to emit. Both are required — registering only the tracer side produces zero spans.

1. Register the ActivitySource on your tracer provider so the OpenTelemetry SDK listens for the instrumentation's spans:

using OpenTelemetry.Trace;

builder.Services.AddOpenTelemetry()
    .WithTracing(t => t
        .AddMongoDBInstrumentation()
        .AddOtlpExporter());

2. Wire the subscriber on each MongoClientSettings so the MongoDB driver actually publishes command events. This is where all instrumentation configuration lives:

var settings = MongoClientSettings.FromConnectionString(connectionString);
settings.AddOpenTelemetryInstrumentation(options =>
{
    options.CaptureCommandText       = true;
    options.SuppressExceptionMessage = true;
    options.MaxCommandTextLength     = 4_000;
    options.MaxInFlightCommands      = 10_000;
    options.EmitLegacyAttributes     = true;
    options.EmitStableAttributes     = true;
    options.FilterCommand            = e => e.CommandName != "ping";
});

var client = new MongoClient(settings);

Every command issued by that MongoClient now emits a span with redacted BSON, db.system.name = "mongodb", db.namespace, server.address, and server.port. No connection strings, no document payloads, no exception bodies.

Heads-up. If your service constructs MongoClient from DI, you need to plumb the instrumented MongoClientSettings through your DI registration. See the sample at samples/Sample.AspNetCore/Program.cs.

Why this library

Production-tested gap-fill over MongoDB.Driver.Core.Extensions.DiagnosticSources v3.0.0:

  • PII-safe redaction by default. The BSON tree is walked; every scalar becomes "?". Collection names, operators, and structure are preserved. Raw capture is opt-in, not opt-out.
  • exception.message suppressed on failed commands. MongoDB driver exceptions echo BSON fragments (E11000 duplicate-key errors carry the conflicting document, schema validation surfaces the rejected payload, bulk-write rejects include rows). Off by default.
  • Dual-write OTel DB semantic conventions. Emits both legacy (db.system, db.statement, db.name) and stable (db.system.name, db.query.text, db.namespace) attributes during the OTel sem-conv migration window.
  • Bounded in-flight Activity map. Struct dictionary key, configurable cap with drop-new overflow. Upstream uses an unbounded ConcurrentDictionary<int, Activity> — a leaky long-running cursor or a flood of unmatched getMores grows the heap forever.

Installation

dotnet add package Tokuro.OpenTelemetry.Instrumentation.MongoDB

Usage

Configuration via object initializer

If you prefer not to use the configure callback, the same options surface accepts an instance:

var settings = MongoClientSettings.FromConnectionString(connectionString);
settings.AddOpenTelemetryInstrumentation(new MongoDBInstrumentationOptions
{
    SuppressExceptionMessage = true,
    MaxCommandTextLength     = 2_000,
    FilterCommand            = e => e.CommandName != "ping",
});

Backend interop

Spans are vendor-neutral OTel. Shipping to Datadog via the DD Agent or DDOT, the agent's DB service-splitting automatically surfaces these spans under service:<svc>-mongodb thanks to the db.system.name attribute. Tempo, Honeycomb, Jaeger, New Relic, and Grafana Cloud OTLP all consume the same payload without backend-specific configuration.

Configuration

All options live on MongoDBInstrumentationOptions, passed to MongoClientSettings.AddOpenTelemetryInstrumentation.

Option Default Meaning
CaptureCommandText true Emit db.statement / db.query.text with the redacted BSON. Values are always redacted regardless; this flag only controls whether the attribute is attached at all.
MaxCommandTextLength 4000 Truncate redacted command text beyond this many chars; suffix ...[truncated].
SuppressExceptionMessage true Omit the exception span event (which would carry exception.type + exception.message) on failed commands; the error.type span attribute is always kept.
MaxInFlightCommands 10000 Defensive upper bound on the in-flight Activity map; on overflow the new command's activity is dropped (stopped, not tracked) while existing in-flight entries are preserved. Each drop emits an EventSource counter.
EmitLegacyAttributes true Emit pre-stable attrs (db.system, db.statement, db.name).
EmitStableAttributes true Emit stable attrs (db.system.name, db.query.text, db.namespace, db.operation.name).
FilterCommand null Optional predicate over CommandStartedEvent. Return false to suppress the span entirely. When null, only the built-in handshake/heartbeat list is filtered.

Features comparison

Capability This library jbogard/MongoDB.Driver.Core.Extensions.DiagnosticSources v3.0.0
BSON redaction default On (scalars → "?") Off — raw command captured
Opt-in raw capture Yes (CaptureCommandText + explicit unredacted toggle) Always-on raw capture when enabled
exception.message handling Suppressed by default Recorded raw (leaks BSON fragments)
OTel legacy attributes Yes Yes
OTel stable attributes (db.system.name …) Yes No
server.address / server.port Yes, low-64-bit-safe IP handling Partial
Bounded in-flight map Yes (MaxInFlightCommands) No — unbounded ConcurrentDictionary<int, Activity>
Struct dictionary key Yes (no boxing) No
TFMs net8.0;net9.0;net10.0 net6.0;net8.0

How it works

The instrumentation subscribes to the MongoDB driver's command-monitoring events via a ClusterConfigurator. On CommandStartedEvent, an Activity is started (cheap when no listener is registered) and stashed in a bounded map keyed by request id. On CommandSucceededEvent / CommandFailedEvent, the activity is retrieved, tagged, and stopped.

flowchart LR
    A[CommandStartedEvent] --> B[ActivitySource.StartActivity]
    B --> C{IsAllDataRequested?}
    C -- no  --> D[Stash in bounded map]
    C -- yes --> E[Walk BSON tree, scalars to '?']
    E --> F[SetTag db.namespace, server.address, ...]
    F --> D
    D --> G{Succeeded or Failed?}
    G -- Succeeded --> H[SetStatus Ok, Stop]
    G -- Failed    --> I[SetStatus Error, error.type, optional exception event, Stop]

The hot path is gated on Activity.IsAllDataRequested. Sampled-out commands incur only a dictionary insert + remove and one Activity allocation that the runtime can elide under tiered JIT.

Caveats & known limitations

  • Aggregation pipelines are redacted structurally only. Stage operators ($match, $group, …) are kept; user-supplied values are not. The shape of a pipeline is sometimes itself sensitive — review before enabling CaptureCommandText in shared-tenancy databases.
  • getMore cursor continuation is not correlated to the originating find. The driver does not surface a parent request id. Each getMore is its own root span by design; correlate via cursor id if needed.
  • Bounded map overflow is a signal. When the in-flight map reaches MaxInFlightCommands, the instrumentation drops the new command's activity (stops it without tracking) and preserves the existing in-flight entries, emitting an EventSource counter on each drop. A non-zero overflow count means either a misbehaving consumer (unmatched request ids) or a real leak.

Performance

The hot path is gated on Activity.IsAllDataRequested. BenchmarkDotNet results live in bench/. The contract is: no allocation regression on the steady-state sampled-out path versus a no-op subscriber, measured on net8.0 and net10.0 with the tiered server GC.

Compatibility

  • MongoDB.Driver >= 3.0.0, < 4.0.0
  • .NET 8, .NET 9, .NET 10
  • OpenTelemetry SDK 1.9+

Contributing

See CONTRIBUTING.md.

Security

See SECURITY.md. Do not file security issues publicly.

License

Apache-2.0. See LICENSE.

Acknowledgements

Prior art: Jimmy Bogard's MongoDB.Driver.Core.Extensions.DiagnosticSources. This library exists to cover gaps that surfaced during a production audit of that package — chiefly PII redaction defaults, exception-message suppression, dual sem-conv emission, and a bounded in-flight map. The shape of the public API is intentionally close to make migration mechanical; see docs/migration-from-jbogard.md.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.1.0 53 5/29/2026