diff --git a/.generated.NoMobile.slnx b/.generated.NoMobile.slnx
index cf655a4335..bbf67bf776 100644
--- a/.generated.NoMobile.slnx
+++ b/.generated.NoMobile.slnx
@@ -114,6 +114,7 @@
+
diff --git a/Sentry-CI-Build-Linux-NoMobile.slnf b/Sentry-CI-Build-Linux-NoMobile.slnf
index dd3b365807..c8f5aecc53 100644
--- a/Sentry-CI-Build-Linux-NoMobile.slnf
+++ b/Sentry-CI-Build-Linux-NoMobile.slnf
@@ -31,6 +31,7 @@
"samples\\Sentry.Samples.OpenTelemetry.AspNetCore\\Sentry.Samples.OpenTelemetry.AspNetCore.csproj",
"samples\\Sentry.Samples.OpenTelemetry.AzureFunctions\\Sentry.Samples.OpenTelemetry.AzureFunctions.csproj",
"samples\\Sentry.Samples.OpenTelemetry.Console\\Sentry.Samples.OpenTelemetry.Console.csproj",
+ "samples\\Sentry.Samples.OpenTelemetry.MongoDB\\Sentry.Samples.OpenTelemetry.MongoDB.csproj",
"samples\\Sentry.Samples.Serilog\\Sentry.Samples.Serilog.csproj",
"src\\Sentry.Analyzers\\Sentry.Analyzers.csproj",
"src\\Sentry.AspNetCore.Blazor.WebAssembly\\Sentry.AspNetCore.Blazor.WebAssembly.csproj",
diff --git a/Sentry-CI-Build-Linux.slnf b/Sentry-CI-Build-Linux.slnf
index c22e97061b..3025600da5 100644
--- a/Sentry-CI-Build-Linux.slnf
+++ b/Sentry-CI-Build-Linux.slnf
@@ -33,6 +33,7 @@
"samples\\Sentry.Samples.OpenTelemetry.AspNetCore\\Sentry.Samples.OpenTelemetry.AspNetCore.csproj",
"samples\\Sentry.Samples.OpenTelemetry.AzureFunctions\\Sentry.Samples.OpenTelemetry.AzureFunctions.csproj",
"samples\\Sentry.Samples.OpenTelemetry.Console\\Sentry.Samples.OpenTelemetry.Console.csproj",
+ "samples\\Sentry.Samples.OpenTelemetry.MongoDB\\Sentry.Samples.OpenTelemetry.MongoDB.csproj",
"samples\\Sentry.Samples.Serilog\\Sentry.Samples.Serilog.csproj",
"src\\Sentry.Analyzers\\Sentry.Analyzers.csproj",
"src\\Sentry.Android.AssemblyReader\\Sentry.Android.AssemblyReader.csproj",
diff --git a/Sentry-CI-Build-Windows-arm64.slnf b/Sentry-CI-Build-Windows-arm64.slnf
index 10ce77aa88..b2ecf8df01 100644
--- a/Sentry-CI-Build-Windows-arm64.slnf
+++ b/Sentry-CI-Build-Windows-arm64.slnf
@@ -34,6 +34,7 @@
"samples\\Sentry.Samples.OpenTelemetry.AspNetCore\\Sentry.Samples.OpenTelemetry.AspNetCore.csproj",
"samples\\Sentry.Samples.OpenTelemetry.AzureFunctions\\Sentry.Samples.OpenTelemetry.AzureFunctions.csproj",
"samples\\Sentry.Samples.OpenTelemetry.Console\\Sentry.Samples.OpenTelemetry.Console.csproj",
+ "samples\\Sentry.Samples.OpenTelemetry.MongoDB\\Sentry.Samples.OpenTelemetry.MongoDB.csproj",
"samples\\Sentry.Samples.Serilog\\Sentry.Samples.Serilog.csproj",
"src\\Sentry.Analyzers\\Sentry.Analyzers.csproj",
"src\\Sentry.Android.AssemblyReader\\Sentry.Android.AssemblyReader.csproj",
diff --git a/Sentry-CI-Build-Windows.slnf b/Sentry-CI-Build-Windows.slnf
index 0b8219a53e..528d7817dd 100644
--- a/Sentry-CI-Build-Windows.slnf
+++ b/Sentry-CI-Build-Windows.slnf
@@ -34,6 +34,7 @@
"samples\\Sentry.Samples.OpenTelemetry.AspNetCore\\Sentry.Samples.OpenTelemetry.AspNetCore.csproj",
"samples\\Sentry.Samples.OpenTelemetry.AzureFunctions\\Sentry.Samples.OpenTelemetry.AzureFunctions.csproj",
"samples\\Sentry.Samples.OpenTelemetry.Console\\Sentry.Samples.OpenTelemetry.Console.csproj",
+ "samples\\Sentry.Samples.OpenTelemetry.MongoDB\\Sentry.Samples.OpenTelemetry.MongoDB.csproj",
"samples\\Sentry.Samples.Serilog\\Sentry.Samples.Serilog.csproj",
"src\\Sentry.Analyzers\\Sentry.Analyzers.csproj",
"src\\Sentry.Android.AssemblyReader\\Sentry.Android.AssemblyReader.csproj",
diff --git a/Sentry-CI-Build-macOS.slnf b/Sentry-CI-Build-macOS.slnf
index 4b21b127b3..f8d2b3ca69 100644
--- a/Sentry-CI-Build-macOS.slnf
+++ b/Sentry-CI-Build-macOS.slnf
@@ -38,6 +38,7 @@
"samples\\Sentry.Samples.OpenTelemetry.AspNetCore\\Sentry.Samples.OpenTelemetry.AspNetCore.csproj",
"samples\\Sentry.Samples.OpenTelemetry.AzureFunctions\\Sentry.Samples.OpenTelemetry.AzureFunctions.csproj",
"samples\\Sentry.Samples.OpenTelemetry.Console\\Sentry.Samples.OpenTelemetry.Console.csproj",
+ "samples\\Sentry.Samples.OpenTelemetry.MongoDB\\Sentry.Samples.OpenTelemetry.MongoDB.csproj",
"samples\\Sentry.Samples.Serilog\\Sentry.Samples.Serilog.csproj",
"src\\Sentry.Analyzers\\Sentry.Analyzers.csproj",
"src\\Sentry.Android.AssemblyReader\\Sentry.Android.AssemblyReader.csproj",
diff --git a/Sentry.slnx b/Sentry.slnx
index cf655a4335..bbf67bf776 100644
--- a/Sentry.slnx
+++ b/Sentry.slnx
@@ -114,6 +114,7 @@
+
diff --git a/SentryNoMobile.slnf b/SentryNoMobile.slnf
index 3917453156..a6ef473815 100644
--- a/SentryNoMobile.slnf
+++ b/SentryNoMobile.slnf
@@ -32,6 +32,7 @@
"samples\\Sentry.Samples.OpenTelemetry.AspNetCore\\Sentry.Samples.OpenTelemetry.AspNetCore.csproj",
"samples\\Sentry.Samples.OpenTelemetry.AzureFunctions\\Sentry.Samples.OpenTelemetry.AzureFunctions.csproj",
"samples\\Sentry.Samples.OpenTelemetry.Console\\Sentry.Samples.OpenTelemetry.Console.csproj",
+ "samples\\Sentry.Samples.OpenTelemetry.MongoDB\\Sentry.Samples.OpenTelemetry.MongoDB.csproj",
"samples\\Sentry.Samples.Serilog\\Sentry.Samples.Serilog.csproj",
"src\\Sentry.Analyzers\\Sentry.Analyzers.csproj",
"src\\Sentry.AspNet\\Sentry.AspNet.csproj",
diff --git a/samples/Sentry.Samples.OpenTelemetry.MongoDB/Program.cs b/samples/Sentry.Samples.OpenTelemetry.MongoDB/Program.cs
new file mode 100644
index 0000000000..08ddf78727
--- /dev/null
+++ b/samples/Sentry.Samples.OpenTelemetry.MongoDB/Program.cs
@@ -0,0 +1,98 @@
+/*
+ * This sample demonstrates how MongoDB queries can be captured as Sentry spans using the
+ * MongoDB.Driver's built-in OpenTelemetry instrumentation (available since driver version 3.7.0).
+ *
+ * The sample requires a MongoDB server. See the README.md alongside this sample for instructions
+ * on how to start one using Docker.
+ */
+
+using System.Diagnostics;
+using MongoDB.Bson;
+using MongoDB.Driver;
+using MongoDB.Driver.Core.Configuration;
+using OpenTelemetry;
+using OpenTelemetry.Trace;
+using Sentry.OpenTelemetry;
+using Sentry.OpenTelemetry.Exporter;
+
+#if SENTRY_DSN_DEFINED_IN_ENV
+var dsn = Environment.GetEnvironmentVariable("SENTRY_DSN")
+ ?? throw new InvalidOperationException("SENTRY_DSN environment variable is not set");
+#else
+// A DSN is required. You can set here in code, or you can set it in the SENTRY_DSN environment variable.
+// See https://docs.sentry.io/product/sentry-basics/dsn-explainer/
+var dsn = SamplesShared.Dsn;
+#endif
+
+var activitySource = new ActivitySource("Sentry.Samples.OpenTelemetry.MongoDB");
+
+SentrySdk.Init(options =>
+{
+ options.Dsn = dsn;
+ options.Debug = true;
+ options.TracesSampleRate = 1.0;
+ options.UseOtlp(); // <-- Configure Sentry to use OpenTelemetry trace information
+});
+
+using var tracerProvider = Sdk.CreateTracerProviderBuilder()
+ .AddSource(activitySource.Name)
+ .AddSource(MongoTelemetry.ActivitySourceName) // <-- Subscribe to the MongoDB driver's built-in instrumentation
+ .AddProcessor(new Sentry.Samples.OpenTelemetry.MongoDB.RedactSensitiveMongoData()) // <-- Redact PII from query text BEFORE it's exported (see below)
+ .AddSentryOtlpExporter(dsn) // <-- Configure OpenTelemetry to send traces to Sentry over OTLP
+ .Build();
+
+// MongoDB.Driver 3.7.0+ creates OpenTelemetry activities for every command out of the box.
+// TracingOptions is only needed to tweak that behaviour - here we opt in to capturing the query
+// text on each span (off by default), which shows up in Sentry as the `db.query.text` span data.
+// NOTE: The captured query text includes the actual field values sent to MongoDB, which may contain
+// PII. See the RedactSensitiveMongoData span processor at the bottom of this file for how to scrub it.
+var mongoUri = Environment.GetEnvironmentVariable("MONGODB_URI") ?? "mongodb://localhost:27017";
+var clientSettings = MongoClientSettings.FromConnectionString(mongoUri);
+clientSettings.ServerSelectionTimeout = TimeSpan.FromSeconds(3); // <-- Fail fast if MongoDB isn't running
+clientSettings.TracingOptions = new TracingOptions
+{
+ QueryTextMaxLength = 4096
+};
+var mongoClient = new MongoClient(clientSettings);
+
+var db = mongoClient.GetDatabase("sentry_mongo_sample");
+var fruit = db.GetCollection("fruit");
+
+try
+{
+ // Everything within this activity gets sent to Sentry as a single transaction, containing one
+ // span for each MongoDB command.
+ using (activitySource.StartActivity("Fruit Salad"))
+ {
+ // Each recipe records the name of the person who contributed it. That's PII, and because we
+ // capture query text (above) it would otherwise be sent to Sentry verbatim in the insert
+ // command - the RedactSensitiveMongoData processor strips it out before export.
+ await fruit.InsertManyAsync([
+ new BsonDocument { { "name", "Apple" }, { "color", "Red" }, { "contributor", "Alice Johnson" } },
+ new BsonDocument { { "name", "Banana" }, { "color", "Yellow" }, { "contributor", "Bob Nguyen" } },
+ new BsonDocument { { "name", "Cherry" }, { "color", "Red" }, { "contributor", "Carla Méndez" } },
+ new BsonDocument { { "name", "Kiwi" }, { "color", "Green" }, { "contributor", "David O'Brien" } }
+ ]);
+
+ var redFruit = await fruit.Find(Builders.Filter.Eq("color", "Red")).ToListAsync();
+ Console.WriteLine($"Found {redFruit.Count} red fruit: " +
+ string.Join(", ", redFruit.Select(f => f["name"].AsString)));
+
+ await fruit.UpdateOneAsync(
+ Builders.Filter.Eq("name", "Apple"),
+ Builders.Update.Set("color", "Green"));
+
+ var count = await fruit.CountDocumentsAsync(Builders.Filter.Eq("color", "Green"));
+ Console.WriteLine($"There are now {count} green fruit.");
+
+ // Drop the database so the sample can be run repeatedly
+ await mongoClient.DropDatabaseAsync(db.DatabaseNamespace.DatabaseName);
+ }
+}
+catch (TimeoutException)
+{
+ Console.WriteLine($"""
+ Could not connect to MongoDB at {mongoUri}
+ Is MongoDB running? See the README.md for this sample for instructions on starting MongoDB using Docker.
+ """);
+}
diff --git a/samples/Sentry.Samples.OpenTelemetry.MongoDB/README.md b/samples/Sentry.Samples.OpenTelemetry.MongoDB/README.md
new file mode 100644
index 0000000000..2cebac7ef4
--- /dev/null
+++ b/samples/Sentry.Samples.OpenTelemetry.MongoDB/README.md
@@ -0,0 +1,92 @@
+# Sentry.Samples.OpenTelemetry.MongoDB
+
+This sample demonstrates how MongoDB queries can be captured as Sentry spans using OpenTelemetry.
+
+[MongoDB.Driver](https://www.nuget.org/packages/MongoDB.Driver) 3.7.0 and later has
+[built-in OpenTelemetry instrumentation](https://www.mongodb.com/docs/drivers/csharp/current/logging-and-monitoring/)
+— no additional instrumentation package is required. The driver emits one activity per MongoDB
+command from an `ActivitySource` named `MongoDB.Driver`, which this sample subscribes to and
+exports to Sentry:
+
+```csharp
+using var tracerProvider = Sdk.CreateTracerProviderBuilder()
+ .AddSource(MongoTelemetry.ActivitySourceName) // "MongoDB.Driver"
+ .AddSentryOtlpExporter(dsn)
+ .Build();
+```
+
+By default the driver does not record the query text on spans. This sample opts in via
+`TracingOptions` on the client settings, so each span includes the (truncated) command as
+`db.query.text`:
+
+```csharp
+clientSettings.TracingOptions = new TracingOptions
+{
+ QueryTextMaxLength = 4096
+};
+```
+
+## A note on PII
+
+When you capture query text, the MongoDB driver records the **actual field values** sent in each
+command (for example `{ "name": "Apple", "contributor": "Alice Johnson" }`) in the `db.query.text`
+span attribute. If those values can contain PII, you need to scrub them.
+
+Importantly, the Sentry OTLP exporter used by this sample sends spans **straight to Sentry**, without
+running them through the SDK's `BeforeSend`/`BeforeSendTransaction` hooks — so you can't rely on those
+to redact data here.
+
+Your options are:
+- **Don't capture query text at all** — leave `QueryTextMaxLength` at its default of `0`. The Queries
+ module still works using the operation and collection name.
+- **Redact client-side with an OpenTelemetry span processor** — registered *before*
+ `AddSentryOtlpExporter` so it runs before spans are exported. This sample includes an example
+ `RedactSensitiveMongoData`, which strips the `contributor` field out of `db.query.text`
+ before it leaves the process. Adapt the field names to your own data.
+- **Configure [server-side data scrubbing](https://docs.sentry.io/security-legal-pii/scrubbing/server-side-scrubbing/)**
+ — note the default rules only catch known-sensitive patterns (passwords, tokens, card numbers), so
+ for arbitrary field values you'll need to add explicit scrubbing rules.
+
+## Prerequisites
+
+### MongoDB
+
+The sample expects a MongoDB server at `mongodb://localhost:27017`. The easiest way to get one
+running is with Docker:
+
+```shell
+docker run --rm -d -p 27017:27017 --name sentry-mongo mongo:8
+```
+
+To check that it's up:
+
+```shell
+docker ps --filter name=sentry-mongo
+```
+
+And to stop it again once you're done (the `--rm` flag means the container is removed
+automatically when stopped):
+
+```shell
+docker stop sentry-mongo
+```
+
+If your MongoDB server is running somewhere else, set the `MONGODB_URI` environment variable to
+the appropriate connection string before running the sample.
+
+### Sentry DSN
+
+As with the other samples in this repository, you need a Sentry DSN. Either set the `SENTRY_DSN`
+environment variable, or replace the placeholder in [samples/SamplesShared.cs](../SamplesShared.cs).
+See https://docs.sentry.io/product/sentry-basics/dsn-explainer/ for details.
+
+## Running the sample
+
+```shell
+dotnet run --project samples/Sentry.Samples.OpenTelemetry.MongoDB
+```
+
+The sample performs a few operations against a `sentry_mongo_sample` database (insert, find,
+update, count and drop) inside a single transaction. In Sentry, look for the `Fruit Salad`
+transaction in **Performance** — each MongoDB command shows up as a child span, with the query
+text attached as `db.query.text`.
diff --git a/samples/Sentry.Samples.OpenTelemetry.MongoDB/RedactSensitiveMongoData.cs b/samples/Sentry.Samples.OpenTelemetry.MongoDB/RedactSensitiveMongoData.cs
new file mode 100644
index 0000000000..01a944366d
--- /dev/null
+++ b/samples/Sentry.Samples.OpenTelemetry.MongoDB/RedactSensitiveMongoData.cs
@@ -0,0 +1,38 @@
+using System.Diagnostics;
+using System.Text.RegularExpressions;
+using OpenTelemetry;
+
+namespace Sentry.Samples.OpenTelemetry.MongoDB;
+
+///
+/// An example OpenTelemetry span processor that redacts sensitive values from the MongoDB
+/// db.query.text attribute before spans are exported to Sentry.
+///
+///
+/// When query-text capture is enabled (see TracingOptions.QueryTextMaxLength above), the
+/// MongoDB driver records the full command - including field values - on the span. If those values
+/// can contain PII, you should redact them before they leave your process.
+///
+/// A span processor is the right place to do this: the Sentry OTLP exporter sends spans straight to
+/// Sentry and does NOT run them through the SDK's BeforeSend/BeforeSendTransaction
+/// hooks, so the processor has to be registered before AddSentryOtlpExporter in the pipeline.
+///
+///
+/// This example redacts the "contributor" field (a person's name). Adapt the field names and logic
+/// to match the sensitive data in your own queries.
+///
+///
+internal sealed partial class RedactSensitiveMongoData : BaseProcessor
+{
+ // Matches "contributor": "" in MongoDB's extended JSON (spacing may vary).
+ [GeneratedRegex("(\"contributor\"\\s*:\\s*)\"[^\"]*\"")]
+ private static partial Regex SensitiveField();
+
+ public override void OnEnd(Activity activity)
+ {
+ if (activity.GetTagItem("db.query.text") is string queryText)
+ {
+ activity.SetTag("db.query.text", SensitiveField().Replace(queryText, "$1\"[Filtered]\""));
+ }
+ }
+}
diff --git a/samples/Sentry.Samples.OpenTelemetry.MongoDB/Sentry.Samples.OpenTelemetry.MongoDB.csproj b/samples/Sentry.Samples.OpenTelemetry.MongoDB/Sentry.Samples.OpenTelemetry.MongoDB.csproj
new file mode 100644
index 0000000000..dfa5adedcd
--- /dev/null
+++ b/samples/Sentry.Samples.OpenTelemetry.MongoDB/Sentry.Samples.OpenTelemetry.MongoDB.csproj
@@ -0,0 +1,29 @@
+
+
+
+ Exe
+ net10.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+ sentry-sdks
+ sentry-dotnet
+ true
+ true
+
+
+
+
+
+
+
+
+