Skip to content

A high-performance Serilog sink that writes log events to MongoDB

License

Notifications You must be signed in to change notification settings

loresoft/serilog-sinks-mongo

Repository files navigation

Serilog.Sinks.Mongo

NuGet License

A high-performance Serilog sink that writes log events to MongoDB. This sink provides efficient batching, flexible configuration, and support for MongoDB-specific features like TTL indexes and capped collections.

Features

  • Batched Writes - Efficient batch processing of log events
  • Automatic Expiration - TTL index support for automatic log rotation
  • Capped Collections - Size and document count limited collections
  • Flexible Configuration - Code-based and configuration file support
  • Customizable Document Format - Extensible document factory pattern
  • High Performance - Asynchronous writes with configurable buffering

Installation

Install via NuGet:

dotnet add package Serilog.Sinks.Mongo

Or using Package Manager Console:

Install-Package Serilog.Sinks.Mongo

Quick Start

Basic Usage

using Serilog;

Log.Logger = new LoggerConfiguration()
    .WriteTo.MongoDB(
        connectionString: "mongodb://localhost:27017",
        databaseName: "serilog",
        collectionName: "logs"
    )
    .CreateLogger();

Log.Information("Hello, MongoDB!");
Log.CloseAndFlush();

With TTL Index (Automatic Expiration)

Log.Logger = new LoggerConfiguration()
    .WriteTo.MongoDB(
        connectionString: "mongodb://localhost:27017",
        databaseName: "serilog",
        collectionName: "logs",
        expireAfter: TimeSpan.FromDays(30) // Logs expire after 30 days
    )
    .CreateLogger();

With Capped Collection

Log.Logger = new LoggerConfiguration()
    .WriteTo.MongoDB(
        connectionString: "mongodb://localhost:27017",
        databaseName: "serilog",
        collectionName: "logs",
        maxDocuments: 10000, // Maximum 10,000 documents
        maxSize: 10485760    // Maximum 10 MB
    )
    .CreateLogger();

Configuration

Code-Based Configuration

Using Connection String

Log.Logger = new LoggerConfiguration()
    .WriteTo.MongoDB(
        connectionString: "mongodb://localhost:27017",
        databaseName: "serilog",
        collectionName: "logs",
        minimumLevel: LogEventLevel.Information,
        expireAfter: TimeSpan.FromDays(7),
        batchSizeLimit: 100,
        bufferingTimeLimit: TimeSpan.FromSeconds(2)
    )
    .CreateLogger();

Using MongoUrl

var mongoUrl = new MongoUrl("mongodb://localhost:27017");

Log.Logger = new LoggerConfiguration()
    .WriteTo.MongoDB(
        mongoUrl: mongoUrl,
        databaseName: "serilog",
        collectionName: "logs"
    )
    .CreateLogger();

Using Options Configuration

Log.Logger = new LoggerConfiguration()
    .WriteTo.MongoDB(options =>
    {
        options.ConnectionString = "mongodb://localhost:27017";
        options.DatabaseName = "serilog";
        options.CollectionName = "logs";
        options.MinimumLevel = LogEventLevel.Debug;
        options.ExpireAfter = TimeSpan.FromDays(30);
        options.BatchSizeLimit = 100;
        options.BufferingTimeLimit = TimeSpan.FromSeconds(5);
        
        // Capped collection options
        options.CollectionOptions = new CreateCollectionOptions
        {
            Capped = true,
            MaxSize = 5242880,    // 5 MB
            MaxDocuments = 1000
        };
        
        // Custom properties to promote to top-level
        options.Properties = new HashSet<string> 
        { 
            "SourceContext", 
            "RequestId",
            "UserId"
        };
        
        // Remove promoted properties from nested Properties object
        options.OptimizeProperties = true;
    })
    .CreateLogger();

JSON Configuration (appsettings.json)

{
  "Serilog": {
    "Using": ["Serilog.Sinks.Mongo", "Serilog.Sinks.Console"],
    "MinimumLevel": {
      "Default": "Information",
      "Override": {
        "Microsoft": "Warning",
        "System": "Warning"
      }
    },
    "WriteTo": [
      {
        "Name": "MongoDB",
        "Args": {
          "connectionString": "mongodb://localhost:27017",
          "databaseName": "serilog",
          "collectionName": "logs",
          "expireAfter": "30.00:00:00"
        }
      }
    ],
    "Enrich": ["FromLogContext"]
  }
}

Then in your code:

using Serilog;
using Microsoft.Extensions.Configuration;

var configuration = new ConfigurationBuilder()
    .AddJsonFile("appsettings.json")
    .Build();

Log.Logger = new LoggerConfiguration()
    .ReadFrom.Configuration(configuration)
    .CreateLogger();

Configuration Options

MongoSinkOptions

Property Default Description
ConnectionString null MongoDB connection string
MongoUrl null Alternative to ConnectionString (takes precedence if both are set)
DatabaseName "serilog" Database name
CollectionName "logs" Collection name
MinimumLevel Verbose Minimum log event level to write
LevelSwitch null Dynamically controls the minimum log level at runtime
ExpireAfter null Time-to-live for automatic document expiration (creates TTL index on Timestamp)
BatchSizeLimit 100 Maximum number of events to include in a single batch
BufferingTimeLimit 00:00:02 Maximum time to wait before writing a batch
CollectionOptions null MongoDB collection creation options (capped collections, time-series, etc.)
Properties {"SourceContext"} Property names to promote from Properties object to document top-level
OptimizeProperties false Remove promoted properties from nested Properties object to avoid duplication
DocumentFactory null Custom document factory for converting log events to BSON
MongoFactory null Custom MongoDB factory for client/database/collection management

Document Structure

By default, log events are stored with the following structure:

{
  "_id": ObjectId("..."),
  "Timestamp": ISODate("2025-11-27T10:30:00.000Z"),
  "Level": "Information",
  "Message": "User logged in successfully",
  "TraceId": "00-abc123...",
  "SpanId": "def456...",
  "SourceContext": "MyApp.Controllers.AuthController",
  "Properties": {
    "UserId": "12345",
    "Username": "john.doe",
    "IPAddress": "192.168.1.1"
  },
  "Exception": {
    "Message": "...",
    "Type": "System.Exception",
    "Text": "...",
    "HResult": -2146233088
  }
}

Property Promotion

Properties can be promoted from the Properties object to the top level of the document:

options.Properties = new HashSet<string> 
{ 
    "SourceContext",
    "RequestId",
    "UserId",
    "MachineName"
};

This results in:

{
  "_id": ObjectId("..."),
  "Timestamp": ISODate("2025-11-27T10:30:00.000Z"),
  "Level": "Information",
  "Message": "Processing request",
  "SourceContext": "MyApp.Services.ProcessingService",
  "RequestId": "req-789",
  "UserId": "12345",
  "MachineName": "WEB-SERVER-01",
  "Properties": {
    // Other properties...
  }
}

Optimizing Property Storage

By default, promoted properties appear both at the top level and within the nested Properties object. To reduce document size and avoid duplication, enable OptimizeProperties:

options.Properties = new HashSet<string> { "SourceContext", "RequestId", "UserId" };
options.OptimizeProperties = true; // Removes promoted properties from nested Properties

With OptimizeProperties enabled, promoted properties are removed from the Properties object after being promoted to the top level.

Custom Document Factory

Implement IDocumentFactory to customize the document structure:

public class CustomDocumentFactory : DocumentFactory
{
    public override BsonDocument? CreateDocument(LogEvent logEvent, MongoSinkOptions options)
    {
        var document = base.CreateDocument(logEvent, options);
        
        if (document != null)
        {
            // Add custom fields
            document["Application"] = "MyApp";
            document["Environment"] = "Production";
            
            // Custom transformations
            if (logEvent.Properties.TryGetValue("RequestPath", out var path))
            {
                document["Path"] = path.ToString();
            }
        }
        
        return document;
    }
}

// Use the custom factory
Log.Logger = new LoggerConfiguration()
    .WriteTo.MongoDB(options =>
    {
        options.ConnectionString = "mongodb://localhost:27017";
        options.DatabaseName = "serilog";
        options.CollectionName = "logs";
        options.DocumentFactory = new CustomDocumentFactory();
    })
    .CreateLogger();

Advanced Scenarios

Multiple Sinks with Different Configurations

Log.Logger = new LoggerConfiguration()
    .WriteTo.MongoDB(
        connectionString: "mongodb://localhost:27017",
        databaseName: "serilog",
        collectionName: "errors",
        minimumLevel: LogEventLevel.Error
    )
    .WriteTo.MongoDB(
        connectionString: "mongodb://localhost:27017",
        databaseName: "serilog",
        collectionName: "all-logs",
        minimumLevel: LogEventLevel.Information,
        expireAfter: TimeSpan.FromDays(7)
    )
    .CreateLogger();

With Microsoft.Extensions.Hosting

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

var builder = Host.CreateApplicationBuilder(args);

builder.Services.AddSerilog(loggerConfiguration =>
{
    loggerConfiguration
        .MinimumLevel.Information()
        .Enrich.FromLogContext()
        .WriteTo.Console()
        .WriteTo.MongoDB(
            connectionString: "mongodb://localhost:27017",
            databaseName: "serilog",
            collectionName: "logs",
            expireAfter: TimeSpan.FromDays(30)
        );
});

var host = builder.Build();
await host.RunAsync();

Performance Tuning

Batch Configuration

Adjust batching settings based on your throughput requirements:

options.BatchSizeLimit = 500;           // Larger batches for high throughput
options.BufferingTimeLimit = TimeSpan.FromSeconds(10); // Longer wait for batch fill

MongoDB Collection Strategies

TTL Index (Time-Based Expiration)

Best for applications that need automatic log cleanup:

.WriteTo.MongoDB(
    connectionString: "mongodb://localhost:27017",
    databaseName: "serilog",
    collectionName: "logs",
    expireAfter: TimeSpan.FromDays(30)
)

A TTL index is automatically created on the Timestamp field to removes expired documents.

Capped Collection (Size/Count Limited)

Best for fixed-size log storage:

.WriteTo.MongoDB(
    connectionString: "mongodb://localhost:27017",
    databaseName: "serilog",
    collectionName: "logs",
    maxDocuments: 100000,  // Keep latest 100k documents
    maxSize: 104857600     // Or 100 MB, whichever is hit first
)

Oldest documents are automatically removed when limits are reached.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

This project is licensed under the MIT License - see the LICENSE file for details.

About

A high-performance Serilog sink that writes log events to MongoDB

Topics

Resources

License

Stars

Watchers

Forks

Sponsor this project

 

Packages

 
 
 

Contributors 3

  •  
  •  
  •  

Languages