VanDerHeijden.Logging.MongoDb
10.1.1
dotnet add package VanDerHeijden.Logging.MongoDb --version 10.1.1
NuGet\Install-Package VanDerHeijden.Logging.MongoDb -Version 10.1.1
<PackageReference Include="VanDerHeijden.Logging.MongoDb" Version="10.1.1" />
<PackageVersion Include="VanDerHeijden.Logging.MongoDb" Version="10.1.1" />
<PackageReference Include="VanDerHeijden.Logging.MongoDb" />
paket add VanDerHeijden.Logging.MongoDb --version 10.1.1
#r "nuget: VanDerHeijden.Logging.MongoDb, 10.1.1"
#:package VanDerHeijden.Logging.MongoDb@10.1.1
#addin nuget:?package=VanDerHeijden.Logging.MongoDb&version=10.1.1
#tool nuget:?package=VanDerHeijden.Logging.MongoDb&version=10.1.1
VanDerHeijden.Logging.MongoDb
MongoDB log writer for VanDerHeijden.Logging.
Writes batched log entries to a MongoDB collection using an unordered InsertManyAsync, with structured
properties stored as a queryable BSON subdocument.
Installation
dotnet add package VanDerHeijden.Logging.MongoDb
Usage
var mongoClient = new MongoClient("mongodb://localhost:27017");
var collection = mongoClient
.GetDatabase("myapp")
.GetCollection<BsonDocument>("logs");
builder.Logging.AddMongoDbLogger(collection, options => options.RetentionDays = 30);
Or from configuration, when an IMongoDatabase is registered in the DI container:
builder.Logging.AddMongoDbLogger();
builder.Services.AddMongoDbLogging(builder.Configuration);
{
"MongoDb": {
"Collections": { "Logs": "Logs" },
"RetentionDays": 30
}
}
Options
| Option | Default | Description |
|---|---|---|
CreateIndexes |
true |
Create the indexes below when the logger starts |
RetentionDays |
null |
When set, entries are removed after this many days (TTL index on Timestamp) |
StoreMessage |
true |
Store the formatted text as Message (User 42 logged in) |
StoreMessageTemplate |
true |
Store the template as MessageTemplate (User {UserId} logged in) |
BatchSize |
100 | Maximum documents per insert |
MaxIdleMs |
3000 | How long a non-full batch waits before it is written |
QueueCapacity |
10 000 | Entries the in-memory queue holds while the writer is busy |
FullMode |
DropOldest |
What happens when the queue is full |
With both enabled a document holds the text twice: once formatted, and once as template plus Properties.
Choose what you need:
StoreMessage |
StoreMessageTemplate |
Stored | Use when |
|---|---|---|---|
true |
true |
Message, MessageTemplate, Properties |
Default: readable text and grouping by template |
true |
false |
Message, Properties |
You read and search the text and do not group by template |
false |
true |
MessageTemplate, Properties |
Smallest documents; the viewer rebuilds the text from template and properties |
false |
false |
Message, Properties |
Same as true / false: the text is never dropped entirely |
Properties is always stored, and at least one of Message and MessageTemplate is always stored: an entry
whose template is not stored (switched off, or the log call had none) keeps its Message even when
StoreMessage is false.
With StoreMessage = false and the template stored, the text is not formatted at all: the logging call skips the
ToString() of every argument, which makes it several times cheaper for calls that carry objects.
In configuration the keys are MongoDb:StoreMessage, MongoDb:StoreMessageTemplate, MongoDb:BatchSize,
MongoDb:MaxIdleMs and MongoDb:QueueCapacity.
The insert rate of the server, not the batch size, limits throughput: on the test machine about 4 000 documents per
second with the four indexes, for batch sizes from 100 to 2 000. If the application logs faster than that in bursts,
raise QueueCapacity so the burst is buffered instead of dropped: a burst of 100 000 entries with
QueueCapacity = 200 000 was stored completely, about 30 seconds later.
Document
logger.LogInformation("Order {OrderId} count {Count} amount {Amount}", orderId, 42, 12.34m) is stored as:
{
"_id": ObjectId("..."),
"Timestamp": ISODate("2026-02-22T14:03:12.456Z"),
"Level": "Information",
"EventId": 0,
"Category": "MyApp.Service",
"Message": "Order 3fa85f64-... count 42 amount 12.34",
"MessageTemplate": "Order {OrderId} count {Count} amount {Amount}",
"Properties": { "OrderId": UUID("3fa85f64-..."), "Count": 42, "Amount": NumberDecimal("12.34") },
"Path": "/api/orders",
"Method": "POST",
"ClientIp": "203.0.113.42"
}
EventName, Exception, Referer, UserAgent, SessionId and SessionGuid are added when set; fields that are
null are not stored. Timestamp is UTC.
Properties
Property values are captured in ILogger.Log; the BSON document is built afterwards by the background writer, so the
logging call stays cheap. The driver never receives a Dictionary<string, object?>, so an unusual argument type cannot
make an insert fail. Objects are captured as a shallow clone: top-level members are frozen at log time, nested objects
and collections are shared with the original until the batch is written.
| .NET value | BSON |
|---|---|
string, bool, int, long, double |
native |
DateTime, DateTimeOffset |
Date (UTC) |
decimal |
Decimal128 |
Guid |
Binary subtype 4 (GuidRepresentation.Standard) |
| enum | String (name) |
null |
Null |
smaller integers, float |
Int64 / Double |
| object, record | Subdocument (nested values mapped as JSON: string, Int32/Int64, Double, Boolean, Null) |
| collection | Array |
| not serializable, or over 32 KB of JSON | String (ToString()) |
Keys are sanitized: every . and a leading $ become _ ({user.name} is stored as user_name).
Query properties directly, e.g. { "Properties.Count": { $gt: 10 } } or { "Properties.Customer.Name": "Alice" }.
Inside an object the JSON types apply: a nested decimal becomes a Double and a nested Guid or DateTime a string.
Pass such a value as its own template argument when you need the native BSON type. Nested keys are sanitized too.
Indexes
Created in the background at startup (a failure never blocks or breaks the application):
| Index | Purpose |
|---|---|
Timestamp descending |
Newest-first listing; carries the TTL when RetentionDays is set |
Level + Timestamp descending |
Filter by level |
Category + Timestamp descending |
Filter by category |
Properties.$** (wildcard) |
Filter on any structured property |
Changing RetentionDays replaces the Timestamp index with the new TTL.
Reliability
- Inserts are unordered (
IsOrdered = false): one rejected document does not stop the rest of the batch. - A retried batch reuses the documents it already built, so every entry keeps its
_idand cannot be stored twice. - The queue uses
DropOldestby default: if MongoDB is unreachable or slower than the application logs, logging never blocks and the oldest buffered entries are discarded.
HTTP fields
The HTTP fields are populated automatically when IHttpContextAccessor is registered:
builder.Services.AddHttpContextAccessor();
Outside an HTTP context they are not stored in the document.
Upgrading
The collection type changed from IMongoCollection<LogEntry> to IMongoCollection<BsonDocument>, and the
MongoDB-specific LogEntry class was removed in favour of the shared VanDerHeijden.Logging.LogEntry.
Repository
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | 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. |
-
net10.0
- MongoDB.Driver (>= 3.12.0)
- VanDerHeijden.Logging (>= 10.1.0)
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 |
|---|---|---|
| 10.1.1 | 85 | 10/5/2026 |
| 10.1.0 | 96 | 9/30/2026 |
| 10.0.11 | 112 | 9/15/2026 |
| 10.0.10 | 103 | 9/15/2026 |
| 10.0.9 | 133 | 5/13/2026 |
| 10.0.8 | 136 | 4/9/2026 |
| 10.0.7 | 142 | 3/11/2026 |
| 10.0.6 | 127 | 3/11/2026 |
| 10.0.4 | 132 | 2/23/2026 |
| 10.0.3 | 129 | 2/23/2026 |
| 10.0.2 | 128 | 2/23/2026 |
| 10.0.1 | 134 | 2/23/2026 |
| 10.0.0 | 132 | 2/22/2026 |